This is an automated email from the ASF dual-hosted git repository.
jerryshao pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/branch-1.3 by this push:
new 7f64eac08d [Cherry-pick to branch-1.3] [#12834] fix(server): reject
null request bodies in remaining REST operations (#12866) (#12881)
7f64eac08d is described below
commit 7f64eac08dbd5aa9e18250dd9ba78c766b85aed4
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Sep 3 21:13:36 2026 +0800
[Cherry-pick to branch-1.3] [#12834] fix(server): reject null request
bodies in remaining REST operations (#12866) (#12881)
**Cherry-pick Information:**
- Original commit: 364e145c804f6c753ed3b5ad0bd8478e170d99b3
- Target branch: `branch-1.3`
- Status: ✅ Conflicts resolved manually
**Resolution:**
- Kept the null-body guards and regression tests for the 17 affected
operations available on `branch-1.3`, plus the deprecated tag route.
- Removed `BulkOperations.java` and `TestBulkOperations.java` because
the Bulk user/group REST feature is not present on `branch-1.3`.
- Kept the existing v1 tag-association path and omitted the main-only v2
tag-value route and tests.
- Resolved the Catalog, Permission, and Statistic conflicts against the
`branch-1.3` control flow without bringing in unrelated main-only
changes.
**Validation:**
- `./gradlew :server:spotlessApply --no-daemon`
- `./gradlew :server:compileJava :server:compileTestJava -PskipITs
--no-daemon`
- Targeted tests for the 9 affected REST test classes: 96 tests, 0
failures, 0 errors
- Conflict-marker scan and `git diff --check`
---------
Co-authored-by: Shuai Liu <[email protected]>
Co-authored-by: mchades <[email protected]>
---
.../server/web/rest/CatalogOperations.java | 16 ++++-
.../web/rest/MetadataObjectPolicyOperations.java | 9 ++-
.../web/rest/MetadataObjectTagOperations.java | 8 +++
.../server/web/rest/MetalakeOperations.java | 7 ++
.../gravitino/server/web/rest/OwnerOperations.java | 8 +++
.../server/web/rest/PermissionOperations.java | 56 +++++++++++++++
.../server/web/rest/PolicyOperations.java | 9 ++-
.../server/web/rest/StatisticOperations.java | 37 ++++++++--
.../server/web/rest/TestCatalogOperations.java | 28 ++++++++
.../rest/TestMetadataObjectPolicyOperations.java | 19 +++++-
.../web/rest/TestMetadataObjectTagOperations.java | 16 +++++
.../server/web/rest/TestMetalakeOperations.java | 63 +++++++++++++++++
.../server/web/rest/TestOwnerOperations.java | 11 +++
.../server/web/rest/TestPermissionOperations.java | 77 +++++++++++++++++++++
.../server/web/rest/TestPolicyOperations.java | 13 ++++
.../server/web/rest/TestStatisticOperations.java | 79 ++++++++++++++++++++--
.../server/web/rest/TestTagOperations.java | 16 +++++
17 files changed, 455 insertions(+), 17 deletions(-)
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/CatalogOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/CatalogOperations.java
index 8def70d574..b7818a1a5a 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/CatalogOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/CatalogOperations.java
@@ -188,6 +188,13 @@ public class CatalogOperations {
@PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
CatalogCreateRequest request) {
+ if (request == null) {
+ // Unlike a failed connection test, which
handleTestConnectionException() reports inside an
+ // HTTP 200 response by design, a missing request body never reaches the
connection test, so
+ // it is rejected with a regular HTTP 400.
+ return Utils.illegalArguments("Request body cannot be null");
+ }
+
LOG.info("Received test connection request for catalog: {}.{}", metalake,
request.getName());
try {
return Utils.doAs(
@@ -227,8 +234,15 @@ public class CatalogOperations {
@PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG)
String catalogName,
CatalogSetRequest request) {
- LOG.info("Received set request for catalog: {}.{}", metalake, catalogName);
+ if (request == null) {
+ return ExceptionHandlers.handleCatalogException(
+ OperationType.SET,
+ catalogName,
+ metalake,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+ LOG.info("Received set request for catalog: {}.{}", metalake, catalogName);
OperationType op = request.isInUse() ? OperationType.ENABLE :
OperationType.DISABLE;
try {
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/MetadataObjectPolicyOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/MetadataObjectPolicyOperations.java
index 78a03570fb..19ea45ab96 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/MetadataObjectPolicyOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/MetadataObjectPolicyOperations.java
@@ -251,12 +251,19 @@ public class MetadataObjectPolicyOperations {
@PathParam("fullName") @AuthorizationFullName String fullName,
@AuthorizationRequest(type =
AuthorizationRequest.RequestType.ASSOCIATE_POLICY)
PoliciesAssociateRequest request) {
+ if (request == null) {
+ return ExceptionHandlers.handlePolicyException(
+ OperationType.ASSOCIATE,
+ "",
+ fullName,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+
LOG.info(
"Received associate policies request for object type: {}, full name:
{} under metalake: {}",
type,
fullName,
metalake);
-
try {
return Utils.doAs(
httpRequest,
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/MetadataObjectTagOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/MetadataObjectTagOperations.java
index d96e655d30..525fe9943e 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/MetadataObjectTagOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/MetadataObjectTagOperations.java
@@ -260,6 +260,14 @@ public class MetadataObjectTagOperations {
@PathParam("fullName") @AuthorizationFullName String fullName,
@AuthorizationRequest(type =
AuthorizationRequest.RequestType.ASSOCIATE_TAG)
TagsAssociateRequest request) {
+ if (request == null) {
+ return ExceptionHandlers.handleTagException(
+ OperationType.ASSOCIATE,
+ "",
+ fullName,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+
LOG.info(
"Received associate tags request for object type: {}, full name: {}
under metalake: {}",
type,
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/MetalakeOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/MetalakeOperations.java
index 7cce216e78..b8596792a1 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/MetalakeOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/MetalakeOperations.java
@@ -181,6 +181,13 @@ public class MetalakeOperations {
@PathParam("name") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalakeName,
MetalakeSetRequest request) {
+ if (request == null) {
+ return ExceptionHandlers.handleMetalakeException(
+ OperationType.SET,
+ metalakeName,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+
LOG.info("Received set request for metalake: {}", metalakeName);
try {
return Utils.doAs(
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/OwnerOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/OwnerOperations.java
index dbe91956b0..962dfde6a2 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/OwnerOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/OwnerOperations.java
@@ -119,6 +119,14 @@ public class OwnerOperations {
@PathParam("metadataObjectType") String metadataObjectType,
@PathParam("fullName") String fullName,
OwnerSetRequest request) {
+ if (request == null) {
+ return ExceptionHandlers.handleOwnerException(
+ OperationType.SET,
+ String.format("metadata object %s", fullName),
+ metalake,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+
try {
MetadataObject object =
MetadataObjects.parse(
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/PermissionOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/PermissionOperations.java
index 6ea1ea3390..ae74667c7c 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/PermissionOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/PermissionOperations.java
@@ -88,6 +88,14 @@ public class PermissionOperations {
String metalake,
@PathParam("user") String user,
RoleGrantRequest request) {
+ if (request == null) {
+ return ExceptionHandlers.handleUserPermissionOperationException(
+ OperationType.GRANT,
+ "",
+ user,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+
try {
return Utils.doAs(
httpRequest,
@@ -117,6 +125,14 @@ public class PermissionOperations {
String metalake,
@PathParam("group") String group,
RoleGrantRequest request) {
+ if (request == null) {
+ return ExceptionHandlers.handleGroupPermissionOperationException(
+ OperationType.GRANT,
+ "",
+ group,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+
try {
return Utils.doAs(
httpRequest,
@@ -146,6 +162,14 @@ public class PermissionOperations {
String metalake,
@PathParam("user") String user,
RoleRevokeRequest request) {
+ if (request == null) {
+ return ExceptionHandlers.handleUserPermissionOperationException(
+ OperationType.REVOKE,
+ "",
+ user,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+
try {
return Utils.doAs(
httpRequest,
@@ -175,6 +199,14 @@ public class PermissionOperations {
String metalake,
@PathParam("group") String group,
RoleRevokeRequest request) {
+ if (request == null) {
+ return ExceptionHandlers.handleGroupPermissionOperationException(
+ OperationType.REVOKE,
+ "",
+ group,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+
try {
return Utils.doAs(
httpRequest,
@@ -208,6 +240,14 @@ public class PermissionOperations {
@PathParam("type") String type,
@PathParam("fullName") String fullName,
PrivilegeGrantRequest privilegeGrantRequest) {
+ if (privilegeGrantRequest == null) {
+ return ExceptionHandlers.handleRolePermissionOperationException(
+ OperationType.GRANT,
+ fullName,
+ role,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+
try {
MetadataObject object =
MetadataObjects.parse(
@@ -256,6 +296,14 @@ public class PermissionOperations {
@PathParam("type") String type,
@PathParam("fullName") String fullName,
PrivilegeRevokeRequest privilegeRevokeRequest) {
+ if (privilegeRevokeRequest == null) {
+ return ExceptionHandlers.handleRolePermissionOperationException(
+ OperationType.REVOKE,
+ fullName,
+ role,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+
try {
MetadataObject object =
MetadataObjects.parse(
@@ -300,6 +348,14 @@ public class PermissionOperations {
String metalake,
@PathParam("role") String role,
PrivilegeOverrideRequest request) {
+ if (request == null) {
+ return ExceptionHandlers.handleRolePermissionOperationException(
+ OperationType.UPDATE,
+ role,
+ metalake,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+
try {
return Utils.doAs(
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/PolicyOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/PolicyOperations.java
index ec336b74ae..1816356524 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/PolicyOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/PolicyOperations.java
@@ -253,8 +253,15 @@ public class PolicyOperations {
String metalake,
@PathParam("policy") @AuthorizationMetadata(type =
Entity.EntityType.POLICY) String name,
PolicySetRequest request) {
- LOG.info("Received set policy request for policy: {} under metalake: {}",
name, metalake);
+ if (request == null) {
+ return ExceptionHandlers.handlePolicyException(
+ OperationType.SET,
+ name,
+ metalake,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+ LOG.info("Received set policy request for policy: {} under metalake: {}",
name, metalake);
OperationType op = request.isEnable() ? OperationType.ENABLE :
OperationType.DISABLE;
try {
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
index 420869c020..e86ce980c8 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
@@ -81,9 +81,6 @@ public class StatisticOperations {
private static final Logger LOG =
LoggerFactory.getLogger(StatisticOperations.class);
- private static final String NULL_STATS_UPDATE_REQUEST_BODY_ERROR =
- "Statistics update request body cannot be null";
-
@Context private HttpServletRequest httpRequest;
private final StatisticDispatcher statisticDispatcher;
@@ -154,6 +151,14 @@ public class StatisticOperations {
@PathParam("type") @AuthorizationObjectType String type,
@PathParam("fullName") @AuthorizationFullName String fullName,
StatisticsUpdateRequest request) {
+ if (request == null) {
+ return ExceptionHandlers.handleStatisticException(
+ OperationType.UPDATE,
+ "",
+ fullName,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+
try {
LOG.info(
"Received update statistics request for object full name: {} type:
{} in the metalake {}",
@@ -163,9 +168,6 @@ public class StatisticOperations {
return Utils.doAs(
httpRequest,
() -> {
- if (request == null) {
- throw new
IllegalArgumentException(NULL_STATS_UPDATE_REQUEST_BODY_ERROR);
- }
request.validate();
MetadataObject object =
MetadataObjects.parse(
@@ -214,6 +216,14 @@ public class StatisticOperations {
@PathParam("type") @AuthorizationObjectType String type,
@PathParam("fullName") @AuthorizationFullName String fullName,
StatisticsDropRequest request) {
+ if (request == null) {
+ return ExceptionHandlers.handleStatisticException(
+ OperationType.DROP,
+ "",
+ fullName,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+
try {
LOG.info(
"Received drop statistics request for object full name: {} type: {}
in the metalake {}",
@@ -353,6 +363,14 @@ public class StatisticOperations {
@PathParam("type") @AuthorizationObjectType String type,
@PathParam("fullName") @AuthorizationFullName String fullName,
PartitionStatisticsUpdateRequest request) {
+ if (request == null) {
+ return ExceptionHandlers.handlePartitionStatsException(
+ OperationType.UPDATE,
+ "",
+ fullName,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
+
LOG.info("Updating partition statistics for table: {} in the metalake {}",
fullName, metalake);
try {
return Utils.doAs(
@@ -427,6 +445,13 @@ public class StatisticOperations {
@PathParam("type") @AuthorizationObjectType String type,
@PathParam("fullName") @AuthorizationFullName String fullName,
PartitionStatisticsDropRequest request) {
+ if (request == null) {
+ return ExceptionHandlers.handlePartitionStatsException(
+ OperationType.DROP,
+ "",
+ fullName,
+ new IllegalArgumentException("Request body cannot be null"));
+ }
try {
return Utils.doAs(
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestCatalogOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestCatalogOperations.java
index dfe09762e6..242bc02149 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestCatalogOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestCatalogOperations.java
@@ -30,6 +30,8 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
@@ -312,6 +314,20 @@ public class TestCatalogOperations extends
BaseOperationsTest {
Assertions.assertEquals(RuntimeException.class.getSimpleName(),
errorResponse2.getType());
}
+ @Test
+ public void testTestConnectionWithNullRequest() {
+ Response resp =
+ target("/metalakes/metalake1/catalogs/testConnection")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .post(Entity.entity("null", MediaType.APPLICATION_JSON_TYPE));
+
+ // A missing request body never reaches the connection test, so it is
reported as a regular
+ // HTTP 400 rather than through the HTTP 200 envelope used for
connection-test failures.
+ assertNullRequestBodyRejected(resp);
+ verify(manager, never()).testConnection(any(), any(), any(), any(), any());
+ }
+
@Test
public void testConnection() {
CatalogCreateRequest req =
@@ -610,6 +626,18 @@ public class TestCatalogOperations extends
BaseOperationsTest {
Assertions.assertEquals(RuntimeException.class.getSimpleName(),
errorResponse1.getType());
}
+ @Test
+ public void testSetCatalogWithNullRequest() {
+ Response resp =
+ target("/metalakes/metalake1/catalogs/catalog1")
+ .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .method("PATCH", Entity.entity("null",
MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(resp);
+ }
+
private static TestCatalog buildCatalog(String metalake, String catalogName)
{
CatalogEntity entity =
CatalogEntity.builder()
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetadataObjectPolicyOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetadataObjectPolicyOperations.java
index e95b06fb88..6ff31e2203 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetadataObjectPolicyOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetadataObjectPolicyOperations.java
@@ -57,12 +57,11 @@ import org.apache.gravitino.policy.PolicyManager;
import org.apache.gravitino.rest.RESTUtils;
import org.glassfish.jersey.internal.inject.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
-import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
-public class TestMetadataObjectPolicyOperations extends JerseyTest {
+public class TestMetadataObjectPolicyOperations extends BaseOperationsTest {
private static class MockServletRequestFactory extends
ServletRequestFactoryBase {
@@ -669,4 +668,20 @@ public class TestMetadataObjectPolicyOperations extends
JerseyTest {
.withAuditInfo(testAuditInfo1)
.build();
}
+
+ @Test
+ public void testAssociatePoliciesForObjectWithNullRequest() {
+ MetadataObject catalog = MetadataObjects.parse("object1",
MetadataObject.Type.CATALOG);
+
+ Response response =
+ target(basePath(metalake))
+ .path(catalog.type().toString())
+ .path(catalog.fullName())
+ .path("policies")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .post(Entity.entity("null", MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(response);
+ }
}
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetadataObjectTagOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetadataObjectTagOperations.java
index e9e7796521..93eb9cf288 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetadataObjectTagOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetadataObjectTagOperations.java
@@ -1036,6 +1036,22 @@ public class TestMetadataObjectTagOperations extends
BaseOperationsTest {
Assertions.assertEquals(RuntimeException.class.getSimpleName(),
errorResponse1.getType());
}
+ @Test
+ public void testAssociateTagsForObjectWithNullRequest() {
+ MetadataObject catalog = MetadataObjects.parse("object1",
MetadataObject.Type.CATALOG);
+
+ Response response =
+ target(basePath(metalake))
+ .path(catalog.type().toString())
+ .path(catalog.fullName())
+ .path("tags")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .post(Entity.entity("null", MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(response);
+ }
+
private String basePath(String metalake) {
return "/metalakes/" + metalake + "/objects";
}
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetalakeOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetalakeOperations.java
index ae7eb39133..a2cbd0b251 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetalakeOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetalakeOperations.java
@@ -51,6 +51,7 @@ import org.apache.gravitino.GravitinoEnv;
import org.apache.gravitino.MetalakeChange;
import org.apache.gravitino.dto.MetalakeDTO;
import org.apache.gravitino.dto.requests.MetalakeCreateRequest;
+import org.apache.gravitino.dto.requests.MetalakeSetRequest;
import org.apache.gravitino.dto.requests.MetalakeUpdateRequest;
import org.apache.gravitino.dto.requests.MetalakeUpdatesRequest;
import org.apache.gravitino.dto.responses.DropResponse;
@@ -70,6 +71,7 @@ import
org.apache.gravitino.server.web.mapper.JsonMappingExceptionMapper;
import org.apache.gravitino.server.web.mapper.JsonParseExceptionMapper;
import org.apache.gravitino.server.web.mapper.JsonProcessingExceptionMapper;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.TestProperties;
import org.junit.jupiter.api.Assertions;
@@ -263,6 +265,67 @@ public class TestMetalakeOperations extends
BaseOperationsTest {
assertNullRequestBodyRejected(resp);
}
+ @Test
+ public void testSetMetalake() {
+ Mockito.doNothing().when(metalakeManager).enableMetalake(any());
+ Mockito.doNothing().when(metalakeManager).disableMetalake(any());
+
+ Response enableResp =
+ target("/metalakes/test")
+ .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .method(
+ "PATCH",
+ Entity.entity(new MetalakeSetRequest(true),
MediaType.APPLICATION_JSON_TYPE));
+
+ Assertions.assertEquals(Response.Status.OK.getStatusCode(),
enableResp.getStatus());
+ Mockito.verify(metalakeManager).enableMetalake(any());
+
+ Response disableResp =
+ target("/metalakes/test")
+ .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .method(
+ "PATCH",
+ Entity.entity(new MetalakeSetRequest(false),
MediaType.APPLICATION_JSON_TYPE));
+
+ Assertions.assertEquals(Response.Status.OK.getStatusCode(),
disableResp.getStatus());
+ Mockito.verify(metalakeManager).disableMetalake(any());
+ }
+
+ @Test
+ public void testSetMetalakeWithNullRequest() {
+ Response resp =
+ target("/metalakes/test")
+ .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .method("PATCH", Entity.entity("null",
MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(resp);
+ }
+
+ @Test
+ public void testSetMetalakeWithMalformedJson() {
+ // The null-body guard must not take over malformed JSON, which keeps
using the registered
+ // Jackson exception mappers.
+ Response resp =
+ target("/metalakes/test")
+ .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .method("PATCH", Entity.entity("{",
MediaType.APPLICATION_JSON_TYPE));
+
+ Assertions.assertEquals(Response.Status.BAD_REQUEST.getStatusCode(),
resp.getStatus());
+ ErrorResponse errorResponse = resp.readEntity(ErrorResponse.class);
+ Assertions.assertEquals(ErrorConstants.ILLEGAL_ARGUMENTS_CODE,
errorResponse.getCode());
+ Assertions.assertEquals(
+ IllegalArgumentException.class.getSimpleName(),
errorResponse.getType());
+ Assertions.assertTrue(errorResponse.getMessage().contains("Malformed json
request"));
+ }
+
@Test
public void testLoadMetalake() {
String metalakeName = "test";
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestOwnerOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestOwnerOperations.java
index 97eb522374..b342f3cfda 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestOwnerOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestOwnerOperations.java
@@ -316,6 +316,17 @@ class TestOwnerOperations extends BaseOperationsTest {
Assertions.assertEquals(ErrorConstants.ILLEGAL_ARGUMENTS_CODE,
errorResponse3.getCode());
}
+ @Test
+ public void testSetOwnerForObjectWithNullRequest() {
+ Response resp =
+ target("/metalakes/metalake1/owners/metalake/metalake1")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .put(Entity.entity("null", MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(resp);
+ }
+
@Test
public void testRoleObject() {
MetadataObject role = MetadataObjects.of(null, "role",
MetadataObject.Type.ROLE);
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestPermissionOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestPermissionOperations.java
index 6a18db9af7..037490a407 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestPermissionOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestPermissionOperations.java
@@ -812,4 +812,81 @@ public class TestPermissionOperations extends
BaseOperationsTest {
Assertions.assertEquals(ErrorConstants.INTERNAL_ERROR_CODE,
errorResponse2.getCode());
Assertions.assertEquals(RuntimeException.class.getSimpleName(),
errorResponse2.getType());
}
+
+ @Test
+ public void testGrantRolesToUserWithNullRequest() {
+ Response resp =
+ target("/metalakes/metalake1/permissions/users/user1/grant")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .put(Entity.entity("null", MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(resp);
+ }
+
+ @Test
+ public void testGrantRolesToGroupWithNullRequest() {
+ Response resp =
+ target("/metalakes/metalake1/permissions/groups/group1/grant")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .put(Entity.entity("null", MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(resp);
+ }
+
+ @Test
+ public void testRevokeRolesFromUserWithNullRequest() {
+ Response resp =
+ target("/metalakes/metalake1/permissions/users/user1/revoke")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .put(Entity.entity("null", MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(resp);
+ }
+
+ @Test
+ public void testRevokeRolesFromGroupWithNullRequest() {
+ Response resp =
+ target("/metalakes/metalake1/permissions/groups/group1/revoke")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .put(Entity.entity("null", MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(resp);
+ }
+
+ @Test
+ public void testGrantPrivilegeToRoleWithNullRequest() {
+ Response resp =
+
target("/metalakes/metalake1/permissions/roles/role1/metalake/metalake1/grant")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .put(Entity.entity("null", MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(resp);
+ }
+
+ @Test
+ public void testRevokePrivilegeFromRoleWithNullRequest() {
+ Response resp =
+
target("/metalakes/metalake1/permissions/roles/role1/metalake/metalake1/revoke")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .put(Entity.entity("null", MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(resp);
+ }
+
+ @Test
+ public void testOverrideRolePrivilegesWithNullRequest() {
+ Response resp =
+ target("/metalakes/metalake1/permissions/roles/role1/")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .put(Entity.entity(new byte[0], MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(resp);
+ }
}
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestPolicyOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestPolicyOperations.java
index 3095512cac..85e45239fe 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestPolicyOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestPolicyOperations.java
@@ -671,6 +671,19 @@ public class TestPolicyOperations extends
BaseOperationsTest {
Assertions.assertEquals(RuntimeException.class.getSimpleName(),
errorResp.getType());
}
+ @Test
+ public void testSetPolicyWithNullRequest() {
+ Response resp =
+ target(policyPath(metalake))
+ .path("policy1")
+ .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .method("PATCH", Entity.entity("null",
MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(resp);
+ }
+
@Test
public void testDeletePolicy() {
when(policyManager.deletePolicy(metalake, "policy1")).thenReturn(true);
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestStatisticOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestStatisticOperations.java
index a970af8214..da591459de 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestStatisticOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestStatisticOperations.java
@@ -73,14 +73,13 @@ import org.apache.gravitino.stats.StatisticValue;
import org.apache.gravitino.stats.StatisticValues;
import org.glassfish.jersey.internal.inject.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
-import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
-public class TestStatisticOperations extends JerseyTest {
+public class TestStatisticOperations extends BaseOperationsTest {
private static class MockServletRequestFactory extends
ServletRequestFactoryBase {
@Override
@@ -392,12 +391,80 @@ public class TestStatisticOperations extends JerseyTest {
.accept("application/vnd.gravitino.v1+json")
.put(entity("null", MediaType.APPLICATION_JSON_TYPE));
- Assertions.assertEquals(Response.Status.BAD_REQUEST.getStatusCode(),
resp.getStatus());
Assertions.assertEquals(MediaType.APPLICATION_JSON_TYPE,
resp.getMediaType());
+ assertNullRequestBodyRejected(resp);
+ }
- ErrorResponse errorResp = resp.readEntity(ErrorResponse.class);
- Assertions.assertEquals(ErrorConstants.ILLEGAL_ARGUMENTS_CODE,
errorResp.getCode());
- Assertions.assertEquals(IllegalArgumentException.class.getSimpleName(),
errorResp.getType());
+ @Test
+ public void testDropStatisticsWithNullRequest() {
+ when(tableDispatcher.tableExists(any())).thenReturn(true);
+
+ MetadataObject tableObject =
+ MetadataObjects.parse(
+ String.format("%s.%s.%s", catalog, schema, table),
MetadataObject.Type.TABLE);
+
+ Response resp =
+ target(
+ "/metalakes/"
+ + metalake
+ + "/objects/"
+ + tableObject.type()
+ + "/"
+ + tableObject.fullName()
+ + "/statistics")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .post(entity("null", MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(resp);
+ }
+
+ @Test
+ public void testUpdatePartitionStatisticsWithNullRequest() {
+ when(tableDispatcher.tableExists(any())).thenReturn(true);
+
+ MetadataObject tableObject =
+ MetadataObjects.parse(
+ String.format("%s.%s.%s", catalog, schema, table),
MetadataObject.Type.TABLE);
+
+ Response resp =
+ target(
+ "/metalakes/"
+ + metalake
+ + "/objects/"
+ + tableObject.type()
+ + "/"
+ + tableObject.fullName()
+ + "/statistics/partitions")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .put(entity("null", MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(resp);
+ }
+
+ @Test
+ public void testDropPartitionStatisticsWithNullRequest() {
+ when(tableDispatcher.tableExists(any())).thenReturn(true);
+
+ MetadataObject tableObject =
+ MetadataObjects.parse(
+ String.format("%s.%s.%s", catalog, schema, table),
MetadataObject.Type.TABLE);
+
+ Response resp =
+ target(
+ "/metalakes/"
+ + metalake
+ + "/objects/"
+ + tableObject.type()
+ + "/"
+ + tableObject.fullName()
+ + "/statistics/partitions")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .post(entity(new byte[0], MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(resp);
}
@Test
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestTagOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestTagOperations.java
index e5152b8046..ae15334359 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestTagOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestTagOperations.java
@@ -1131,6 +1131,22 @@ public class TestTagOperations extends
BaseOperationsTest {
Assertions.assertEquals(RuntimeException.class.getSimpleName(),
errorResponse1.getType());
}
+ @Test
+ public void testAssociateTagsForObjectWithNullRequest() {
+ MetadataObject catalog = MetadataObjects.parse("object1",
MetadataObject.Type.CATALOG);
+
+ // The deprecated route delegates to MetadataObjectTagOperations, so it
inherits the same guard.
+ Response response =
+ target(tagPath(metalake))
+ .path(catalog.type().toString())
+ .path(catalog.fullName())
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .post(Entity.entity("null", MediaType.APPLICATION_JSON_TYPE));
+
+ assertNullRequestBodyRejected(response);
+ }
+
private String tagPath(String metalake) {
return "/metalakes/" + metalake + "/tags";
}