Skip to content

Conversation

@Induwara04
Copy link
Contributor

@Induwara04 Induwara04 commented Dec 12, 2025

This pull request adds new API uniqueness validation logic to the management portal, enabling checks for both API name+version and identifier uniqueness. It introduces new types, hooks, and context state to support these validations, and refactors the context provider to expose the new state and actions. Additionally, it includes some minor code style and formatting improvements in the API overview page.

Issue: #388
Screenshot 2025-12-12 at 13 40 48

API Uniqueness Validation Support

  • Added new types (ApiUniquenessValidationResponse, ApiNameVersionValidationRequest, etc.) to represent API uniqueness validation requests and responses in validation.ts.
  • Implemented useApiUniquenessValidation hook to validate API name+version and identifier uniqueness via backend endpoints.

Validation Context Enhancements

  • Extended GithubProjectValidationContextValue and the GithubProjectValidationProvider to include state, actions, and helpers for API name+version and identifier uniqueness validation. This includes loading/error/result state, validation functions, and derived booleans for uniqueness. [1] [2] [3] [4] [5]
  • Improved type safety and error handling in the context provider, including type guards and discriminant narrowing for validation results. [1] [2]

Summary by CodeRabbit

  • New Features

    • Live, debounced uniqueness checks for API name/version and identifier; creation metadata reports validation state to parent flows.
    • Auto-generation of API identifier from name+version with an editable identifier workflow and inline validation feedback.
  • Bug Fixes & Improvements

    • Create actions are disabled when metadata validation errors exist; validation state now consistently blocks creation across flows.
    • Import flow accepts/displayName for improved previews and prefill.
  • UI/UX Improvements

    • API overview header shows initials; various layout and copy refinements.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 12, 2025

Walkthrough

Adds API name/version and identifier uniqueness validation: new hook, extended validation context, debounced remote checks in CreationMetaData, and creation-flow gating and payload adjustments to use identifier/displayName.

Changes

Cohort / File(s) Summary
Validation Context & Hook
portals/management-portal/src/context/validationContext.tsx, portals/management-portal/src/hooks/validation.ts
Added useApiUniquenessValidation and types (ApiUniquenessValidationResponse, ApiNameVersionValidationRequest, ApiValidateError); extended context with name/version and identifier loading/error/result state, validateNameVersion/validateIdentifier methods, uniqueness flags, and discriminant error narrowing; integrated API calls and AbortController handling.
CreationMetaData component
portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx
Added identifier auto-generation/editing, debounced remote validation for name/version and identifier using the validation context, schedule/flush helpers, lifecycle cleanup, inline error display, and new onValidationChange prop reporting { nameVersionError, identifierError, hasError }.
Creation flow integration
portals/management-portal/src/pages/apis/CreationFlows/.../GithubCreationFlow.tsx, .../URLCreationFlow.tsx, .../UploadCreationFlow.tsx, .../EndPointCreationFlow.tsx
Introduced metaHasErrors state and wired onValidationChange from CreationMetaData; creation gating now includes metaHasErrors; auto-fill and payloads updated to include identifier and displayName; creation payload structures adjusted and resets refined.
Import/OpenAPI hook type
portals/management-portal/src/hooks/apis.ts
ImportOpenApiRequest.api now optionally accepts displayName?: string and import payloads updated to include displayName where used.
API Overview UI
portals/management-portal/src/pages/apis/ApiOverview.tsx
UI/styling refactor: initials helper for header badge, promise-based clipboard handling, and JSX/layout/style adjustments; no data-flow/API changes.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CreationMetaData
    participant DebounceTimer
    participant ValidationContext
    participant UniquenessHook
    participant API

    User->>CreationMetaData: edit name/version or identifier
    CreationMetaData->>DebounceTimer: schedule validation (debounce)
    DebounceTimer-->>CreationMetaData: trigger validation
    CreationMetaData->>ValidationContext: call validateNameVersion/validateIdentifier
    ValidationContext->>UniquenessHook: invoke validateApiNameVersion/validateApiIdentifier
    UniquenessHook->>API: GET /api/v1/apis/validate?...
    API-->>UniquenessHook: ApiUniquenessValidationResponse
    UniquenessHook-->>ValidationContext: return result
    ValidationContext-->>CreationMetaData: update loading/result/error
    CreationMetaData->>CreationMetaData: display errors and call onValidationChange
    CreationMetaData->>User: enable/disable Create based on hasError
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Areas needing extra attention:
    • Debounce logic, timers, and cleanup in CreationMetaData.tsx
    • AbortController and race-condition handling in validationContext.tsx
    • Discriminant type-narrowing and error extraction in the validation flow
    • Propagation and gating via onValidationChange / metaHasErrors across creation flows

Possibly related PRs

Suggested reviewers

  • thivindu
  • Krishanx92

Poem

🐰 I hopped through slugs and names with care,
Debounced my checks in the crisp spring air.
IDs found true, versions passed the test,
I nudged the flows, then took a rest. 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete relative to the template, missing critical sections like Goals, Approach, User stories, Documentation, Automation tests, Security checks, and Test environment. Add missing template sections: Goals, Approach (with UI screenshots/details), User stories, Documentation, Automation tests (unit and integration), Security checks, and Test environment details.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change—adding API name, identifier, and version validation functionality to the system.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
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)
portals/management-portal/src/pages/apis/CreationFlows/EndPointCreationFlow.tsx (1)

52-62: Reset metaHasErrors when wizard opens.

Similar to other flows, metaHasErrors should be reset in the useEffect that handles the open state change to prevent stale validation errors.

   React.useEffect(() => {
     if (open) {
       resetEndpointMeta();
       setWizardStep("endpoint");
       setWizardState({ endpointUrl: "" });
       setWizardError(null);
+      setMetaHasErrors(false);
     }
   }, [open, resetEndpointMeta]);
portals/management-portal/src/pages/apis/ApiOverview.tsx (1)

681-688: Avoid blank header when api.displayName is missing.

Title uses api.displayName but the initials are computed from api.name; if displayName is optional, consider api.displayName ?? api.name.

🧹 Nitpick comments (10)
portals/management-portal/src/hooks/validation.ts (1)

188-240: Consider early return for empty inputs.

The validation functions will make network requests even when name, version, or identifier are empty strings. Consider adding early validation to avoid unnecessary API calls.

   const validateApiNameVersion = useCallback(
     async (
       payload: ApiNameVersionValidationRequest,
       opts?: { signal?: AbortSignal }
     ): Promise<ApiUniquenessValidationResponse> => {
+      if (!payload.name.trim() || !payload.version.trim()) {
+        return { valid: true, error: null };
+      }
+
       const qs = new URLSearchParams({
         name: payload.name,
         version: payload.version,
       }).toString();
   const validateApiIdentifier = useCallback(
     async (
       identifier: string,
       opts?: { signal?: AbortSignal }
     ): Promise<ApiUniquenessValidationResponse> => {
+      if (!identifier.trim()) {
+        return { valid: true, error: null };
+      }
+
       const qs = new URLSearchParams({ identifier }).toString();
portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/GithubCreationFlow.tsx (2)

374-384: Consider including metaHasErrors in canCreate for consistency.

The canCreate computed value validates most conditions but excludes metaHasErrors, which is then added separately in the button's disabled prop. Including it in canCreate would make the logic more cohesive.

   const canCreate =
     step === "details" &&
     !validating &&
     !ghLoading &&
     !!selectedProjectId &&
     !!repoUrl?.trim() &&
     !!selectedBranch &&
     !!isDirValid &&
     !!(contractMeta?.name || "").trim() &&
     !!(contractMeta?.context || "").trim() &&
-    isValidMajorMinorVersion((contractMeta?.version || "").trim());
+    isValidMajorMinorVersion((contractMeta?.version || "").trim()) &&
+    !metaHasErrors;

Then simplify the button:

-                    disabled={!canCreate || metaHasErrors || creating}
+                    disabled={!canCreate || creating}

Also applies to: 631-637


602-603: Consider removing commented-out code.

The commented-out Card/CardContent wrappers appear to be leftover scaffolding. If this layout change is intentional, these comments should be removed.

Also applies to: 639-640

portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx (1)

382-382: Consider removing commented-out code.

The commented-out Paper wrappers appear to be leftover scaffolding. If this layout change is intentional, these comments should be removed for cleaner code.

Also applies to: 426-426

portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx (2)

360-360: Avoid as any casts on event handlers.

The as any casts on onBlur suggest a type mismatch between what TextInput expects and what's being passed. This bypasses TypeScript's type checking.

Consider properly typing the handlers or wrapping them:

- onBlur={(() => flushNameVersionValidation({ force: true })) as any}
+ onBlur={() => flushNameVersionValidation({ force: true })}
- onBlur={(() => flushIdentifierValidation()) as any}
+ onBlur={() => flushIdentifierValidation()}

If the TextInput component expects a specific signature like (e: FocusEvent) => void, update the wrappers accordingly or fix the component's prop types.

Also applies to: 378-378


98-98: Consider moving constant outside component.

debounceMs is a constant that never changes. Defining it inside the component means it's included in useCallback dependency arrays unnecessarily and could cause confusion during code review.

+ const DEBOUNCE_MS = 2000;
+
  const CreationMetaData: React.FC<Props> = ({
    ...
  }) => {
-   const debounceMs = 2000;

Then update references to use DEBOUNCE_MS and remove it from dependency arrays.

portals/management-portal/src/context/validationContext.tsx (1)

174-222: Consider adding AbortController support to prevent race conditions.

Unlike validate() which creates an AbortController, validateNameVersion and validateIdentifier don't support request cancellation. Given that CreationMetaData.tsx uses debounced validation, rapid user input could trigger multiple in-flight requests, and earlier responses arriving after later ones could overwrite correct results.

The underlying hooks already support the signal option. Consider either:

  1. Creating an AbortController per call and exposing abort capability
  2. Using a request counter/token to ignore stale responses:
  const validateNameVersion = useCallback(
    async (payload: ApiNameVersionValidationRequest) => {
+     const ac = new AbortController();
      setNameVersionLoading(true);
      setNameVersionError(null);

      try {
-       const res = await validateApiNameVersion(payload);
+       const res = await validateApiNameVersion(payload, { signal: ac.signal });
        if (mountedRef.current) setNameVersionResult(res);
        return res;
      } catch (e) {
+       if (e instanceof DOMException && e.name === 'AbortError') {
+         throw e; // Let caller handle abort
+       }
        // ... rest of error handling
      }
    },
    [validateApiNameVersion]
  );
portals/management-portal/src/pages/apis/ApiOverview.tsx (3)

54-60: Harden URL construction (slash-joining + avoid brittle assumptions about ports/vhost format).

Current string concat can yield double slashes (e.g., context ending with /) and hard-codes :8080 / :5443 (may be environment-specific). Consider normalizing the path and/or deriving ports from config.

-const httpUrl =
-  vhost && api ? `http://${vhost}:8080${api.context}/${api.version}` : null;
-const httpsUrl =
-  vhost && api ? `https://${vhost}:5443${api.context}/${api.version}` : null;
+const joinPath = (...parts: string[]) =>
+  "/" + parts.map((p) => p.replace(/^\/+|\/+$/g, "")).filter(Boolean).join("/");
+const path = joinPath(api.context, api.version);
+const httpUrl = vhost ? `http://${vhost}:8080${path}` : null;
+const httpsUrl = vhost ? `https://${vhost}:5443${path}` : null;

67-75: Prevent stale setTimeout updates and “Copied!” flicker on repeated clicks.

Multiple clicks schedule multiple timers; also timers can fire after unmount. Consider cancelling the previous timer and cleaning up on unmount (via useRef + useEffect cleanup) in GatewayListItem.

Also applies to: 191-405


570-574: Initials helper: consider non-ASCII letters (i18n) and empty displayName fallback alignment.

name.replace(/[^A-Za-z]/g, "") drops non-Latin characters; for i18n, consider Unicode property escapes (if your TS/target supports it), e.g. /[^\p{L}]/gu.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3facb86 and 677ed24.

📒 Files selected for processing (8)
  • portals/management-portal/src/context/validationContext.tsx (8 hunks)
  • portals/management-portal/src/hooks/validation.ts (7 hunks)
  • portals/management-portal/src/pages/apis/ApiOverview.tsx (18 hunks)
  • portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/GithubCreationFlow.tsx (9 hunks)
  • portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/URLCreationFlow.tsx (3 hunks)
  • portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx (11 hunks)
  • portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx (7 hunks)
  • portals/management-portal/src/pages/apis/CreationFlows/EndPointCreationFlow.tsx (6 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-02T07:06:41.830Z
Learnt from: piyumaldk
Repo: wso2/api-platform PR: 223
File: portals/management-portal/src/common/VersionInput.tsx:23-31
Timestamp: 2025-12-02T07:06:41.830Z
Learning: In portals/management-portal/src/common/VersionInput.tsx, the touchedRef pattern is intentionally used for one-time initialization. Once the component loads, only user modifications should update the value, and external prop changes are deliberately ignored to prevent disrupting user input.

Applied to files:

  • portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx
  • portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx
📚 Learning: 2025-11-18T10:07:26.115Z
Learnt from: thivindu
Repo: wso2/api-platform PR: 160
File: platform-api/src/internal/utils/api.go:1403-1434
Timestamp: 2025-11-18T10:07:26.115Z
Learning: In the wso2/api-platform repository's OpenAPI validation endpoint response (ValidateOpenAPIRequest/OpenAPIValidationResponse), backend service names are not required in the API DTO because the frontend only displays the backend URLs if they exist. The Name field can be left empty when parsing OpenAPI/Swagger definitions.

Applied to files:

  • portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx
🧬 Code graph analysis (4)
portals/management-portal/src/pages/apis/ApiOverview.tsx (2)
portals/management-portal/src/context/ApiContext.tsx (1)
  • useApisContext (340-346)
portals/management-portal/src/hooks/apis.ts (2)
  • ApiSummary (28-45)
  • ApiGatewaySummary (87-106)
portals/management-portal/src/pages/apis/CreationFlows/EndPointCreationFlow.tsx (1)
portals/management-portal/src/context/CreateComponentBuildpackContext.tsx (1)
  • useCreateComponentBuildpackContext (92-100)
portals/management-portal/src/context/validationContext.tsx (1)
portals/management-portal/src/hooks/validation.ts (6)
  • GithubProjectValidationResponse (29-31)
  • GithubProjectValidationRequest (8-13)
  • ApiUniquenessValidationResponse (54-57)
  • ApiNameVersionValidationRequest (59-62)
  • useGithubProjectValidation (100-125)
  • useApiUniquenessValidation (188-241)
portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx (5)
portals/management-portal/src/hooks/apis.ts (2)
  • ImportOpenApiRequest (72-84)
  • ApiSummary (28-45)
portals/management-portal/src/hooks/validation.ts (2)
  • OpenApiValidationResponse (43-45)
  • useOpenApiValidation (127-185)
portals/management-portal/src/context/CreateComponentBuildpackContext.tsx (1)
  • useCreateComponentBuildpackContext (92-100)
portals/management-portal/src/helpers/openApiHelpers.ts (4)
  • formatVersionToMajorMinor (60-81)
  • firstServerUrl (12-16)
  • deriveContext (18-20)
  • isValidMajorMinorVersion (88-91)
portals/management-portal/src/components/src/components/Common/ApiOperationsList.tsx (1)
  • ApiOperationsList (58-108)
🔇 Additional comments (14)
portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/URLCreationFlow.tsx (1)

300-304: LGTM!

The validation change handler is correctly wired to propagate validation state from CreationMetaData, and the Create button is properly gated on metaHasErrors along with other required field checks.

Also applies to: 318-333

portals/management-portal/src/pages/apis/CreationFlows/EndPointCreationFlow.tsx (1)

221-225: LGTM!

The validation change handler and Create button gating logic are correctly implemented, consistent with the other creation flows.

Also applies to: 256-270

portals/management-portal/src/hooks/validation.ts (2)

47-62: LGTM!

The new types for API uniqueness validation are well-structured. The ApiUniquenessValidationResponse correctly models the backend response with a discriminated valid boolean and optional error object.


119-119: LGTM!

The refactoring to return JSON directly instead of assigning to intermediate variables improves conciseness while maintaining the same behavior.

Also applies to: 151-151, 179-179, 211-211, 235-235

portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/GithubCreationFlow.tsx (1)

602-610: LGTM!

The onValidationChange handler is correctly wired to propagate validation state from CreationMetaData.

portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx (1)

383-389: LGTM!

The validation change handler and Create button gating logic are correctly implemented, consistent with the other creation flows.

Also applies to: 403-418

portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx (3)

136-150: LGTM - Identifier display logic is sound.

The computed identifierDisplayValue correctly appends the major version suffix only when the user hasn't manually edited the identifier, and identifierToValidate properly derives from it. The coupling between version and identifier is intentional for the auto-generated case.


286-293: LGTM - Proper cleanup of debounce timers.

The cleanup effect correctly clears both timer refs on unmount, preventing memory leaks and potential state updates on unmounted components.


413-428: LGTM - Error display implementation.

Validation errors are properly surfaced to users with appropriate styling and positioning.

portals/management-portal/src/context/validationContext.tsx (4)

75-81: LGTM - Well-implemented type guard.

The isGithubValidationErr type guard provides proper discriminant narrowing, enabling type-safe access to the errors array only when validation fails.


236-244: LGTM - Derived uniqueness values.

The isNameVersionUnique and isIdentifierUnique computed values correctly derive from the validation results with proper null handling.


246-264: LGTM - Comprehensive reset function.

The reset function properly clears all validation state including the new name/version and identifier validation states.


266-330: LGTM - Context value properly memoized.

All state values and callbacks are correctly included in the useMemo dependency array, ensuring the context value only updates when necessary.

portals/management-portal/src/pages/apis/ApiOverview.tsx (1)

440-444: UI-only refactors/readability changes look fine.

Also applies to: 813-888, 1006-1006

Copy link
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: 2

♻️ Duplicate comments (2)
portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/URLCreationFlow.tsx (1)

54-72: Good: metaHasErrors reset on reopen (prevents stale validation state).
This addresses the earlier “stale errors after reopening” issue.

portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/GithubCreationFlow.tsx (1)

104-117: Good: metaHasErrors is explicitly reset when the flow closes.

🧹 Nitpick comments (2)
portals/management-portal/src/pages/apis/ApiOverview.tsx (1)

56-75: Clipboard optional-chaining crash fixed; consider a fallback UX when Clipboard API is unavailable.
Current chaining avoids .then() on undefined, but failures are silently ignored.

 const handleCopyUrl = (url: string) => {
-  navigator.clipboard
-    ?.writeText?.(url)
-    ?.then(() => {
-      setCopiedUrl(url);
-      setTimeout(() => setCopiedUrl(null), 2000);
-    })
-    ?.catch(() => {});
+  const p = navigator.clipboard?.writeText?.(url);
+  if (!p) return; // optionally show a tooltip/snackbar: "Copy not supported"
+  p.then(() => {
+    setCopiedUrl(url);
+    setTimeout(() => setCopiedUrl(null), 2000);
+  }).catch(() => {
+    // optionally show a tooltip/snackbar: "Copy failed"
+  });
 };
portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx (1)

367-386: Avoid as any on onBlur handlers (tighten TextInput typing instead).
The casts hide real handler signature mismatches and make refactors riskier.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 677ed24 and 09a86aa.

📒 Files selected for processing (4)
  • portals/management-portal/src/pages/apis/ApiOverview.tsx (18 hunks)
  • portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/GithubCreationFlow.tsx (10 hunks)
  • portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/URLCreationFlow.tsx (4 hunks)
  • portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx (7 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-02T07:06:41.830Z
Learnt from: piyumaldk
Repo: wso2/api-platform PR: 223
File: portals/management-portal/src/common/VersionInput.tsx:23-31
Timestamp: 2025-12-02T07:06:41.830Z
Learning: In portals/management-portal/src/common/VersionInput.tsx, the touchedRef pattern is intentionally used for one-time initialization. Once the component loads, only user modifications should update the value, and external prop changes are deliberately ignored to prevent disrupting user input.

Applied to files:

  • portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx
🧬 Code graph analysis (2)
portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx (3)
portals/management-portal/src/helpers/openApiHelpers.ts (1)
  • slugify (3-5)
portals/management-portal/src/context/CreateComponentBuildpackContext.tsx (1)
  • useCreateComponentBuildpackContext (92-100)
portals/management-portal/src/context/validationContext.tsx (1)
  • useGithubProjectValidationContext (335-343)
portals/management-portal/src/pages/apis/ApiOverview.tsx (2)
portals/management-portal/src/context/ApiContext.tsx (1)
  • useApisContext (340-346)
portals/management-portal/src/hooks/apis.ts (2)
  • ApiSummary (28-45)
  • ApiGatewaySummary (87-106)
🔇 Additional comments (6)
portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/URLCreationFlow.tsx (1)

301-335: Good: Create is now gated on metadata validation state.
Wiring CreationMetaData.onValidationChangemetaHasErrors and disabling Create is consistent with the new uniqueness validation UX.

portals/management-portal/src/pages/apis/ApiOverview.tsx (1)

571-575: initials() behavior looks safe (empty/non-letter names fall back to “API”).

Also applies to: 678-679

portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/GithubCreationFlow.tsx (3)

35-40: selectedProjectId is now optional: please verify all call-sites still pass it in create flows.
You correctly guard at runtime (if (!selectedProjectId) ...). This is mainly about preventing unintended “Project is required” regressions due to a missed prop after the type change.

Also applies to: 323-326


260-289: Good: version normalization + major/minor validation are consistent end-to-end.
Prefill via formatVersionToMajorMinor(...) aligns with isValidMajorMinorVersion(...) gating.

Also applies to: 372-383


602-636: Good: metadata uniqueness validation is now wired into Create gating.

portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx (1)

33-38: Nice: onValidationChangeRef avoids requiring parents to memoize callbacks.
This resolves the earlier “inline arrow causes effect churn” issue without pushing complexity to all callers.

Also applies to: 100-105, 348-355

Copy link
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

♻️ Duplicate comments (1)
portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx (1)

426-433: Similar stale value concern for identifier validation.

When version changes, identifierDisplayValue (and thus identifierToValidate) will update, but the scheduled validation captures the current (pre-change) value. The same fix pattern from the name change handler applies here.

Consider passing the computed identifier explicitly when scheduling:

               scheduleNameVersionValidation({ version: v });
               setIdentifierError(null);
-              scheduleIdentifierValidation();
+              // Compute what the new identifier will be after this version change
+              const newMajor = (v || "").trim().match(/\d+/)?.[0] ?? "";
+              const base = (meta.identifier ?? "").trim();
+              const newIdent = !meta.identifierEdited && newMajor && !/-v\d+$/i.test(base) 
+                ? `${base}-v${newMajor}` 
+                : base;
+              scheduleIdentifierValidation({ identifier: newIdent });
             }}
🧹 Nitpick comments (2)
portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx (2)

15-21: Redundant .trim() at line 21.

The initial .trim() on line 17 already removes whitespace, and subsequent operations (lowercase, regex replacements) don't introduce whitespace. The final .trim() is a no-op.

 const slugify = (val: string) =>
   val
     .trim()
     .toLowerCase()
     .replace(/[^a-z0-9]+/g, "-")
-    .replace(/^-+|-+$/g, "")
-    .trim();
+    .replace(/^-+|-+$/g, "");

386-404: Avoid as any type assertions for onBlur.

The as any casts suggest a type mismatch with TextInput's onBlur prop. If onBlur expects (event: React.FocusEvent) => void, the IIFE pattern works but the cast hides potential type issues.

Consider either:

  1. Updating TextInput to accept () => void for onBlur, or
  2. Using the event parameter explicitly:
-onBlur={(() => flushNameVersionValidation({ force: true })) as any}
+onBlur={() => flushNameVersionValidation({ force: true })}

If the cast is needed due to TextInput's typing, consider adding a brief comment explaining why.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 09a86aa and d7041fe.

📒 Files selected for processing (1)
  • portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx (7 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-02T07:06:41.830Z
Learnt from: piyumaldk
Repo: wso2/api-platform PR: 223
File: portals/management-portal/src/common/VersionInput.tsx:23-31
Timestamp: 2025-12-02T07:06:41.830Z
Learning: In portals/management-portal/src/common/VersionInput.tsx, the touchedRef pattern is intentionally used for one-time initialization. Once the component loads, only user modifications should update the value, and external prop changes are deliberately ignored to prevent disrupting user input.

Applied to files:

  • portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx
🧬 Code graph analysis (1)
portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx (4)
portals/management-portal/src/helpers/openApiHelpers.ts (1)
  • slugify (3-5)
portals/management-portal/src/context/CreateComponentBuildpackContext.tsx (1)
  • useCreateComponentBuildpackContext (92-100)
portals/management-portal/src/context/validationContext.tsx (1)
  • useGithubProjectValidationContext (335-343)
portals/management-portal/src/components/src/components/TextInput/TextInput.tsx (1)
  • TextInput (55-169)
🔇 Additional comments (13)
portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx (13)

33-42: LGTM!

The onValidationChange prop type is well-structured, and the override types with force flag enable proper cache bypass when needed.


81-109: LGTM!

Good use of refs to store onValidationChange callback, avoiding stale closure issues in effects. The timer and cache refs are properly initialized.


125-139: LGTM!

Handler functions are clean and correctly apply slugification and track edit state.


141-158: LGTM with minor consideration.

The version suffix logic is sound. Note that if meta.version is something like "v1.0.0" (prefixed with 'v'), the regex /\d+/ will still extract "1" correctly, which is the intended behavior.


163-219: LGTM!

Validation logic correctly handles:

  • Cache bypass with force flag
  • Cache cleared on failure (allows retry after transient errors)
  • Validating state properly tracked in finally block
  • Good error message extraction

221-242: LGTM!

Debounce and flush logic is correctly implemented. Timer is properly cleared before scheduling new validations.


244-278: LGTM!

Good implementation that validates the slugified identifier (matching what gets persisted). Cache handling and error recovery are correct.


280-301: LGTM!

Consistent with the name/version scheduling pattern.


303-310: LGTM!

Proper cleanup of timers on component unmount prevents memory leaks and stale callbacks.


312-330: LGTM!

The one-time initialization pattern using didInitValidateRef is correct. The disabled ESLint rule is intentional since re-running on dependency changes would defeat the purpose.


358-373: LGTM!

Correctly propagates validation state to parent. Including nameVersionValidating and identifierValidating in hasError prevents form submission while validation is in-flight (addresses the "click Create before debounce" bypass).


399-403: LGTM!

The identifier change handler correctly clears error state and schedules validation. The validation function will slugify the input before checking.


439-454: LGTM!

Clean error display with proper conditional rendering. Good accessibility with the aria-label on the edit button.

Copy link
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: 4

Caution

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

⚠️ Outside diff range comments (1)
portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx (1)

314-342: Fix stale identifier validation: pass the computed next identifier into scheduleIdentifierValidation.

Right now scheduleIdentifierValidation() (Line 341, Line 448) reads identifierToValidate from the current render, but you just called change(nextPatch) / change({ version: v, identifier: nextIdentifier }). This can validate the previous identifier and never validate the newly persisted one.

Suggested patch (keep logic the same, but validate the actual next value):

   const handleNameChange = (v: string) => {
@@
-    if (!meta.identifierEdited && !isIdentifierEditing) {
-      nextPatch.identifier = buildIdentifierFromNameAndVersion(
-        v,
-        meta.version || ""
-      );
-    }
+    const nextIdentifier =
+      !meta.identifierEdited && !isIdentifierEditing
+        ? buildIdentifierFromNameAndVersion(v, meta.version || "")
+        : (meta.identifier ?? "");
+    if (!meta.identifierEdited && !isIdentifierEditing) {
+      nextPatch.identifier = nextIdentifier;
+    }
@@
     scheduleNameVersionValidation({ name: v });
-    scheduleIdentifierValidation();
+    scheduleIdentifierValidation({ identifier: nextIdentifier });
   };
@@
             onChange={(v: string) => {
@@
               const shouldAuto = !meta.identifierEdited && !isIdentifierEditing;
               const nextIdentifier = shouldAuto
                 ? buildIdentifierFromNameAndVersion(
                     meta.displayName || meta.name || "",
                     v
                   )
                 : meta.identifier;
@@
               scheduleNameVersionValidation({ version: v });
               setIdentifierError(null);
-              scheduleIdentifierValidation();
+              scheduleIdentifierValidation({ identifier: nextIdentifier ?? "" });
             }}

Also applies to: 430-449

🧹 Nitpick comments (3)
portals/management-portal/src/pages/apis/CreationFlows/EndPointCreationFlow.tsx (1)

19-36: De-duplicate slug/identifier helpers (already exist elsewhere) to prevent drift.

These helpers are repeated across multiple flows; consider moving majorFromVersion + buildIdentifierFromNameAndVersion to a shared helper module (you already have slugify in portals/management-portal/src/helpers/openApiHelpers.ts). This will prevent subtle inconsistencies between flows.

portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/URLCreationFlow.tsx (1)

22-39: De-duplicate slug/identifier helpers across flows.

Same helpers appear in multiple creation flows—move majorFromVersion / buildIdentifierFromNameAndVersion to a shared helper (and reuse openApiHelpers.slugify).

portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/GithubCreationFlow.tsx (1)

53-70: De-duplicate slug/identifier helpers (use shared helper).

Same slugify/majorFromVersion/buildIdentifierFromNameAndVersion logic exists in other flows; consider extracting to a single helper (and reusing portals/management-portal/src/helpers/openApiHelpers.ts where possible) to avoid divergence.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7041fe and fd4e752.

📒 Files selected for processing (5)
  • portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/GithubCreationFlow.tsx (15 hunks)
  • portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/URLCreationFlow.tsx (10 hunks)
  • portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx (11 hunks)
  • portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx (5 hunks)
  • portals/management-portal/src/pages/apis/CreationFlows/EndPointCreationFlow.tsx (8 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-02T07:06:41.830Z
Learnt from: piyumaldk
Repo: wso2/api-platform PR: 223
File: portals/management-portal/src/common/VersionInput.tsx:23-31
Timestamp: 2025-12-02T07:06:41.830Z
Learning: In portals/management-portal/src/common/VersionInput.tsx, the touchedRef pattern is intentionally used for one-time initialization. Once the component loads, only user modifications should update the value, and external prop changes are deliberately ignored to prevent disrupting user input.

Applied to files:

  • portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx
  • portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx
🧬 Code graph analysis (4)
portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx (5)
portals/management-portal/src/helpers/openApiHelpers.ts (6)
  • slugify (3-5)
  • formatVersionToMajorMinor (60-81)
  • firstServerUrl (12-16)
  • deriveContext (18-20)
  • defaultServiceName (7-10)
  • isValidMajorMinorVersion (88-91)
portals/management-portal/src/hooks/apis.ts (2)
  • ImportOpenApiRequest (72-84)
  • ApiSummary (28-45)
portals/management-portal/src/hooks/validation.ts (2)
  • OpenApiValidationResponse (43-45)
  • useOpenApiValidation (127-185)
portals/management-portal/src/context/CreateComponentBuildpackContext.tsx (1)
  • useCreateComponentBuildpackContext (92-100)
portals/management-portal/src/components/src/components/Common/ApiOperationsList.tsx (1)
  • ApiOperationsList (58-108)
portals/management-portal/src/pages/apis/CreationFlows/EndPointCreationFlow.tsx (2)
portals/management-portal/src/context/CreateComponentBuildpackContext.tsx (1)
  • useCreateComponentBuildpackContext (92-100)
portals/management-portal/src/hooks/apis.ts (1)
  • CreateApiPayload (57-70)
portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/GithubCreationFlow.tsx (1)
portals/management-portal/src/helpers/openApiHelpers.ts (2)
  • slugify (3-5)
  • formatVersionToMajorMinor (60-81)
portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx (3)
portals/management-portal/src/helpers/openApiHelpers.ts (1)
  • slugify (3-5)
portals/management-portal/src/context/CreateComponentBuildpackContext.tsx (1)
  • useCreateComponentBuildpackContext (92-100)
portals/management-portal/src/context/validationContext.tsx (1)
  • useGithubProjectValidationContext (335-343)

Copy link
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

♻️ Duplicate comments (2)
portals/management-portal/src/pages/apis/CreationFlows/EndPointCreationFlow.tsx (1)

121-152: Remove as any cast: ensure EndpointMeta is compatible with context setter type.

The setEndpointMeta updater types prev correctly as EndpointMeta but then casts the return value to as any at line 151. This bypasses TypeScript's type checking and can mask shape mismatches.

Verify that EndpointMeta (defined at lines 56-59) is compatible with the type expected by setEndpointMeta from useCreateComponentBuildpackContext(). If identifier and identifierEdited are valid fields in the context's metadata type, remove the as any cast:

-        return nextMeta as any;
+        return nextMeta;

If the context expects only ProxyMetadata, either:

  1. Update the context type to accept EndpointMeta, or
  2. Only return the fields that are part of ProxyMetadata
portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx (1)

446-461: Align Create button gating with onCreate requirements: check displayName and identifier, not name.

The Create button currently checks contractMeta?.name (line 451), but the onCreate function requires and validates identifier (lines 212-223). This inconsistency could enable the button when creation would actually fail.

Update the disabled condition to match onCreate's requirements:

  disabled={
    creating ||
    metaHasErrors ||
-   !(contractMeta?.name || "").trim() ||
+   !(contractMeta?.displayName || contractMeta?.name || "").trim() ||
+   !((contractMeta as any)?.identifier || "").trim() ||
    !(contractMeta?.context || "").trim() ||
    !isValidMajorMinorVersion(
      (contractMeta?.version || "").trim()
    )
  }
🧹 Nitpick comments (1)
portals/management-portal/src/pages/apis/CreationFlows/EndPointCreationFlow.tsx (1)

261-265: Validation wiring looks good; consider extracting button-disabled logic for readability.

The onValidationChange callback correctly propagates validation state, and the Create button properly gates on all required fields. The logic is comprehensive and correct.

For improved readability, consider extracting the disabled condition:

const meta = endpointMeta as EndpointMeta | undefined;
const canCreate =
  !creating &&
  !metaHasErrors &&
  !!(meta?.displayName || meta?.name || "").trim() &&
  !!(meta?.identifier || "").trim() &&
  !!(meta?.context || "").trim() &&
  !!(meta?.version || "").trim();

// Then in JSX:
<Button
  variant="contained"
  onClick={handleCreate}
  disabled={!canCreate}
  sx={{ textTransform: "none" }}
>
  {creating ? "Creating..." : "Create"}
</Button>

Also applies to: 296-317

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd4e752 and 6308636.

📒 Files selected for processing (4)
  • portals/management-portal/src/hooks/apis.ts (1 hunks)
  • portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/URLCreationFlow.tsx (10 hunks)
  • portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx (11 hunks)
  • portals/management-portal/src/pages/apis/CreationFlows/EndPointCreationFlow.tsx (9 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-02T07:06:41.830Z
Learnt from: piyumaldk
Repo: wso2/api-platform PR: 223
File: portals/management-portal/src/common/VersionInput.tsx:23-31
Timestamp: 2025-12-02T07:06:41.830Z
Learning: In portals/management-portal/src/common/VersionInput.tsx, the touchedRef pattern is intentionally used for one-time initialization. Once the component loads, only user modifications should update the value, and external prop changes are deliberately ignored to prevent disrupting user input.

Applied to files:

  • portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx
🧬 Code graph analysis (2)
portals/management-portal/src/pages/apis/CreationFlows/EndPointCreationFlow.tsx (2)
portals/management-portal/src/context/CreateComponentBuildpackContext.tsx (2)
  • ProxyMetadata (4-15)
  • useCreateComponentBuildpackContext (92-100)
portals/management-portal/src/hooks/apis.ts (1)
  • CreateApiPayload (57-70)
portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/URLCreationFlow.tsx (2)
portals/management-portal/src/helpers/openApiHelpers.ts (4)
  • slugify (3-5)
  • deriveContext (18-20)
  • defaultServiceName (7-10)
  • isValidMajorMinorVersion (88-91)
portals/management-portal/src/hooks/apis.ts (1)
  • ImportOpenApiRequest (72-85)
🔇 Additional comments (6)
portals/management-portal/src/hooks/apis.ts (1)

78-78: LGTM!

The addition of the optional displayName field properly extends the ImportOpenApiRequest type to support the displayName-based naming used throughout the creation flows.

portals/management-portal/src/pages/apis/CreationFlows/EndPointCreationFlow.tsx (1)

183-200: LGTM!

The payload construction correctly uses identifier for the name field and includes all required fields with proper typing. The explicit CreateApiPayload type annotation ensures type safety.

portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/URLCreationFlow.tsx (2)

226-238: LGTM!

The payload construction is now properly typed as ImportOpenApiRequest without any type casts. The structure correctly maps identifier to name and includes all required and optional fields with proper typing.


345-349: LGTM!

The validation wiring correctly propagates errors via onValidationChange, and the Create button comprehensively checks all required fields including metaHasErrors, displayName, identifier, context, and version validity using isValidMajorMinorVersion.

Also applies to: 366-380

portals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx (2)

252-264: LGTM!

The payload construction is properly typed as ImportOpenApiRequest and correctly uses identifier for the name field, with all required and optional fields properly structured.


425-431: LGTM!

The onValidationChange callback is correctly wired to propagate validation state from CreationMetaData to the parent flow's metaHasErrors state.

@thivindu thivindu merged commit 62508ec into wso2:main Dec 13, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants