dotCMS / dotCMS/core

fix: OpenAPI spec inconsistencies and documentation gaps in PublishingResource (/api/v1/publishing)

Open
#34,860 1 comment 1 reaction 3 assignees View on GitHub

@fmontes is already working on this.

Since Mar 4, 2026.

  • #34910 by @hassandotcms — closed without merging
Bug Doc : Needs Doc dotCMS : Rest API
Dominant language
Java
Stars
970
Forks
486
Avg merge
3d 33m
Merged PRs (30d)
170

Description

Summary

A review of the OpenAPI annotations in PublishingResource.java and its associated form/view classes revealed 10 issues ranging from spec-vs-implementation mismatches (confirmed by curl tests) to missing schema metadata that breaks code generation and Swagger UI usability.

Affected file: dotCMS/src/main/java/com/dotcms/rest/api/v1/publishing/PublishingResource.java and related files in the same package (PushBundleForm, AbstractRetryBundlesForm, AbstractRetryBundleResultView, AbstractPublishingJobView, AbstractPublishingJobDetailView, AbstractEndpointDetailView).


Findings

HIGH — Spec vs. Implementation mismatches (confirmed by curl tests)

1. status query params lack machine-readable allowableValues

  • Affects: GET /api/v1/publishing?status= and DELETE /api/v1/publishing/purge?status=
  • Valid values are listed as prose text only. Swagger UI won't render a dropdown and codegen tools won't produce enums.
  • Fix: Add @Schema(allowableValues = {...}) with all valid PublishAuditStatus.Status names, or change to List<String> with collection format.

2. DELETE /{bundleId} — 404 documented but never returned

  • Confirmed by TC-3.1 and TC-3.3: deleting a non-existent bundle returns 200 {"message":"Bundle deleted successfully"} instead of 404.
  • @ApiResponse(responseCode = "404") is unreachable — deleteBundleAndDependencies succeeds silently for unknown IDs.
  • Fix: Return 404 when neither audit nor bundle table contains the ID (correct fix), or remove the 404 response from the spec.

3. POST /push/{bundleId} — validation order causes wrong status codes

  • Confirmed by TC-5.9, TC-5.10, TC-5.15, TC-5.17: requests with invalid date format, invalid filterKey, or uppercase operation return 404 (bundle not found) instead of 400, because parseISO8601Date and filterKey lookup run after the bundle existence check.
  • The spec documents 400 for these cases but the implementation returns 404 when the bundle is missing.
  • Fix: Move date-format parsing and filterKey validation into form.checkValid() before the bundle lookup.

MEDIUM — Missing schema metadata

4. POST /retrydeliveryStrategy missing allowableValues and case-sensitivity note

  • AbstractRetryBundlesForm.deliveryStrategy() doesn't list allowed values or state that the enum is case-sensitive (TC-4.13: "all_endpoints" returns 400).
  • Fix: Add allowableValues = {"ALL_ENDPOINTS", "FAILED_ENDPOINTS"} and defaultValue = "ALL_ENDPOINTS".

5. POST /push/{bundleId}operation case-insensitivity not documented

  • PushBundleForm does operation.toLowerCase(), making operation case-insensitive. But deliveryStrategy in retry is case-sensitive. The docs treat both identically.
  • Fix: Add "Case-insensitive." to the operation description. Consider making deliveryStrategy case-insensitive too for consistency.

6. PushBundleForm date fields — missing format and no timezone-required note

  • publishDate / expireDate lack format = "date-time". The docs don't state that timezone offset is mandatory — a date without offset (e.g., "2025-12-01T10:00:00") fails (confirmed by TC-5.10).
  • Fix: Add format = "date-time" and note timezone offset is required.

7. filterKey absent from list and detail views

  • AbstractPublishingJobView and AbstractPublishingJobDetailView expose filterName (display label) but not filterKey (the value needed for API calls).
  • A developer viewing a job has no way to know which filterKey to pass to POST /push/{bundleId} to reproduce the push.
  • Fix: Add filterKey field to AbstractPublishingJobDetailView at minimum.

LOW — Minor doc improvements

8. DELETE /purge — async completion mechanism not explained for API clients

  • The 200 description says "processes in background" but doesn't explain how results are delivered (dotCMS system message/WebSocket). Non-browser clients will never receive the completion notification.
  • Fix: Add to the description: "The result is delivered as a system notification to the triggering user's browser session. Non-browser API clients will not receive the completion event."

9. bundleId path param example uses wrong format

  • @Parameter(example = "f3d9a4b7-staging-bundle-2026-01-15") doesn't match real bundle IDs which are ULID format (e.g., 01KJWNJM2C67DM56GHBJ4S7B89).
  • Fix: Update examples to ULID format.

10. EndpointDetailView.port — no format constraint on required string

  • port() is String + requiredMode = REQUIRED but accepts "". No pattern or range documented.
  • Fix: Add a note on valid values or a pattern constraint.

Steps to reproduce (bug items)

BASE="http://localhost:7070"
TOKEN="<valid-backend-token>"
MISSING_BUNDLE="01KJWNJM2C67DM56GHBJ4S7B89"

# Bug 2 - DELETE non-existent bundle returns 200 instead of 404
curl -s -w "\nHTTP %{http_code}" -X DELETE \
  -H "Authorization: Bearer $TOKEN" \
  "$BASE/api/v1/publishing/$MISSING_BUNDLE"
# Expected: 404   Actual: 200

# Bug 3 - Invalid date format returns 404 instead of 400
curl -s -w "\nHTTP %{http_code}" -X POST \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"operation":"publish","publishDate":"2025/12/01 10:00:00","environments":["env-id"],"filterKey":"ForcePush.yml"}' \
  "$BASE/api/v1/publishing/push/$MISSING_BUNDLE"
# Expected: 400   Actual: 404

Acceptance Criteria

  • DELETE /{bundleId} returns 404 when the bundle does not exist in either audit or bundle table
  • All input validation in pushBundle (date format, filterKey, operation) runs before the bundle existence check
  • status query parameters on GET list and DELETE purge have machine-readable allowableValues
  • deliveryStrategy in RetryBundlesForm has allowableValues and case-sensitivity documented
  • operation in PushBundleForm has case-insensitivity documented
  • publishDate/expireDate annotated with format = "date-time" and timezone requirement stated
  • filterKey exposed in PublishingJobDetailView
  • Purge async behavior documented for non-browser clients
  • bundleId examples updated to ULID format
  • EndpointDetailView.port has format/constraint documented

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.