Skip to content

[#12769] fix(server): handle null request bodies in createView - #12770

Merged
jerryshao merged 2 commits into
apache:mainfrom
mchades:codex/view-create-null-request
Sep 1, 2026
Merged

[#12769] fix(server): handle null request bodies in createView#12770
jerryshao merged 2 commits into
apache:mainfrom
mchades:codex/view-create-null-request

Conversation

@mchades

@mchades mchades commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

  • Guard ViewOperations.createView against a null request before accessing any
    request fields or invoking request.validate().
  • Route the validation failure through the existing view exception handler so
    the endpoint returns a structured HTTP 400 response.
  • Cache the view name after the null guard and reuse it for logging, identifier
    construction, and catch-path error handling.
  • Add TestViewOperations coverage for an empty/null request entity.
  • Verify that malformed JSON continues to use the existing JSON exception
    mappers and returns HTTP 400.

Why are the changes needed?

An empty create-view request currently causes ViewOperations.createView to
dereference request before null validation. This produces an unhandled HTTP
500 response with an empty body instead of the structured client-error response
used by other REST endpoints.

The change follows the existing null-request handling pattern in
MetalakeOperations.createMetalake.

Fix: #12769

Does this PR introduce any user-facing change?

Yes. A create-view request with an empty or null body now returns a structured
HTTP 400 response instead of an empty HTTP 500 response.

There are no API schema or configuration changes. Valid requests and malformed
JSON handling are unchanged.

How was this patch tested?

Before the fix, the null-request regression test failed with:

expected: <400> but was: <500>

The malformed-JSON regression test passed independently, confirming that the
existing mapper behavior was not the cause.

After the fix:

./gradlew :server:test \
  --tests org.apache.gravitino.server.web.rest.TestViewOperations \
  -PskipITs --no-daemon

Result: 9 tests passed, 0 failures, 0 errors.

Additional checks:

./gradlew :server:spotlessCheck --no-daemon
git diff --check

Both checks passed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes POST .../views so that an empty/null request body no longer triggers a NullPointerException (HTTP 500 with empty body) and instead returns the existing structured client error response (HTTP 400) consistent with other REST endpoints (e.g., MetalakeOperations.createMetalake).

Changes:

  • Add a null guard in ViewOperations.createView and route the error through ExceptionHandlers.handleViewException to produce a structured HTTP 400 response.
  • Cache the view name after the null guard and reuse it for logging, identifier construction, and catch-path error handling.
  • Add REST tests covering a null request entity and malformed JSON, registering the JSON exception mappers to validate expected HTTP 400 behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
server/src/main/java/org/apache/gravitino/server/web/rest/ViewOperations.java Prevents null dereference on create-view requests and returns structured HTTP 400 via existing exception handling.
server/src/test/java/org/apache/gravitino/server/web/rest/TestViewOperations.java Adds regression tests for null request bodies and malformed JSON, ensuring HTTP 400 + structured ErrorResponse.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@mchades mchades added the branch-1.3 Automatically cherry-pick commit to branch-1.3 label Sep 1, 2026
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

Code Coverage Report

Overall Project 68.87% +0.82% 🟢
Files changed 92.24% 🟢

Module Coverage
aliyun 19.74% 🔴
api 51.74% 🟢
authorization-common 85.96% 🟢
authorization-ranger 4.38% 🔴
aws 53.54% 🟢
azure 32.1% 🔴
catalog-common 19.1% 🔴
catalog-fileset 80.3% 🟢
catalog-glue 69.24% 🟢
catalog-hive 82.96% 🟢
catalog-jdbc-common 45.69% 🟢
catalog-jdbc-doris 82.69% 🟢
catalog-jdbc-mysql 79.33% 🟢
catalog-jdbc-postgresql 83.39% 🟢
catalog-jdbc-starrocks 79.16% 🟢
catalog-kafka 76.99% 🟢
catalog-lakehouse-generic 60.55% 🟢
catalog-lakehouse-hudi 79.1% 🟢
catalog-lakehouse-iceberg 85.93% 🟢
catalog-lakehouse-paimon 84.26% 🟢
catalog-model 77.99% 🟢
cli 44.48% 🟢
client-java 77.48% 🟢
common 55.43% 🟢
core 83.85% +0.4% 🟢
filesystem-hadoop3 76.45% 🟢
flink 0.0% 🔴
flink-common 52.1% 🟢
flink-runtime 0.0% 🔴
gcp 32.2% 🔴
hadoop-auth 68.0% 🟢
hadoop-common 17.84% 🔴
hive-metastore-common 53.4% 🟢
iceberg-aliyun-bundle 0.0% 🔴
iceberg-common 64.75% 🟢
iceberg-rest-server 75.96% 🟢
idp-basic 85.98% 🟢
integration-test-common 0.0% 🔴
jobs 62.92% 🟢
lance-common 32.63% 🔴
lance-rest-server 65.46% 🟢
lineage 53.02% 🟢
optimizer 83.17% 🟢
optimizer-api 21.95% 🔴
server 88.35% +4.79% 🟢
server-common 80.5% 🟢
spark 28.57% 🔴
spark-common 48.92% 🟢
tencent 81.78% 🟢
trino-connector 51.26% 🟢
Files
Module File Coverage
core RoleMetaBaseSQLProvider.java 100.0% 🟢
RoleMetaPostgreSQLProvider.java 100.0% 🟢
OccWriteSupport.java 100.0% 🟢
UserMetaService.java 96.23% 🟢
GroupMetaService.java 96.08% 🟢
RoleMetaSQLProviderFactory.java 96.0% 🟢
RoleMetaService.java 94.83% 🟢
FilesetMetaService.java 93.98% 🟢
POConverters.java 88.34% 🟢
ModelMetaService.java 81.82% 🟢
TopicMetaService.java 77.34% 🟢
RoleMetaMapper.java 0.0% 🔴
server CatalogOperations.java 100.0% 🟢
FunctionOperations.java 100.0% 🟢
ModelOperations.java 100.0% 🟢
SchemaOperations.java 100.0% 🟢
TagOperations.java 100.0% 🟢
ViewOperations.java 100.0% 🟢
PolicyOperations.java 97.39% 🟢
TableOperations.java 97.26% 🟢
TopicOperations.java 97.26% 🟢
JobOperations.java 93.58% 🟢
FilesetOperations.java 85.98% 🟢
MetalakeOperations.java 79.52% 🟢

@mchades mchades self-assigned this Sep 1, 2026
new IllegalArgumentException("Request body cannot be null"));
}

String viewName = request.getName();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name can also be null before validation. You'd better also check nullable before logging.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SLF4J safely logs a null name as null; returning before the log would lose that request trace. request.validate() then rejects it before use.

ViewCreateRequest request) {
LOG.info(
"Received create view request: {}.{}.{}.{}", metalake, catalog, schema, request.getName());
if (request == null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we only check null and ignore the same problem in L181?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original change only covered the reported createView path. alterView and the equivalent alter operations now reject null request bodies before validate(), with regression tests.

@jerryshao
jerryshao merged commit 391493c into apache:main Sep 1, 2026
38 checks passed
jerryshao pushed a commit that referenced this pull request Sep 1, 2026
… bodies in createView (#12770) (#12789)

**Cherry-pick Information:**
- Original commit: 391493c
- Target branch: `branch-1.3`
- Status: ✅ Clean cherry-pick (no conflicts)

Co-authored-by: mchades <liminghuang@datastrato.com>
@mchades
mchades deleted the codex/view-create-null-request branch September 2, 2026 05:58
jerryshao pushed a commit that referenced this pull request Sep 2, 2026
…reate/register/add operations (#12791)

### What changes were proposed in this pull request?

Several `create`/`register`/`add` REST operations called
`request.validate()` without first checking for a null request body, so
an empty or JSON `null` body dereferenced the request and returned HTTP
500 instead of a proper 400. Applied the existing `createMetalake`
pattern (null-check first, then route an `IllegalArgumentException`
through the corresponding exception handler) to:

- Catalog, Schema, Table, Fileset, Topic, Policy, and Tag creation
- Function, Model, and JobTemplate registration
- User, Group, and Role creation
- Bulk user and group creation

Also updated a pre-existing test in `TestGroupOperations` that had
asserted the old 500 status as expected behavior, and added a
`WithNullRequest` test for each fixed operation, matching the
`assertNullRequestBodyRejected` pattern from #12770.

### Why are the changes needed?

A null request body currently returns HTTP 500 with an internal error
response for these operations, instead of a structured 400. This is
inconsistent with the already-fixed `alter`/`createView` operations
(#12769, #12770) and with `createMetalake`, and it leaks an internal
error to the caller for what is really a bad request.

Fix: #12788

### Does this PR introduce _any_ user-facing change?

Yes. A `create`/`register`/`add` request with a null or empty body now
returns HTTP 400 with error code `1001`, error type
`IllegalArgumentException`, and a message stating the request body
cannot be null, instead of HTTP 500.

### How was this patch tested?

Added a `testXxxWithNullRequest()` test for each of the 14 fixed
operations, verifying HTTP 400, error code `1001`, error type
`IllegalArgumentException`, and the error message, using the shared
`assertNullRequestBodyRejected` helper. Ran `:server:test` scoped to the
14 affected test classes: 176 tests, all passing.
yuqi1129 pushed a commit that referenced this pull request Sep 3, 2026
… body before validate() in create/register/add operations (#12791) (#12845)

**Cherry-pick Information:**
- Original commit: 29a566b
- Target branch: `branch-1.3`
- Status: ✅ Conflicts resolved, ready for review

**Conflict resolution notes:**
The automated cherry-pick left unresolved git conflict markers in 13
REST
operation classes and `TestGroupOperations`, and introduced
`BulkOperations.java`/`TestBulkOperations.java` as brand-new files whose
dependencies (`org.apache.gravitino.bulk.*`, `Bulk*Request`/`Response`
DTOs)
do not exist on `branch-1.3`. The following manual resolution was
applied:

- Resolved conflict markers by keeping the incoming null-check fix in
  Catalog/Schema/Table/Fileset/Topic/Function/Model/Tag/Policy/Job/Role/
  Group/User operations.
- Fixed unused local variable warnings (errorprone `-Werror`) left by
the
resolution by wiring the new name variables into the existing
catch-block
  exception handler calls, matching upstream `main`.
- Removed `BulkOperations.java` and `TestBulkOperations.java`: the Bulk
  user/group REST feature they depend on has not been ported to
  `branch-1.3`.
- Removed `testAddGroupWithNullRequestBodyDoesNotExposeNpe` and
`testAddGroupWithExternalId` from `TestGroupOperations`: they originate
  from unrelated upstream commits (#12590, #11921) not present on
  `branch-1.3`, and leaked in via cherry-pick diff context.

Verified `:server:compileJava`, `:server:compileTestJava`, and
`:server:spotlessCheck` pass, and the targeted unit tests for all 13
modified operation test classes pass with 0 failures.

---

### Original PR description (#12791)

### What changes were proposed in this pull request?

Several `create`/`register`/`add` REST operations called
`request.validate()`
without first checking for a null request body, so an empty or JSON
`null`
body dereferenced the request and returned HTTP 500 instead of a proper
400.
Applied the existing `createMetalake` pattern (null-check first, then
route
an `IllegalArgumentException` through the corresponding exception
handler)
to:

- Catalog, Schema, Table, Fileset, Topic, Policy, and Tag creation
- Function, Model, and JobTemplate registration
- User, Group, and Role creation

### Why are the changes needed?

A null request body currently returns HTTP 500 with an internal error
response for these operations, instead of a structured 400. This is
inconsistent with the already-fixed `alter`/`createView` operations
(#12769, #12770) and with `createMetalake`, and it leaks an internal
error
to the caller for what is really a bad request.

Fix: #12788

### Does this PR introduce _any_ user-facing change?

Yes. A `create`/`register`/`add` request with a null or empty body now
returns HTTP 400 with error code `1001`, error type
`IllegalArgumentException`, and a message stating the request body
cannot
be null, instead of HTTP 500.

### How was this patch tested?

Ran the targeted unit tests for the 13 affected test classes on
`branch-1.3`: all passing with 0 failures.

---------

Co-authored-by: Fayupable <90789180+Fayupable@users.noreply.github.com>
Co-authored-by: Jerry Shao <jshao@apache.org>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
mchades pushed a commit that referenced this pull request Sep 3, 2026
…erations (#12866)

### What changes were proposed in this pull request?

- Reject a null request body in the 20 operations listed in #12834,
using operation-level guards in 9 resource classes.
- Move the pre-existing `updateStatistics` guard out of the `Utils.doAs`
lambda to the front of the method and drop its private message constant,
so all four operations in `StatisticOperations` report the same message.
- Return the 400 for `testConnection` directly through
`Utils.illegalArguments(...)` instead of
`handleTestConnectionException`, which by design reports connection-test
outcomes inside an HTTP 200 body.
- Follow the existing pattern of `MetalakeOperations.createMetalake`.
- Simplify the null-safe field extractions used for logging and are now
unreachable, in `testConnection`, `PermissionOperations` and
`StatisticOperations`.
- Add regression coverage for every fixed operation, plus a
malformed-JSON test and a normal-path `setMetalake` test.



### Why are the changes needed?

An omitted body, an empty body, or a JSON literal `null` is a client
error, but these endpoints answer it with HTTP 500.

Fix: #12834

### Does this PR introduce _any_ user-facing change?

Yes. These endpoints now return HTTP 400 with error code `1001`, error
type `IllegalArgumentException`, and a message stating that the request
body cannot be null, instead of HTTP 500.

One existing message also changes: a null body on `PUT
/api/metalakes/{metalake}/objects/{type}/{fullName}/statistics`
previously returned `Statistics update request body cannot be null` and
now returns the canonical `Request body cannot be null`. Its status code
and error code are unchanged.

There are no API schema or configuration changes, valid requests behave
exactly as before, and malformed-JSON handling is unchanged.

### How was this patch tested?

Added 21 `testXxxWithNullRequest()` tests, one per fixed operation plus
one for the deprecated tag route, using the shared
`assertNullRequestBodyRejected` helper introduced by #12770.
`TestStatisticOperations` and `TestMetadataObjectPolicyOperations` now
extend `BaseOperationsTest` to use it, and the pre-existing
`testUpdateTableStatisticsWithNullRequestBody` was converged onto it.
Also added `testSetMetalakeWithMalformedJson`, which sends `{` and
asserts the response still comes from the JSON exception mappers, and
`testSetMetalake` for the normal enable/disable path, which had no
coverage before.

With the production changes reverted and only the tests applied, all 21
null-body tests fail with:

```text
expected: <400> but was: <500>
```

With the fix applied:

```shell
./gradlew :server:test -PskipITs --no-daemon        # 396 tests, 0 failures, 0 errors
./gradlew :server:spotlessCheck --no-daemon
git diff --check
```


### Note for reviewers

With `gravitino.authorization.enable=true` the two `associate*`
endpoints never reach the new guard: `AssociateTagAuthorizationExecutor`
/ `AssociatePolicyAuthorizationExecutor` return `false` for a null
request, so `GravitinoInterceptionService` answers 403 before the
resource method runs. The guard is still correct for the default
configuration, where that service is not registered at all. I left the
executors alone as changing an authorization decision felt out of scope;
a follow-up would have to `return true` there, so that a null body
reaches the guard in the resource method and is rejected with the 400.
Happy to file that issue if you would like it handled.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

branch-1.3 Automatically cherry-pick commit to branch-1.3

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug report] createView returns an empty HTTP 500 response for an empty request body

4 participants