Skip to content

[codex] Adapt portal OpenAPI to v0.2.0#5608

Open
nobodyiam wants to merge 3 commits into
apolloconfig:masterfrom
nobodyiam:codex/openapi-migration-latest-spec
Open

[codex] Adapt portal OpenAPI to v0.2.0#5608
nobodyiam wants to merge 3 commits into
apolloconfig:masterfrom
nobodyiam:codex/openapi-migration-latest-spec

Conversation

@nobodyiam
Copy link
Copy Markdown
Member

@nobodyiam nobodyiam commented May 10, 2026

What's the purpose of this PR

Adapt Apollo Portal to the published apollo-openapi v0.2.0 contract and continue the Portal OpenAPI migration work started in #5607.

This PR updates the Portal OpenAPI generator input from v0.1.0 to v0.2.0, aligns the App and Cluster OpenAPI controller/service contracts with the generated interfaces, and keeps already-migrated Portal UI App reads on the current OpenAPI paths.

It also makes operator propagation explicit below controllers, so service-layer code no longer depends on UserInfoHolder.getUser() for OpenAPI requests.

Which issue(s) this PR fixes:

N/A

Brief changelog

  • Point apollo-portal OpenAPI generation to apollo-openapi v0.2.0.
  • Adapt App OpenAPI return types and endpoints, including env cluster info and missing env responses.
  • Normalize Portal UI App OpenAPI responses for existing frontend callers.
  • Pass operator explicitly from controllers into Portal services and OpenAPI server services.
  • Remove converter references to models that are no longer generated by v0.2.0.
  • Add and update tests for App/Cluster OpenAPI contracts, operator propagation, and Portal service call paths.
  • Update the temporary OpenAPI migration tracking docs for the v0.2.0 baseline.

Follow this checklist to help us incorporate your contribution quickly and easily:

  • Read the Contributing Guide before making this pull request.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Write necessary unit tests to verify the code.
  • Run mvn clean test to make sure this pull request doesn't break anything.
  • Run mvn spotless:apply to format your code.
  • Update the CHANGES log.

Verification

  • git ls-remote --tags https://github.com/apolloconfig/apollo-openapi.git refs/tags/v0.2.0
  • python3 scripts/openapi/check_openapi_compatibility_test.py
  • python3 scripts/openapi/check_openapi_compatibility.py --base https://raw.githubusercontent.com/apolloconfig/apollo-openapi/refs/tags/v0.1.0/apollo-openapi.yaml --head https://raw.githubusercontent.com/apolloconfig/apollo-openapi/refs/tags/v0.2.0/apollo-openapi.yaml fails as expected with documented v0.1.0 to v0.2.0 compatibility differences.
  • ./mvnw -B -pl apollo-portal -am -DskipTests clean compile
  • ./mvnw -B clean test
  • ./mvnw -B -pl apollo-assembly -am -DskipTests package
  • Portal e2e with H2 envs from .github/workflows/portal-ui-e2e.yml: wait-for-ready.sh passed; npm ci && npx playwright install chromium && BASE_URL=http://127.0.0.1:8070 PLAYWRIGHT_WORKERS=2 npm run test:e2e:ci passed with 16 tests.
  • ./mvnw -B spotless:apply
  • git diff --check

Summary by CodeRabbit

  • New Features

    • OpenAPI specification upgraded to v0.2.0.
  • Improvements

    • Explicit operator/audit context added across app, cluster, namespace, import, and consumer flows.
    • Environment-cluster and missing-env endpoints return structured per-env status with normalized responses.
    • Frontend service calls normalized to new OpenAPI paths.
  • Documentation

    • Updated OpenAPI migration tracking and frontend URL inventory.
  • Tests

    • Added/updated tests for operator handling, validation, and per-env status behaviors.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 10, 2026

📝 Walkthrough

Walkthrough

Upgrades Apollo Portal to apollo-openapi v0.2.0 and threads explicit operator/modifiedBy parameters through OpenAPI controllers, portal controllers, and services; updates OpenAPI response types and frontend OpenAPI calls; removes UserInfoHolder wiring; updates converters, tests, and migration docs.

Changes

OpenAPI v0.2.0 Migration with Operator Parameter Threading

Layer / File(s) Summary
Spec & Public Contracts
CHANGES.md, apollo-portal/pom.xml, .../AppOpenApiService.java, .../ClusterOpenApiService.java
POM updated to reference apollo-openapi v0.2.0. OpenAPI interfaces adjusted: operator parameters added; env-cluster/miss-env return types updated; legacy navtree endpoint removed.
OpenAPI Server Implementations
.../ServerAppOpenApiService.java, .../ServerClusterOpenApiService.java, .../ServerNamespaceOpenApiService.java, .../ServerReleaseOpenApiService.java
Server-side OpenAPI methods now accept operator arguments, build per-env status DTOs (OpenEnvClusterInfo/OpenMissEnvDTO), and validate required fields (throw BadRequestException where appropriate).
Portal Service Refactoring
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/*
Removed UserInfoHolder from many constructors; public methods now accept explicit operator/modifiedBy parameters (AppService, ClusterService, NamespaceService, ItemService, ConfigsImportService, FavoriteService, NamespaceBranchService, ServerConfigService, ReleaseService, etc.); audit fields set from operator.
Portal Controllers: Operator Derivation
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/*
Controllers now read current user id from UserInfoHolder and pass it as operator to service write operations (apps, clusters, namespaces, items, favorites, consumers, imports, server configs, branches, releases).
OpenAPI v1 Controllers: Operator Resolution
apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/*
AppController and ClusterController added resolveOperator helpers to validate/derive operator based on auth type (USER vs CONSUMER); endpoint signatures and response types updated (e.g., ResponseEntity, List, List); getAppNavTree removed.
Model Converters & Cleanup
apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/util/OpenApiModelConverters.java
Removed unused converters (ReleaseBO→OpenReleaseBO, namespace sync converters) and adjusted gray release rule conversion to iterate Set of rule items.
Frontend Service Updates
apollo-portal/src/main/resources/static/scripts/services/AppService.js
load_navtree/openapi/v1/apps/:appId/env-cluster-info; find_miss_envs/openapi/v1/apps/:appId/miss-envs; added normalizeOpenApiStatusArray() to map array-of-status responses into legacy entities shape.
Tests & Documentation
apollo-portal/src/test/java/..., docs/*, CHANGES.md
Unit and controller tests updated/added to match operator-threaded APIs and OpenAPI behavior; migration docs updated to reflect v0.2.0 baseline and path changes; CHANGES release note added.

Estimated code review effort
🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

size:L

Suggested reviewers

  • arrow1991
  • hezhangjian

🐰 I hopped from v0.1 to v0.2 with cheer,
Operators threaded, the path made clear.
DTOs aligned and tests now sing,
Docs updated for the migration spring.
A tiny rabbit celebrates this gear.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

@nobodyiam nobodyiam marked this pull request as ready for review May 10, 2026 14:58
Copilot AI review requested due to automatic review settings May 10, 2026 14:59
@dosubot dosubot Bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label May 10, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 advances the Apollo Portal OpenAPI migration by bumping the generator input contract to apollo-openapi v0.2.0 and aligning Portal OpenAPI controllers/server-services (plus selected Portal UI reads) with the updated generated interfaces and response shapes. It also continues the migration goal of making the operator explicit below controller boundaries rather than relying on UserInfoHolder inside service-layer logic for OpenAPI requests.

Changes:

  • Update apollo-portal OpenAPI spec URL to apollo-openapi v0.2.0 and adapt OpenAPI App/Cluster contracts to the new paths and DTOs.
  • Propagate operator explicitly through Portal services/OpenAPI server services; update controllers and tests accordingly.
  • Keep already-migrated Portal UI App reads on OpenAPI while normalizing new array-style responses back into the legacy entities/body structure expected by AppUtil.collectData.

Reviewed changes

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

Show a summary per file
File Description
docs/zh/contribution/apollo-portal-openapi-migration-tracking.md Update migration tracking notes for v0.2.0 baseline and path/compat policy.
docs/zh/contribution/apollo-portal-openapi-frontend-url-inventory.md Update inventory entries for the new App OpenAPI paths (env-cluster-info, miss-envs).
docs/en/contribution/apollo-portal-openapi-migration-tracking.md English migration tracking updates for v0.2.0 baseline and exceptions.
docs/en/contribution/apollo-portal-openapi-frontend-url-inventory.md English inventory updates to reflect the new OpenAPI paths.
CHANGES.md Add release-note entry referencing the OpenAPI v0.2.0 adaptation PR.
apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/ServiceExceptionTest.java Adjust App service call signature and mock operator source.
apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/NamespaceServiceTest.java Update namespace deletion to pass explicit operator.
apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/FavoriteServiceTest.java Update FavoriteService calls to include explicit operator/login user id.
apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/ConfigsExportServiceTest.java Update import/export test stubs to include operator parameter propagation.
apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/ConfigServiceTest.java Update ItemService method signatures to accept operator explicitly.
apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/AppServiceTest.java Update AppService APIs to accept operator explicitly (removing UserInfoHolder reliance).
apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/AppNamespaceServiceTest.java Update AppNamespaceService APIs to accept operator explicitly.
apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ServerConfigControllerTest.java Update controller tests for new service signatures requiring operator.
apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ConsumerControllerTest.java Update ConsumerController construction and operator propagation assertions.
apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ConfigsImportControllerTest.java Update controller tests for operator being passed into import service calls.
apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ClusterControllerTest.java Update cluster controller tests for operator propagation.
apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterControllerTest.java Update OpenAPI v1 cluster tests for operator propagation to services.
apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterControllerParamBindLowLevelTest.java Ensure operator query param binding reaches service layer.
apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerTest.java Adapt App OpenAPI tests to v0.2.0 paths/DTOs and new return/void semantics.
apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerParamBindLowLevelTest.java Add/adjust low-level binding tests for create/update/delete operator handling.
apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/service/ConsumerServiceTest.java Update ConsumerService tests for explicit operator parameters.
apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/server/service/ServerAppOpenApiServiceTest.java New unit tests for v0.2.0 App server-service DTO shapes and per-env failures.
apollo-portal/src/main/resources/static/scripts/services/AppService.js Update UI OpenAPI paths and normalize new array responses into legacy entities/body shape.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ServerConfigService.java Remove UserInfoHolder dependency; require modifiedBy explicitly.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ReleaseService.java Remove implicit fallback to UserInfoHolder; rely on releasedBy set by callers.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/NamespaceService.java Remove UserInfoHolder dependency; pass operator explicitly for create/delete.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/NamespaceBranchService.java Remove overloads that implicitly read operator from UserInfoHolder.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ItemService.java Remove UserInfoHolder dependency; require operator explicitly for mutations.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/FavoriteService.java Remove UserInfoHolder dependency; require login user id explicitly.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ConfigsImportService.java Remove UserInfoHolder dependency; pass operator through import workflow.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ClusterService.java Remove UserInfoHolder dependency; require operator for create/delete.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppService.java Remove UserInfoHolder dependency; require operator for create/update/delete flows.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppNamespaceService.java Remove UserInfoHolder dependency; require operator for create/delete flows.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ServerConfigController.java Pass current user id into ServerConfigService methods.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ReleaseController.java Ensure releasedBy is set from current user before publishing releases.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceController.java Pass operator explicitly into Namespace/AppNamespace service operations.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceBranchController.java Pass operator explicitly into branch operations and merges.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ItemController.java Pass operator explicitly into ItemService mutation operations.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/FavoriteController.java Pass operator explicitly into FavoriteService operations.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConsumerController.java Pass operator explicitly into ConsumerService operations.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConfigsImportController.java Pass operator explicitly into ConfigsImportService operations.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ClusterController.java Pass operator explicitly into ClusterService operations.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AppController.java Pass operator explicitly into AppService operations.
apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterController.java Align with v0.2.0 operator semantics and propagate operator to OpenAPI services.
apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/AppController.java Align App OpenAPI endpoints/DTOs with v0.2.0 and explicit operator resolution.
apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/util/OpenApiModelConverters.java Remove converters for models no longer generated in v0.2.0; adjust rule-items type.
apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerService.java Remove UserInfoHolder dependency; require operator explicitly for write operations.
apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerNamespaceOpenApiService.java Propagate operator into AppNamespace creation.
apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerClusterOpenApiService.java Propagate operator into cluster create/delete operations.
apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerAppOpenApiService.java Adapt App server-service to v0.2.0 DTOs/paths and operator propagation.
apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ClusterOpenApiService.java Update OpenAPI server service interface for operator propagation.
apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/AppOpenApiService.java Update OpenAPI server service interface for v0.2.0 return types and operator propagation.
apollo-portal/pom.xml Point OpenAPI generator input spec URL to apollo-openapi v0.2.0.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

logger.warn("Failed to load env {} navigation for app {}", env, appId, e);
OpenEnvClusterInfo info = new OpenEnvClusterInfo();
info.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
info.setMessage("load env:" + env.getName() + " cluster error." + e.getMessage());
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in abfd47a. The env-cluster-info error path now logs the exception server-side and returns a stable client-facing message without appending e.getMessage().

"load env:" + env.getName() + " cluster error." + e.getMessage());
entity.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
entity.setMessage(
String.format("load appId:%s from env %s error.", appId, env) + e.getMessage());
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in abfd47a. The miss-envs error path now formats a stable response message and logs the full exception instead of returning raw exception text.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ReleaseService.java (1)

57-72: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add validation for releasedBy in ServerReleaseOpenApiService.publishNamespace() before transforming the model.

The OpenAPI v1 controller validates releasedBy is non-empty before calling publishNamespace, but ServerReleaseOpenApiService.publishNamespace() is a public interface method that lacks its own validation. When BeanUtils.transform() copies properties from NamespaceReleaseDTO to NamespaceReleaseModel, a null or empty releasedBy from the DTO will either not be copied or remain null. This should be validated in the service method rather than relying on callers to ensure the field is populated, especially since the method is publicly exposed as ReleaseOpenApiService interface.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ReleaseService.java`
around lines 57 - 72, ServerReleaseOpenApiService.publishNamespace() must
validate the releasedBy field before transforming the DTO into a
NamespaceReleaseModel; add a null/empty check on
NamespaceReleaseDTO.getReleasedBy() (or the incoming parameter) at the start of
publishNamespace(), and throw an appropriate exception (e.g.,
IllegalArgumentException or a BadRequest/BadParam exception used in this
codebase) if releasedBy is blank so BeanUtils.transform() cannot produce a model
with a null/empty releasedBy; reference the method
ServerReleaseOpenApiService.publishNamespace(), the DTO class
NamespaceReleaseDTO, the model NamespaceReleaseModel and the
BeanUtils.transform() call when applying the change.
apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterController.java (1)

79-81: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove duplicate user existence validation.

The resolveOperator method already validates that the user exists (lines 108-110), so this additional check is redundant.

♻️ Proposed fix to remove duplication
-    if (userService.findByUserId(operator) == null) {
-      throw BadRequestException.userNotExists(operator);
-    }
-
     return ResponseEntity.ok(this.clusterOpenApiService.createCluster(env, cluster, operator));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterController.java`
around lines 79 - 81, Remove the redundant user existence check in
ClusterController: delete the block that calls
userService.findByUserId(operator) and throws
BadRequestException.userNotExists(operator), since resolveOperator already
performs that validation; keep resolveOperator as the single source of truth for
operator validation to avoid duplicated checks.
🧹 Nitpick comments (7)
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/FavoriteService.java (1)

73-101: 💤 Low value

Optional: avoid reassigning the userId parameter.

Line 84 silently reassigns the userId method parameter to loginUserId when the caller is requesting another user's favorites. Functionally this preserves the previous "user can only search his own favorites" guard, but reassigning a method parameter makes the data flow harder to follow. Consider introducing a local (e.g., effectiveUserId) and using it consistently below, or — if you'd rather make the policy explicit — throw a BadRequestException / AccessDeniedException instead of silently rewriting input.

♻️ Suggested refactor
-    if (!isUserIdEmpty) {
-      // user can only search his own favorite app
-      if (!Objects.equals(loginUserId, userId)) {
-        userId = loginUserId;
-      }
-    }
+    String effectiveUserId = userId;
+    if (!isUserIdEmpty && !Objects.equals(loginUserId, userId)) {
+      // user can only search his own favorite app
+      effectiveUserId = loginUserId;
+    }
@@
-    if (isAppIdEmpty) {
-      return favoriteRepository.findByUserIdOrderByPositionAscDataChangeCreatedTimeAsc(userId,
-          page);
-    }
+    if (isAppIdEmpty) {
+      return favoriteRepository.findByUserIdOrderByPositionAscDataChangeCreatedTimeAsc(
+          effectiveUserId, page);
+    }
@@
-    return Collections.singletonList(favoriteRepository.findByUserIdAndAppId(userId, appId));
+    return Collections.singletonList(
+        favoriteRepository.findByUserIdAndAppId(effectiveUserId, appId));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/FavoriteService.java`
around lines 73 - 101, The search method silently reassigns the parameter userId
to loginUserId which obscures data flow; introduce a local variable (e.g.,
effectiveUserId) and set it to either the provided userId or, if the caller is
not permitted, to loginUserId (or alternatively throw an AccessDeniedException)
and then use effectiveUserId everywhere instead of mutating the userId
parameter; update all branches that call favoriteRepository
(findByUserIdOrderByPositionAscDataChangeCreatedTimeAsc, findByUserIdAndAppId)
to use effectiveUserId while leaving appId and loginUserId checks intact.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ItemController.java (1)

82-91: 💤 Low value

LGTM — operator threaded through correctly.

Optional nit: createItem and updateItem (lines 104, 121) already extract userInfoHolder.getUser().getUserId() into a local userId/username variable, while modifyItemsByText, update, and revokeItems inline the chain. Extracting a local for consistency would slightly improve readability and make it easier to add logging/auditing later, but it's not blocking.

Also applies to: 209-232, 250-255

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ItemController.java`
around lines 82 - 91, Extract userInfoHolder.getUser().getUserId() into a local
variable in modifyItemsByText, update, and revokeItems (similar to
createItem/updateItem) for consistency and easier future logging; e.g., add a
local String userId = userInfoHolder.getUser().getUserId() at the top of each
method and use userId when calling configService.updateConfigItemByText or other
service methods, and apply the same change in the other occurrences referenced
(around the 209-232 and 250-255 blocks).
apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/AppOpenApiService.java (1)

33-35: 💤 Low value

Two near-identical method names; consider Javadoc to disambiguate.

getEnvClusters and getEnvClusterInfo differ only in their return type and a single word, but they correspond to different OpenAPI endpoints/payloads in v0.2.0. Adding a brief Javadoc on each (purpose, endpoint they back, payload shape) would help callers pick the right one and prevent accidental misuse.

As per coding guidelines: "New Java classes should include a short Javadoc describing the class purpose" — extending the same care to the public methods of changed/expanded interfaces aligns with that intent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/AppOpenApiService.java`
around lines 33 - 35, The two public methods getEnvClusters and
getEnvClusterInfo on AppOpenApiService are easily confused; add concise Javadoc
above each method that states its purpose, the OpenAPI endpoint/version (v0.2.0)
it implements, and the payload/return shape (e.g., returns
List<OpenEnvClusterDTO> vs List<OpenEnvClusterInfo>) so callers can distinguish
them; reference the interface name AppOpenApiService and the DTO types
OpenEnvClusterDTO and OpenEnvClusterInfo in the comments to make intent and
usage explicit.
apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/ConfigServiceTest.java (1)

65-86: 💤 Low value

Consider asserting the operator is propagated to the change set.

These tests now pass "test" as the operator, but neither test verifies that the value actually reaches changeSets.setDataChangeLastModifiedBy(...) or itemAPI.updateItemsByChangeSet(...). A simple ArgumentCaptor<ItemChangeSets> or verify(itemAPI).updateItemsByChangeSet(...) would lock in the new contract and prevent silent regressions if the operator threading is later broken.

Also applies to: 101-123

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/ConfigServiceTest.java`
around lines 65 - 86, In testUpdateConfigByText ensure the "test" operator is
actually propagated to the ItemChangeSets and passed to the update call: capture
the ItemChangeSets passed to itemAPI.updateItemsByChangeSet (use an
ArgumentCaptor<ItemChangeSets>), assert that
captor.getValue().getDataChangeLastModifiedBy() (or
changeSets.getDataChangeLastModifiedBy()) equals "test", and optionally verify
itemAPI.updateItemsByChangeSet(...) was invoked with that captured change set;
reference methods/configService.updateConfigItemByText, ItemChangeSets,
changeSets.setDataChangeLastModifiedBy, and itemAPI.updateItemsByChangeSet when
adding the assertions.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/FavoriteController.java (1)

49-49: 💤 Low value

Consider extracting the user once per request.

userInfoHolder.getUser().getUserId() is called in each method. While correct, extracting it to a local variable would improve efficiency and make it easier to add null-safety checks if needed.

♻️ Optional refactor to extract user once
 `@PostMapping`("/favorites")
 public Favorite addFavorite(`@RequestBody` Favorite favorite) {
-  return favoriteService.addFavorite(favorite, userInfoHolder.getUser().getUserId());
+  String userId = userInfoHolder.getUser().getUserId();
+  return favoriteService.addFavorite(favorite, userId);
 }

Apply similar pattern to other methods if desired.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/FavoriteController.java`
at line 49, In FavoriteController, avoid calling
userInfoHolder.getUser().getUserId() inline for each request; extract the
user/id to a local variable at the start of the controller method (e.g., long
userId = userInfoHolder.getUser().getUserId() or User user =
userInfoHolder.getUser()) and pass that variable into
favoriteService.addFavorite(...) and other service calls, adding a null-safety
check for user (throw or return an appropriate error if null) so the methods use
the cached value instead of repeated calls.
apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerService.java (1)

97-116: ⚡ Quick win

Add a centralized non-blank operator precondition for write paths.

Now that audit attribution is fully caller-supplied, guarding operator/createdBy at service entry points will prevent null/blank audit values from leaking into persistence calls.

Proposed hardening diff
@@
 public class ConsumerService {
@@
+  private void validateOperator(String operator) {
+    Preconditions.checkArgument(!Strings.isNullOrEmpty(operator), "Operator can not be empty");
+  }
+
   public Consumer createConsumer(Consumer consumer, String operator) {
+    validateOperator(operator);
     String appId = consumer.getAppId();
@@
   public ConsumerToken generateAndSaveConsumerToken(Consumer consumer, Integer rateLimit,
       Date expires, String operator) {
     Preconditions.checkArgument(consumer != null, "Consumer can not be null");
+    validateOperator(operator);
@@
   public List<ConsumerRole> assignNamespaceRoleToConsumer(String token, String appId,
       String namespaceName, String env, String operator) {
+    validateOperator(operator);
@@
   public ConsumerRole assignCreateApplicationRoleToConsumer(String token, String operator) {
+    validateOperator(operator);
@@
   public ConsumerRole assignAppRoleToConsumer(Long consumerId, String appId, String operator) {
+    validateOperator(operator);
@@
   private ConsumerToken generateConsumerToken(Consumer consumer, Integer rateLimit, Date expires,
       String createdBy) {
+    validateOperator(createdBy);

Also applies to: 118-126, 278-328, 341-357

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerService.java`
around lines 97 - 116, Add a centralized precondition that the caller-supplied
operator is non-null and non-blank at service write entry points: check the
operator parameter at the start of methods like ConsumerService.createConsumer
(and the other write methods called out: the ranges covering consumer updates
and other create/update/delete paths) and throw a BadRequestException (or
appropriate validation exception) immediately if operator is null/blank; do this
before any audit fields are set (e.g., before
setDataChangeCreatedBy/setDataChangeLastModifiedBy) so no null/blank operator
values reach persistence, and factor the check into a small private helper
(e.g., validateOperatorOrThrow) to reuse across those methods.
apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AppController.java (1)

129-130: ⚡ Quick win

Consider extracting operator once per method.

Lines 129-130, 143, and 190 call userInfoHolder.getUser().getUserId() inline, while lines 167-171 extract the operator into a local variable first. The local variable approach (used in the create(@PathVariable String env, ...) method) is more efficient and safer if the operator is used multiple times or passed to multiple service calls.

♻️ Consistent operator extraction pattern
 `@PostMapping`
 `@ApolloAuditLog`(type = OpType.CREATE, name = "App.create")
 public App create(`@Valid` `@RequestBody` AppModel appModel) {
-
+  String operator = userInfoHolder.getUser().getUserId();
   App app = transformToApp(appModel);
-  return appService.createAppAndAddRolePermission(app, appModel.getAdmins(),
-      userInfoHolder.getUser().getUserId());
+  return appService.createAppAndAddRolePermission(app, appModel.getAdmins(), operator);
 }

Apply the same pattern to update() and deleteApp().

Also applies to: 143-143, 190-190

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AppController.java`
around lines 129 - 130, In update() and deleteApp() replace repeated inline
calls to userInfoHolder.getUser().getUserId() with a single local variable
(e.g., String operator = userInfoHolder.getUser().getUserId()) at the top of
each method and pass that operator to service calls such as
appService.createAppAndAddRolePermission(...) and other appService methods;
locate usages in update(), deleteApp(), and the call sites to ensure all
occurrences use the new operator variable consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerAppOpenApiService.java`:
- Around line 125-130: The handler in ServerAppOpenApiService is putting raw
exception text (e.getMessage()) into the OpenEnvClusterInfo response (see
OpenEnvClusterInfo and calls to info.setMessage), which must be removed; instead
log the full exception server-side (logger.warn(..., e)) and set a stable
generic client message such as "Internal server error while loading environment"
(or similar) in info.setMessage; apply the same change to the other occurrence
around the OpenEnvClusterInfo creation at the 233-242 region so no exception
details are returned to callers.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerNamespaceOpenApiService.java`:
- Around line 76-80: In createAppNamespace(OpenAppNamespaceDTO), validate
appNamespaceDTO.getDataChangeCreatedBy() is non-null and non-blank before
calling appNamespaceService.createAppNamespaceInLocal: if the creator is
null/blank, throw an appropriate bad-request/illegal-argument exception (e.g.,
IllegalArgumentException or a framework BadRequestException) so the write is
rejected early; perform this check at the top of createAppNamespace, referencing
appNamespaceDTO.getDataChangeCreatedBy() and the createAppNamespaceInLocal call
to ensure no unaudited write is attempted.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/AppController.java`:
- Around line 83-89: The controller currently only rejects null appId but allows
empty or whitespace strings to reach downstream methods like createApp and
assignAppRoleToConsumer; update the null check in AppController (where
req.getApp() and OpenAppDTO app are validated) to reject blank appId values by
using StringUtils.hasText(app.getAppId()) (or equivalent trim-and-empty check)
and throw BadRequestException("AppId is null or blank") when the check fails so
empty/whitespace appId is rejected at the controller boundary.
- Around line 176-179: createAppInEnv currently resolves the operator but does
not stamp audit fields on the OpenAppDTO; before calling
appOpenApiService.createAppInEnv(env, app, resolvedOperator) set
app.setDataChangeCreatedBy(resolvedOperator) and
app.setDataChangeLastModifiedBy(resolvedOperator) (same pattern used in
createApp and updateApp) so the DTO contains the normalized audit metadata prior
to delegation.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppService.java`:
- Around line 127-138: The createAppInRemote method currently accepts a
blank/null operator and uses it for audit fields and role initialization; add a
guard at the start of createAppInRemote to reject blank operators (e.g.,
StringUtils.isBlank(operator)) and throw an appropriate unchecked exception
(IllegalArgumentException or a custom BadRequestException) so we fail fast
before mutating app or calling appAPI.createApp and
roleInitializationService.initClusterNamespaceRoles; apply the same pattern to
the other affected methods mentioned (the other create/update/delete methods
that set dataChangeCreatedBy/dataChangeLastModifiedBy and call
roleInitializationService) to ensure no write happens with a blank operator.

---

Outside diff comments:
In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterController.java`:
- Around line 79-81: Remove the redundant user existence check in
ClusterController: delete the block that calls
userService.findByUserId(operator) and throws
BadRequestException.userNotExists(operator), since resolveOperator already
performs that validation; keep resolveOperator as the single source of truth for
operator validation to avoid duplicated checks.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ReleaseService.java`:
- Around line 57-72: ServerReleaseOpenApiService.publishNamespace() must
validate the releasedBy field before transforming the DTO into a
NamespaceReleaseModel; add a null/empty check on
NamespaceReleaseDTO.getReleasedBy() (or the incoming parameter) at the start of
publishNamespace(), and throw an appropriate exception (e.g.,
IllegalArgumentException or a BadRequest/BadParam exception used in this
codebase) if releasedBy is blank so BeanUtils.transform() cannot produce a model
with a null/empty releasedBy; reference the method
ServerReleaseOpenApiService.publishNamespace(), the DTO class
NamespaceReleaseDTO, the model NamespaceReleaseModel and the
BeanUtils.transform() call when applying the change.

---

Nitpick comments:
In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/AppOpenApiService.java`:
- Around line 33-35: The two public methods getEnvClusters and getEnvClusterInfo
on AppOpenApiService are easily confused; add concise Javadoc above each method
that states its purpose, the OpenAPI endpoint/version (v0.2.0) it implements,
and the payload/return shape (e.g., returns List<OpenEnvClusterDTO> vs
List<OpenEnvClusterInfo>) so callers can distinguish them; reference the
interface name AppOpenApiService and the DTO types OpenEnvClusterDTO and
OpenEnvClusterInfo in the comments to make intent and usage explicit.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerService.java`:
- Around line 97-116: Add a centralized precondition that the caller-supplied
operator is non-null and non-blank at service write entry points: check the
operator parameter at the start of methods like ConsumerService.createConsumer
(and the other write methods called out: the ranges covering consumer updates
and other create/update/delete paths) and throw a BadRequestException (or
appropriate validation exception) immediately if operator is null/blank; do this
before any audit fields are set (e.g., before
setDataChangeCreatedBy/setDataChangeLastModifiedBy) so no null/blank operator
values reach persistence, and factor the check into a small private helper
(e.g., validateOperatorOrThrow) to reuse across those methods.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AppController.java`:
- Around line 129-130: In update() and deleteApp() replace repeated inline calls
to userInfoHolder.getUser().getUserId() with a single local variable (e.g.,
String operator = userInfoHolder.getUser().getUserId()) at the top of each
method and pass that operator to service calls such as
appService.createAppAndAddRolePermission(...) and other appService methods;
locate usages in update(), deleteApp(), and the call sites to ensure all
occurrences use the new operator variable consistently.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/FavoriteController.java`:
- Line 49: In FavoriteController, avoid calling
userInfoHolder.getUser().getUserId() inline for each request; extract the
user/id to a local variable at the start of the controller method (e.g., long
userId = userInfoHolder.getUser().getUserId() or User user =
userInfoHolder.getUser()) and pass that variable into
favoriteService.addFavorite(...) and other service calls, adding a null-safety
check for user (throw or return an appropriate error if null) so the methods use
the cached value instead of repeated calls.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ItemController.java`:
- Around line 82-91: Extract userInfoHolder.getUser().getUserId() into a local
variable in modifyItemsByText, update, and revokeItems (similar to
createItem/updateItem) for consistency and easier future logging; e.g., add a
local String userId = userInfoHolder.getUser().getUserId() at the top of each
method and use userId when calling configService.updateConfigItemByText or other
service methods, and apply the same change in the other occurrences referenced
(around the 209-232 and 250-255 blocks).

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/FavoriteService.java`:
- Around line 73-101: The search method silently reassigns the parameter userId
to loginUserId which obscures data flow; introduce a local variable (e.g.,
effectiveUserId) and set it to either the provided userId or, if the caller is
not permitted, to loginUserId (or alternatively throw an AccessDeniedException)
and then use effectiveUserId everywhere instead of mutating the userId
parameter; update all branches that call favoriteRepository
(findByUserIdOrderByPositionAscDataChangeCreatedTimeAsc, findByUserIdAndAppId)
to use effectiveUserId while leaving appId and loginUserId checks intact.

In
`@apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/ConfigServiceTest.java`:
- Around line 65-86: In testUpdateConfigByText ensure the "test" operator is
actually propagated to the ItemChangeSets and passed to the update call: capture
the ItemChangeSets passed to itemAPI.updateItemsByChangeSet (use an
ArgumentCaptor<ItemChangeSets>), assert that
captor.getValue().getDataChangeLastModifiedBy() (or
changeSets.getDataChangeLastModifiedBy()) equals "test", and optionally verify
itemAPI.updateItemsByChangeSet(...) was invoked with that captured change set;
reference methods/configService.updateConfigItemByText, ItemChangeSets,
changeSets.setDataChangeLastModifiedBy, and itemAPI.updateItemsByChangeSet when
adding the assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ac29553a-320a-482f-bb85-056107a33d14

📥 Commits

Reviewing files that changed from the base of the PR and between 177a9dc and f511523.

📒 Files selected for processing (53)
  • CHANGES.md
  • apollo-portal/pom.xml
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/AppOpenApiService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ClusterOpenApiService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerAppOpenApiService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerClusterOpenApiService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerNamespaceOpenApiService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/util/OpenApiModelConverters.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/AppController.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterController.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/AppController.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ClusterController.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConfigsImportController.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ConsumerController.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/FavoriteController.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ItemController.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceBranchController.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/NamespaceController.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ReleaseController.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ServerConfigController.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppNamespaceService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ClusterService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ConfigsImportService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/FavoriteService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ItemService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/NamespaceBranchService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/NamespaceService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ReleaseService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ServerConfigService.java
  • apollo-portal/src/main/resources/static/scripts/services/AppService.js
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/server/service/ServerAppOpenApiServiceTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/service/ConsumerServiceTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerParamBindLowLevelTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterControllerParamBindLowLevelTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterControllerTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/ServiceExceptionTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ClusterControllerTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ConfigsImportControllerTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ConsumerControllerTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ServerConfigControllerTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/AppNamespaceServiceTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/AppServiceTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/ConfigServiceTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/ConfigsExportServiceTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/FavoriteServiceTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/NamespaceServiceTest.java
  • docs/en/contribution/apollo-portal-openapi-frontend-url-inventory.md
  • docs/en/contribution/apollo-portal-openapi-migration-tracking.md
  • docs/zh/contribution/apollo-portal-openapi-frontend-url-inventory.md
  • docs/zh/contribution/apollo-portal-openapi-migration-tracking.md

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f5115238b8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apollo-portal/pom.xml
<name>Apollo Portal</name>
<properties>
<apollo.openapi.spec.url>https://raw.githubusercontent.com/apolloconfig/apollo-openapi/refs/tags/v0.1.0/apollo-openapi.yaml</apollo.openapi.spec.url>
<apollo.openapi.spec.url>https://raw.githubusercontent.com/apolloconfig/apollo-openapi/refs/tags/v0.2.0/apollo-openapi.yaml</apollo.openapi.spec.url>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Adapt item endpoints to the v0.2 operator contract

Switching the generated contract to v0.2.0 also changes the item mutation endpoints to accept operator as a query parameter, but the existing /openapi/v1/.../items controller is still the hand-written DTO implementation and is not wired to the generated ItemManagementApi (checked with rg "ItemManagementApi"). In this state, clients generated from v0.2.0 can call update/create/delete with ?operator=... as documented, but the runtime controller ignores it and still rejects requests unless dataChangeCreatedBy/dataChangeLastModifiedBy is duplicated in the body, so item mutations through the new contract fail or audit the wrong field. Please update the item controller/service path to the generated v0.2 interface before advertising this spec.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch. This is a valid Item-domain gap, but we are intentionally deferring it to the next migration slice instead of expanding this App-focused PR. The follow-up should migrate the OpenAPI item controller/service path to the generated v0.2 ItemManagementApi contract, including the operator query-parameter behavior for item mutations.

Comment on lines +94 to +96
if (Boolean.TRUE.equals(req.getAssignAppRoleToSelf())) {
long consumerId = this.consumerAuthUtil.retrieveConsumerIdFromCtx();
consumerService.assignAppRoleToConsumer(consumerId, app.getAppId());
consumerService.assignAppRoleToConsumer(consumerId, app.getAppId(), resolvedOperator);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Guard self-role assignment to consumer-auth requests

When a Portal USER calls POST /openapi/v1/apps with assignAppRoleToSelf=true, the new USER-aware operator resolution succeeds, but this block still unconditionally reads a consumer id from the request; Portal-session requests do not have the ApolloConsumerId attribute, so retrieveConsumerIdFromCtx() throws and the app creation path returns a server error after creating the app. Please either ignore/reject this flag for USER auth or only retrieve the consumer id when UserIdentityContextHolder is CONSUMER.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in abfd47a. assignAppRoleToSelf now only retrieves and assigns a consumer role when the current auth type is CONSUMER; Portal USER requests no longer depend on ApolloConsumerId. Added coverage in AppControllerParamBindLowLevelTest.

Comment thread apollo-portal/pom.xml
<name>Apollo Portal</name>
<properties>
<apollo.openapi.spec.url>https://raw.githubusercontent.com/apolloconfig/apollo-openapi/refs/tags/v0.1.0/apollo-openapi.yaml</apollo.openapi.spec.url>
<apollo.openapi.spec.url>https://raw.githubusercontent.com/apolloconfig/apollo-openapi/refs/tags/v0.2.0/apollo-openapi.yaml</apollo.openapi.spec.url>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Implement the v0.2 batch text item endpoint

The v0.2 spec now advertises PUT /openapi/v1/envs/{env}/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/items (batchUpdateItemsByText), but the runtime OpenAPI item controller still only has PUT .../items/{key} and is not implementing the generated item interface (verified with rg "ItemManagementApi" and the mappings under openapi/v1/controller/ItemController.java). Generated v0.2 clients using the text-mode batch update operation will therefore get a 404 even though the POM now publishes that contract; please add the OpenAPI mapping or keep the spec pinned until this operation exists.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Agreed, this endpoint still needs a runtime implementation before Item v0.2 coverage can be considered complete. We are leaving it for the next Item-domain migration PR, where batchUpdateItemsByText should be added through the generated ItemManagementApi path rather than folded into this App compatibility patch.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/server/service/ServerReleaseOpenApiServiceTest.java (1)

47-56: ⚡ Quick win

Add a test for null NamespaceReleaseDTO path

publishNamespace now guards both blank releasedBy and null DTO, but only the blank branch is asserted here. Add one null-input test to lock in that behavior.

Suggested test addition
+  `@Test`
+  void publishNamespaceShouldRejectNullDto() {
+    assertThrows(BadRequestException.class,
+        () -> service.publishNamespace("appId", "DEV", "default", "application", null));
+
+    verify(releaseService, never()).publish(any(NamespaceReleaseModel.class));
+  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/server/service/ServerReleaseOpenApiServiceTest.java`
around lines 47 - 56, Add a new unit test that calls
service.publishNamespace("appId", "DEV", "default", "application", null) and
asserts it throws BadRequestException; name it e.g.
publishNamespaceShouldRejectNullDto. After asserting the exception,
verify(releaseService, never()).publish(any(NamespaceReleaseModel.class)) to
ensure publish is not invoked. This mirrors the existing
publishNamespaceShouldRejectBlankReleasedBy test and locks in the null-DTO guard
in publishNamespace.
apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerParamBindLowLevelTest.java (1)

66-69: 💤 Low value

Consider migrating to JUnit 5 for new test methods.

While the existing JUnit 4 tests are supported via Vintage, new test methods could use JUnit 5 annotations (@BeforeEach, @AfterEach, @ExtendWith(SpringExtension.class)) to align with the project's default testing framework guideline. This would be a gradual migration path.

Example JUnit 5 migration pattern

For new tests, you could use:

`@ExtendWith`(SpringExtension.class)
`@SpringBootTest`
`@AutoConfigureMockMvc`(addFilters = false)
public class AppControllerParamBindLowLevelTest {
  
  `@BeforeEach`  // instead of `@Before`
  public void setUp() { ... }
  
  `@BeforeEach`  // instead of second `@Before`
  public void setAuthentication() { ... }
  
  `@AfterEach`  // instead of `@After`
  public void clearAuthentication() { ... }
}

As per coding guidelines, Use JUnit 5 as the default testing framework with Vintage enabled for legacy JUnit 4 tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerParamBindLowLevelTest.java`
around lines 66 - 69, The test class AppControllerParamBindLowLevelTest
currently uses JUnit 4 (`@RunWith`(SpringRunner.class)) while the project standard
is JUnit 5; update the class to use JUnit 5 by replacing
`@RunWith`(SpringRunner.class) with `@ExtendWith`(SpringExtension.class), and
convert lifecycle methods from `@Before/`@After to `@BeforeEach/`@AfterEach (or
`@BeforeAll/`@AfterAll if appropriate) while keeping `@SpringBootTest` and
`@AutoConfigureMockMvc`(addFilters = false); ensure imports are updated to JUnit 5
counterparts and that any JUnit 4-specific constructs are adapted or left under
Vintage only for legacy tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerReleaseOpenApiService.java`:
- Around line 46-48: The current single guard in ServerReleaseOpenApiService
conflates a null releaseDTO and a blank releasedBy; change it to two checks:
first if (releaseDTO == null) throw new BadRequestException("Release payload can
not be null."); then if (StringUtils.isBlank(releaseDTO.getReleasedBy())) throw
new BadRequestException("Params(releasedBy) can not be empty."); reference the
releaseDTO variable and its getReleasedBy() call to locate and split the
validation.

---

Nitpick comments:
In
`@apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/server/service/ServerReleaseOpenApiServiceTest.java`:
- Around line 47-56: Add a new unit test that calls
service.publishNamespace("appId", "DEV", "default", "application", null) and
asserts it throws BadRequestException; name it e.g.
publishNamespaceShouldRejectNullDto. After asserting the exception,
verify(releaseService, never()).publish(any(NamespaceReleaseModel.class)) to
ensure publish is not invoked. This mirrors the existing
publishNamespaceShouldRejectBlankReleasedBy test and locks in the null-DTO guard
in publishNamespace.

In
`@apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerParamBindLowLevelTest.java`:
- Around line 66-69: The test class AppControllerParamBindLowLevelTest currently
uses JUnit 4 (`@RunWith`(SpringRunner.class)) while the project standard is JUnit
5; update the class to use JUnit 5 by replacing `@RunWith`(SpringRunner.class)
with `@ExtendWith`(SpringExtension.class), and convert lifecycle methods from
`@Before/`@After to `@BeforeEach/`@AfterEach (or `@BeforeAll/`@AfterAll if
appropriate) while keeping `@SpringBootTest` and `@AutoConfigureMockMvc`(addFilters
= false); ensure imports are updated to JUnit 5 counterparts and that any JUnit
4-specific constructs are adapted or left under Vintage only for legacy tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8839a217-4d4f-4286-a87f-ffd755aa8a99

📥 Commits

Reviewing files that changed from the base of the PR and between f511523 and abfd47a.

📒 Files selected for processing (14)
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/AppOpenApiService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerAppOpenApiService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerNamespaceOpenApiService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerReleaseOpenApiService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/AppController.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterController.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppService.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/server/service/ServerAppOpenApiServiceTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/server/service/ServerNamespaceOpenApiServiceTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/server/service/ServerReleaseOpenApiServiceTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/service/ConsumerServiceTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/v1/controller/AppControllerParamBindLowLevelTest.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/AppServiceTest.java
🚧 Files skipped from review as they are similar to previous changes (10)
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerNamespaceOpenApiService.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/server/service/ServerAppOpenApiServiceTest.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/AppService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ClusterController.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/openapi/service/ConsumerServiceTest.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/service/ConsumerService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/AppController.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerAppOpenApiService.java
  • apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/AppOpenApiService.java
  • apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/service/AppServiceTest.java

Comment on lines +46 to +48
if (releaseDTO == null || StringUtils.isBlank(releaseDTO.getReleasedBy())) {
throw new BadRequestException("Params(releasedBy) can not be empty.");
}
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Split validation errors for null payload vs blank releasedBy

The guard is good, but returning the same message for both cases makes client-side troubleshooting harder. Please return a specific message for a null body and another for blank releasedBy.

Proposed patch
-    if (releaseDTO == null || StringUtils.isBlank(releaseDTO.getReleasedBy())) {
-      throw new BadRequestException("Params(releasedBy) can not be empty.");
-    }
+    if (releaseDTO == null) {
+      throw new BadRequestException("Request body can not be empty.");
+    }
+    if (StringUtils.isBlank(releaseDTO.getReleasedBy())) {
+      throw new BadRequestException("Param(releasedBy) can not be empty.");
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (releaseDTO == null || StringUtils.isBlank(releaseDTO.getReleasedBy())) {
throw new BadRequestException("Params(releasedBy) can not be empty.");
}
if (releaseDTO == null) {
throw new BadRequestException("Request body can not be empty.");
}
if (StringUtils.isBlank(releaseDTO.getReleasedBy())) {
throw new BadRequestException("Param(releasedBy) can not be empty.");
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/server/service/ServerReleaseOpenApiService.java`
around lines 46 - 48, The current single guard in ServerReleaseOpenApiService
conflates a null releaseDTO and a blank releasedBy; change it to two checks:
first if (releaseDTO == null) throw new BadRequestException("Release payload can
not be null."); then if (StringUtils.isBlank(releaseDTO.getReleasedBy())) throw
new BadRequestException("Params(releasedBy) can not be empty."); reference the
releaseDTO variable and its getReleasedBy() call to locate and split the
validation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants