-
Notifications
You must be signed in to change notification settings - Fork 32
Add API Name, Identifier and version validation #417
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this 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: ResetmetaHasErrorswhen wizard opens.Similar to other flows,
metaHasErrorsshould be reset in theuseEffectthat handles theopenstate 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 whenapi.displayNameis missing.Title uses
api.displayNamebut the initials are computed fromapi.name; ifdisplayNameis optional, considerapi.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, oridentifierare 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 includingmetaHasErrorsincanCreatefor consistency.The
canCreatecomputed value validates most conditions but excludesmetaHasErrors, which is then added separately in the button'sdisabledprop. Including it incanCreatewould 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/CardContentwrappers 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
Paperwrappers 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: Avoidas anycasts on event handlers.The
as anycasts ononBlursuggest a type mismatch between whatTextInputexpects 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
TextInputcomponent 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.
debounceMsis a constant that never changes. Defining it inside the component means it's included inuseCallbackdependency 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_MSand 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 anAbortController,validateNameVersionandvalidateIdentifierdon't support request cancellation. Given thatCreationMetaData.tsxuses 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
signaloption. Consider either:
- Creating an AbortController per call and exposing abort capability
- 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 stalesetTimeoutupdates 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+useEffectcleanup) inGatewayListItem.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
📒 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.tsxportals/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 onmetaHasErrorsalong 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
ApiUniquenessValidationResponsecorrectly models the backend response with a discriminatedvalidboolean and optionalerrorobject.
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
onValidationChangehandler is correctly wired to propagate validation state fromCreationMetaData.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
identifierDisplayValuecorrectly appends the major version suffix only when the user hasn't manually edited the identifier, andidentifierToValidateproperly 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
isGithubValidationErrtype guard provides proper discriminant narrowing, enabling type-safe access to theerrorsarray only when validation fails.
236-244: LGTM - Derived uniqueness values.The
isNameVersionUniqueandisIdentifierUniquecomputed 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
useMemodependency 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
.../management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/GithubCreationFlow.tsx
Show resolved
Hide resolved
.../management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx
Show resolved
Hide resolved
...als/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/URLCreationFlow.tsx
Show resolved
Hide resolved
portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this 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:metaHasErrorsreset 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:metaHasErrorsis 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()onundefined, 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: Avoidas anyononBlurhandlers (tightenTextInputtyping instead).
The casts hide real handler signature mismatches and make refactors riskier.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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.
WiringCreationMetaData.onValidationChange→metaHasErrorsand 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:selectedProjectIdis 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 viaformatVersionToMajorMinor(...)aligns withisValidMajorMinorVersion(...)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:onValidationChangeRefavoids 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
portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx
Show resolved
Hide resolved
There was a problem hiding this 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 thusidentifierToValidate) 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: Avoidas anytype assertions foronBlur.The
as anycasts suggest a type mismatch withTextInput'sonBlurprop. IfonBlurexpects(event: React.FocusEvent) => void, the IIFE pattern works but the cast hides potential type issues.Consider either:
- Updating
TextInputto accept() => voidforonBlur, or- 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
📒 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
onValidationChangeprop type is well-structured, and the override types withforceflag enable proper cache bypass when needed.
81-109: LGTM!Good use of refs to store
onValidationChangecallback, 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.versionis 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
forceflag- Cache cleared on failure (allows retry after transient errors)
- Validating state properly tracked in
finallyblock- 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
didInitValidateRefis 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
nameVersionValidatingandidentifierValidatinginhasErrorprevents 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-labelon the edit button.
portals/management-portal/src/pages/apis/CreationFlows/CreationMetaData.tsx
Show resolved
Hide resolved
There was a problem hiding this 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 intoscheduleIdentifierValidation.Right now
scheduleIdentifierValidation()(Line 341, Line 448) readsidentifierToValidatefrom the current render, but you just calledchange(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+buildIdentifierFromNameAndVersionto a shared helper module (you already haveslugifyinportals/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/buildIdentifierFromNameAndVersionto a shared helper (and reuseopenApiHelpers.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/buildIdentifierFromNameAndVersionlogic exists in other flows; consider extracting to a single helper (and reusingportals/management-portal/src/helpers/openApiHelpers.tswhere possible) to avoid divergence.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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.tsxportals/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)
.../management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx
Show resolved
Hide resolved
.../management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx
Show resolved
Hide resolved
...als/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/URLCreationFlow.tsx
Show resolved
Hide resolved
portals/management-portal/src/pages/apis/CreationFlows/EndPointCreationFlow.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this 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: Removeas anycast: ensureEndpointMetais compatible with context setter type.The
setEndpointMetaupdater typesprevcorrectly asEndpointMetabut then casts the return value toas anyat 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 bysetEndpointMetafromuseCreateComponentBuildpackContext(). IfidentifierandidentifierEditedare valid fields in the context's metadata type, remove theas anycast:- return nextMeta as any; + return nextMeta;If the context expects only
ProxyMetadata, either:
- Update the context type to accept
EndpointMeta, or- Only return the fields that are part of
ProxyMetadataportals/management-portal/src/pages/apis/CreationFlows/ContractCreationFlows/UploadCreationFlow.tsx (1)
446-461: Align Create button gating withonCreaterequirements: checkdisplayNameandidentifier, notname.The Create button currently checks
contractMeta?.name(line 451), but theonCreatefunction requires and validatesidentifier(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
onValidationChangecallback 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
📒 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
displayNamefield properly extends theImportOpenApiRequesttype 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
identifierfor thenamefield and includes all required fields with proper typing. The explicitCreateApiPayloadtype 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
ImportOpenApiRequestwithout any type casts. The structure correctly mapsidentifiertonameand 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 includingmetaHasErrors,displayName,identifier,context, and version validity usingisValidMajorMinorVersion.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
ImportOpenApiRequestand correctly usesidentifierfor thenamefield, with all required and optional fields properly structured.
425-431: LGTM!The
onValidationChangecallback is correctly wired to propagate validation state fromCreationMetaDatato the parent flow'smetaHasErrorsstate.
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

API Uniqueness Validation Support
ApiUniquenessValidationResponse,ApiNameVersionValidationRequest, etc.) to represent API uniqueness validation requests and responses invalidation.ts.useApiUniquenessValidationhook to validate API name+version and identifier uniqueness via backend endpoints.Validation Context Enhancements
GithubProjectValidationContextValueand theGithubProjectValidationProviderto 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]Summary by CodeRabbit
New Features
Bug Fixes & Improvements
UI/UX Improvements
✏️ Tip: You can customize this high-level summary in your review settings.