Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public void accept(ErrorResponse error, String requestId) {
}
switch (code) {
case 400:
throw new BadRequestException(String.format("%s", message));
throw new BadRequestException("%s", message);
case 401:
throw new NotAuthorizedException("Not authorized: %s", message);
case 403:
Expand All @@ -69,7 +69,7 @@ public void accept(ErrorResponse error, String requestId) {
case 500:
throw new ServiceFailureException("Server error: %s", message);
case 501:
throw new NotImplementedException(message);
throw new NotImplementedException("%s", message);
case 503:
throw new ServiceUnavailableException("Service unavailable: %s", message);
default:
Expand Down
3 changes: 2 additions & 1 deletion paimon-api/src/main/java/org/apache/paimon/rest/RESTApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -1487,7 +1487,8 @@ public GetFunctionResponse getFunction(Identifier identifier) {
throw new NoSuchResourceException(
ErrorResponse.RESOURCE_TYPE_FUNCTION,
identifier.getObjectName(),
"Invalid function name: " + identifier.getObjectName());
"Invalid function name: %s",
identifier.getObjectName());
}
return client.get(
resourcePaths.function(identifier.getDatabaseName(), identifier.getObjectName()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ public class BadRequestException extends RESTException {
public BadRequestException(String message, Object... args) {
super(message, args);
}

public BadRequestException(Throwable cause, String message, Object... args) {
super(cause, message, args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@
public class NotAuthorizedException extends RESTException {

public NotAuthorizedException(String message, Object... args) {
super(String.format(message, args));
super(message, args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@
public class NotImplementedException extends RESTException {

public NotImplementedException(String message, Object... args) {
super(String.format(message, args));
super(message, args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@
public class ServiceFailureException extends RESTException {

public ServiceFailureException(String message, Object... args) {
super(String.format(message, args));
super(message, args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@
public class ServiceUnavailableException extends RESTException {

public ServiceUnavailableException(String message, Object... args) {
super(String.format(message, args));
super(message, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.paimon.rest;

import org.apache.paimon.catalog.Identifier;
import org.apache.paimon.options.Options;
import org.apache.paimon.rest.exceptions.NoSuchResourceException;

import org.junit.jupiter.api.Test;

import static org.apache.paimon.rest.RESTCatalogInternalOptions.PREFIX;
import static org.apache.paimon.rest.RESTCatalogOptions.TOKEN;
import static org.apache.paimon.rest.RESTCatalogOptions.TOKEN_PROVIDER;
import static org.apache.paimon.rest.RESTCatalogOptions.URI;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

/** Tests for the name check {@link RESTApi} makes before it sends anything. */
public class RESTApiFunctionNameTest {

@Test
public void testARejectedFunctionNameIsReportedAndNotFormatted() {
Options options = new Options();
options.set(URI, "http://127.0.0.1:1");
options.set(TOKEN_PROVIDER, "bear");
options.set(TOKEN, "secret");
options.set(PREFIX, "catalog");
RESTApi api = new RESTApi(options, false);

// the name is why the request is refused, so it belongs in the message as data -- the
// validator rejects '%', which is also what java.util.Formatter reads as a conversion
assertThatThrownBy(() -> api.getFunction(Identifier.create("db", "function%")))
.isInstanceOf(NoSuchResourceException.class)
.hasMessageContaining("function%");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ private PagedList<String> listSystemTablesPaged(
return SystemTableLoader.loadGlobalTableNamesPaged(
context.options(), maxResults, pageToken, tableNamePattern, tableType);
} catch (IllegalArgumentException e) {
throw new BadRequestException(e.getMessage());
throw new BadRequestException(e, "%s", e.getMessage());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

import static org.apache.paimon.rest.interceptor.LoggingInterceptor.DEFAULT_REQUEST_ID;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

/** Test for {@link DefaultErrorHandler}. */
public class DefaultErrorHandlerTest {
Expand Down Expand Up @@ -83,6 +84,25 @@ public void testHandleErrorResponse() {
() -> defaultErrorHandler.accept(generateErrorResponse(503), DEFAULT_REQUEST_ID));
}

@Test
public void testErrorMessageIsNotReadAsAFormatString() {
// a server message is data, and this one holds what java.util.Formatter reads as syntax
String message = "quota 80% exceeded";
for (int code : new int[] {400, 401, 403, 404, 405, 406, 409, 500, 501, 502, 503}) {
RESTException exception =
assertThrows(
RESTException.class,
() ->
defaultErrorHandler.accept(
new ErrorResponse("table", "t", message, code),
DEFAULT_REQUEST_ID),
"status " + code);
assertTrue(
exception.getMessage().contains(message),
"status " + code + " lost the message: " + exception.getMessage());
}
}

private ErrorResponse generateErrorResponse(int code) {
return new ErrorResponse(null, null, "message", code);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
import java.util.Map;
import java.util.UUID;

import static org.apache.paimon.catalog.Catalog.SYSTEM_DATABASE_NAME;
import static org.apache.paimon.catalog.Catalog.TABLE_DEFAULT_OPTION_PREFIX;
import static org.apache.paimon.rest.RESTApi.HEADER_PREFIX;
import static org.apache.paimon.rest.RESTApi.READ_VIA_HEADER;
Expand Down Expand Up @@ -142,6 +143,19 @@ void testAuthFail() {
.isInstanceOf(NotAuthorizedException.class);
}

@Test
void testRejectedSystemTablePatternKeepsWhatRaisedIt() {
// the message is the argument, never the format, and the cause is what says where it
// came from -- the caller sees only what this exception carries
assertThatThrownBy(
() ->
catalog.listTablesPaged(
SYSTEM_DATABASE_NAME, null, null, "a%b", null))
.isInstanceOf(BadRequestException.class)
.hasMessageContaining("prefix sql like pattern")
.hasCauseInstanceOf(IllegalArgumentException.class);
}

@Test
void testInvalidManagementDtoReturnsBadRequest() throws Exception {
String database = "invalid_management_dto";
Expand Down
Loading