feat: improve organization creation and update validation#2394
Conversation
WalkthroughCentralizes organization validation into Zod schemas in Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
Comment |
…aracter-in-organization-slug
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
controlplane/src/core/repositories/OrganizationRepository.ts (1)
110-136: Case‑insensitive slug lookup is correct; consider an expression index if this becomes hotUsing
lower(organizations.slug)compared toslug.toLowerCase()makes lookups resilient to existing mixed‑case data and better aligned with the new lowercasing validation. IfbySlugis on a hot path and the table grows large, consider adding a functional index onlower(slug)in Postgres to preserve index usage for this query.controlplane/src/core/bufservices/organization/updateOrganizationDetails.ts (1)
11-14: Schema-based validation for updates is good; confirm callers always send both name and slugUsing
organizationSchema.safeParseand normalizedvalidatedReq.data.name/slugfor uniqueness checks, Keycloak group rename, role renames, andupdateOrganizationkeeps update behavior consistent with the create flow and the centralized rules.One thing to double‑check: this now always validates both
organizationNameandorganizationSlugtogether. If any existing client relied on partially updating just the name or just the slug (leaving the other unset/empty), those requests will now returnERR_BAD_REQUESTdue to the schema’s non‑optional fields. If partial updates are not supported anywhere, this is fine; otherwise you may want a variant schema that treats one of the fields as optional.Also note that the audit log at the end still uses
authContext.organizationSlug, which will hold the old slug when a rename occurs; if you want logs to reflect the new slug, consider switching this to the updated value.Also applies to: 63-76, 96-111, 118-120
controlplane/test/organization/create-organization.test.ts (1)
1-1: Comprehensive negative tests for org creation look good; consider adding the “account” slug caseThese new tests do a good job of asserting the schema-driven behavior for whitespace-only, too-short/too-long names and slugs, and reserved slug values, including the exact error messages returned as
details.Since
organizationSlugSchemaalso treats"account"as a reserved keyword, you might want to extend thetest.eachtable to include it, to keep test coverage aligned with the full reserved list:- test.each(['login', 'create', 'signup'])( + test.each(['login', 'create', 'signup', 'account'])(Also applies to: 43-139
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
controlplane/src/core/bufservices/check/getChecksByFederatedGraphName.ts(0 hunks)controlplane/src/core/bufservices/organization/createOrganization.ts(4 hunks)controlplane/src/core/bufservices/organization/updateOrganizationDetails.ts(4 hunks)controlplane/src/core/constants.ts(2 hunks)controlplane/src/core/repositories/OrganizationRepository.ts(1 hunks)controlplane/src/core/util.test.ts(2 hunks)controlplane/src/core/util.ts(0 hunks)controlplane/test/deactivate-org.test.ts(1 hunks)controlplane/test/organization/create-organization.test.ts(2 hunks)studio/src/pages/create.tsx(2 hunks)
💤 Files with no reviewable changes (2)
- controlplane/src/core/util.ts
- controlplane/src/core/bufservices/check/getChecksByFederatedGraphName.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-29T10:28:04.846Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1749-1751
Timestamp: 2025-08-29T10:28:04.846Z
Learning: In the controlplane codebase, authentication and authorization checks (including organization scoping) are handled at the service layer in files like unlinkSubgraph.ts before calling repository methods. Repository methods like unlinkSubgraph() in SubgraphRepository.ts can focus purely on data operations without redundant security checks.
Applied to files:
controlplane/src/core/bufservices/organization/updateOrganizationDetails.tscontrolplane/src/core/bufservices/organization/createOrganization.tscontrolplane/src/core/repositories/OrganizationRepository.ts
📚 Learning: 2025-10-17T16:35:24.723Z
Learnt from: Aenimus
Repo: wundergraph/cosmo PR: 2287
File: composition/tests/v1/federation-factory.test.ts:569-583
Timestamp: 2025-10-17T16:35:24.723Z
Learning: In the wundergraph/cosmo repository's Vitest test suite, `toHaveLength()` works with Map objects and `expect(map.has(key))` without an explicit matcher correctly asserts the boolean result in the test assertions for `composition/tests/v1/federation-factory.test.ts`.
Applied to files:
controlplane/test/organization/create-organization.test.ts
🧬 Code graph analysis (5)
controlplane/src/core/bufservices/organization/updateOrganizationDetails.ts (1)
controlplane/src/core/constants.ts (1)
organizationSchema(32-41)
controlplane/test/organization/create-organization.test.ts (2)
controlplane/test/test-util.ts (1)
SetupTest(66-418)controlplane/src/core/test-util.ts (1)
genID(53-55)
controlplane/src/core/bufservices/organization/createOrganization.ts (1)
controlplane/src/core/constants.ts (1)
organizationSchema(32-41)
controlplane/src/core/util.test.ts (1)
controlplane/src/core/constants.ts (1)
organizationSlugSchema(15-30)
controlplane/src/core/repositories/OrganizationRepository.ts (1)
controlplane/src/db/schema.ts (1)
organizations(1266-1289)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: build_push_image
- GitHub Check: Analyze (go)
- GitHub Check: build_test
- GitHub Check: build_push_image
- GitHub Check: build_test
🔇 Additional comments (4)
controlplane/test/deactivate-org.test.ts (1)
105-109: LGTM: capturing createOrganization result keeps behavior unchangedStoring the
createOrganizationresponse in a local constant does not alter the test flow; nothing further needed here.studio/src/pages/create.tsx (1)
83-107: Frontend validation now aligned with backend org schemaTrimming name/slug, lowercasing slug, extending length to 32, and blocking reserved slugs (including
"account") bring the UI validation in line with the centralized backend schema, which should prevent most client/server mismatch issues.controlplane/src/core/bufservices/organization/createOrganization.ts (1)
17-18: Centralized schema validation and use of normalized name/slug look solidValidating the request with
organizationSchema.safeParseand short‑circuiting withERR_BAD_REQUESTplus field‑level messages is a clear improvement. UsingvalidatedReq.data.name/slugfor Keycloak seeding andcreateOrganizationensures the stored slug is the normalized, lowercased value enforced by the schema.Also applies to: 30-39, 68-71, 82-85
controlplane/src/core/constants.ts (1)
1-1: Centralized org name/slug schemas are well-structured and match usage elsewhereDefining
organizationSlugSchema(trim + lowercase + regex + 3–32 length + reserved slugs) andorganizationSchemahere gives a single source of truth that both bufservices and tests consume, which is exactly what you want for consistent validation and error messaging. The frontend schema instudio/src/pages/create.tsxmirrors these rules closely, so future changes should mostly happen in this file.Also applies to: 15-41
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
controlplane/src/core/bufservices/organization/createOrganization.ts (1)
17-39: Centralized schema validation looks good; consider narrowing the validated payload and error reportingGood call moving name/slug validation into
organizationSchemaand failing fast withERR_BAD_REQUEST. This keeps the handler consistent with other schema-driven validations.Two small, optional tweaks you might consider:
- Validate only the fields you care about
Right now you callorganizationSchema.safeParse(req), while the schema only describes{ name, slug }. To avoid relying on how unknown keys are treated and to decouple from future changes toCreateOrganizationRequest, you could explicitly project the payload:- const validatedReq = organizationSchema.safeParse(req); + const validatedReq = organizationSchema.safeParse({ + name: req.name, + slug: req.slug, + });
- Expose or log multiple field errors (optional)
fieldErrors.name?.[0] || fieldErrors.slug?.[0]only surfaces the first available message. If both fields are invalid, you might want to either:
- Concatenate messages into a single string, or
- Prefer one field deterministically (e.g., slug-first) depending on UX expectations, and optionally log the full
validatedReq.errorat debug level for easier diagnosis.These are non-blocking; the current behavior is functionally correct.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
controlplane/src/core/bufservices/organization/createOrganization.ts(4 hunks)controlplane/src/core/bufservices/organization/updateOrganizationDetails.ts(4 hunks)controlplane/src/core/constants.ts(2 hunks)controlplane/src/core/util.test.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- controlplane/src/core/bufservices/organization/updateOrganizationDetails.ts
- controlplane/src/core/util.test.ts
- controlplane/src/core/constants.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-29T10:28:04.846Z
Learnt from: JivusAyrus
Repo: wundergraph/cosmo PR: 2156
File: controlplane/src/core/repositories/SubgraphRepository.ts:1749-1751
Timestamp: 2025-08-29T10:28:04.846Z
Learning: In the controlplane codebase, authentication and authorization checks (including organization scoping) are handled at the service layer in files like unlinkSubgraph.ts before calling repository methods. Repository methods like unlinkSubgraph() in SubgraphRepository.ts can focus purely on data operations without redundant security checks.
Applied to files:
controlplane/src/core/bufservices/organization/createOrganization.ts
🧬 Code graph analysis (1)
controlplane/src/core/bufservices/organization/createOrganization.ts (1)
controlplane/src/core/constants.ts (1)
organizationSchema(33-42)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: build_push_image
- GitHub Check: build_test
- GitHub Check: build_test
- GitHub Check: build_push_image
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (go)
🔇 Additional comments (1)
controlplane/src/core/bufservices/organization/createOrganization.ts (1)
70-85: Using validatedslug/namefor Keycloak seeding and org creation is correctSwitching
seedGroupandcreateOrganizationto usevalidatedReq.data.slugandvalidatedReq.data.nameensures that any schema-level transforms (e.g., trimming, normalization) are applied consistently across Keycloak and the database. This avoids subtle mismatches between what was validated and what is persisted / propagated.No changes requested here; this looks solid.
…aracter-in-organization-slug
…-possible-character-in-organization-slug' into wilson/eng-8610-remove-spaces-as-possible-character-in-organization-slug
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
controlplane/src/core/util.test.ts (1)
204-222: Slug tests now match the 3–32 char schema; consider a couple of extra casesThe updated expectations and new cases (
'25CharactersLong123456789'as valid, whitespace-only, too-short, and too-long slugs) correctly reflectorganizationSlugSchema(trim + lowercase, 3–32 chars, regex constraints), and checkingsafeParse(...).successagainstexpectedis sound.If you want to harden coverage a bit further, you could optionally:
- Add a case for a reserved keyword (e.g.
'login') expectingfalse.- Add one test that asserts the normalized output (e.g.
'ACME-Corp'parses successfully andparsed.data === 'acme-corp').Not required for this PR, but would more tightly pin the schema’s behavior.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
controlplane/src/core/util.test.ts(2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-17T16:35:24.723Z
Learnt from: Aenimus
Repo: wundergraph/cosmo PR: 2287
File: composition/tests/v1/federation-factory.test.ts:569-583
Timestamp: 2025-10-17T16:35:24.723Z
Learning: In the wundergraph/cosmo repository's Vitest test suite, `toHaveLength()` works with Map objects and `expect(map.has(key))` without an explicit matcher correctly asserts the boolean result in the test assertions for `composition/tests/v1/federation-factory.test.ts`.
Applied to files:
controlplane/src/core/util.test.ts
🧬 Code graph analysis (1)
controlplane/src/core/util.test.ts (1)
controlplane/src/core/constants.ts (1)
organizationSlugSchema(15-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: build_test
- GitHub Check: build_push_image
- GitHub Check: build_push_image
- GitHub Check: build_test
- GitHub Check: Analyze (go)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
controlplane/src/core/util.test.ts (1)
2-3: Import changes correctly switch tests to the schema-based validatorDropping the old slug validator import and using
organizationSlugSchemakeeps tests aligned with the new centralized validation logic inconstants.ts. No issues here.
…aracter-in-organization-slug
Summary by CodeRabbit
New Features
Bug Fixes
Tests
✏️ Tip: You can customize this high-level summary in your review settings.
Checklist
Fixes a discrepancy in validation of the organization name between the frontend and backend