docs(api-reference): type remaining nullable/object org fields - #256
Conversation
Mirrors nvm-monorepo #2538 — completes the org-spec object-type cleanup. Adds BrandingDto, OrganizationStripeConfigDto, OrganizationActivitySubjectDto and re-types 10 fields that rendered as bare `type: object`: - Scalars -> string: OrganizationMemberWithProfileDto.name/.stripeAccountId, CustomerResponseDto.userName/.userEmail, ActivityEvent.actorUserId - Nested DTOs: CreateOrganizationDto.branding/live/sandbox, ActivityEvent.subject - Free-form map: ActivityEvent.metadata -> additionalProperties
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
There was a problem hiding this comment.
Pull request overview
Updates the Organizations OpenAPI spec to eliminate “bare” type: object fields by introducing explicit component schemas and retyping previously-ambiguous properties, improving generated API docs and client/schema tooling.
Changes:
- Re-typed several previously-
objectfields tostringwithnullable: truewhere the backend returnsnull. - Replaced inline
type: objectblobs with$ref-based component schemas for organization branding, Stripe config, and activity subjects. - Made
metadataexplicitly free-form viaadditionalProperties: trueto avoid structurelessobjectrendering.
Comments suppressed due to low confidence (1)
docs/api-reference/organizations-openapi.json:7512
accountUpdatedAtis described as an ISO timestamp but is missingformat: "date-time"(other ISO timestamp fields in this spec include the date-time format), which can reduce schema clarity and tooling support.
"accountUpdatedAt": {
"type": "string",
"description": "ISO timestamp the account was last updated"
},
r-marques
left a comment
There was a problem hiding this comment.
🤖 Automated PR review — 🟡 Mergeable with nits
Reviewed origin/main...HEAD (1 file, +155/−23) with a read-only panel: general code review, a source-accuracy cross-check against the nvm-monorepo backend, and a spec-consistency sweep.
Rather than trust the "copied verbatim / verified against the backend DTOs" claim, I checked all three new schemas field-by-field against the source — and they hold up. BrandingDto matches apps/api/src/organizations/dto/save-branding-dto.ts exactly (13 fields, same descriptions and examples); OrganizationStripeConfigDto matches the Stripe interface at libs/commons/src/lib/types/MetadataTypes.ts:107 exactly (6 fields, correct optionality/nullability — accountType really is 'express' | 'standard' | null); the retyped scalars match the type: String additions in nvm-monorepo#2538. No invented fields, none missing. The required arrays line up precisely with the decorators too: actorUserId/metadata/userName/userEmail correctly stay optional, name/stripeAccountId/subject/kind/id correctly required.
Mechanically it's clean: JSON parses, no duplicate keys, every $ref resolves, no name collisions with the sibling openapi.json, no .mdx page left stale, and the spec is openapi: 3.0.0 so nullable: true is legal here.
Measured effect: I ran @redocly/cli lint on base vs. HEAD. Base reports no-invalid-schema-examples: 4; HEAD reports zero, with every other rule count identical (29 warnings → 25, 0 new findings, no new unused components). This PR does exactly what it says.
No blockers. Three things worth fixing, mostly because they'd propagate into generated clients.
🟡 Should fix (3)
-
OrganizationActivitySubjectDtois a closed schema, but the contract is open —docs/api-reference/organizations-openapi.json:7520. The description advertises{ kind, id, ...extra }, the backend field is typedsubject!: { kind: string; id: string; [key: string]: unknown }(an index signature — extras are genuinely part of the contract), and the upstream class JSDoc even says "event types may attach extra keys, so the object is open (additionalProperties)" — yet neither this schema nor the upstream DTO actually declares it. Anything generating types from this spec will silently drop every extra key, which is precisely the payload that makes an activity event useful. Wants"additionalProperties": true. Since the source contradicts itself, fix it in #2538 and mirror down rather than patching only the docs side. -
accountTypeis a nullable enum that doesn't listnull—:7491. It's{"type": "string", "enum": ["express","standard"], "nullable": true}, but in OpenAPI 3.0nullable: truedoes not addnullto anenum; per the OAS docs, "nullmust be explicitly included in the list ofenumvalues. Usingnullable: truealone is not enough." The backing type is'express' | 'standard' | null, so as written the documented type can't express the real one. Worth noting neither redocly nor spectral's core ruleset flags this, so no tooling will ever catch it. Same fix belongs upstream. -
The upstream source PR is still open, and nothing here would detect divergence — nvm-monorepo#2538 is
OPEN, this file is hand-maintained, and the repo's only workflow ispublish-skill-clawhub.yml(no spec lint or diff gate). Both fixes above live upstream, so #2538 will likely move. Merging this first is safe for correctness — #2538 changes only Swagger annotations, not runtime response shapes, so nothing here misdescribes the live API — but it does mean silent drift if the upstream shape shifts. Suggest gating on #2538 landing, or re-diffing once it does.
💡 Good to have (4)
-
Two of the three new schemas are unreachable from any endpoint.
BrandingDto(:7412) andOrganizationStripeConfigDto(:7482) are referenced only byCreateOrganizationDto, and no path in this spec references that schema —/organizationsdocuments onlyput→UpdateOrganizationDto. So 3 of the 10 retyped fields (branding,live,sandbox) won't render for readers at all. The orphan pre-dates this PR (redocly flags the same 2 unused components on base), but since the PR is about what readers see, it's worth deciding: is a create-organization endpoint missing from the spec, or shouldCreateOrganizationDtobe pruned? (OrganizationActivitySubjectDtois reachable, so that third of the change does land.) -
The
sandboxexample regressed, and it's recoverable without dropping the$ref. The removed inline examples distinguishedacct_1234567890(live) fromacct_test_1234567890(sandbox); both now point at one shared schema, so readers see a live-looking ID in the sandbox slot. OAS 3.0 allowsexampleas anallOfsibling — and this file already does exactly that atCustomerResponseDto.status(:5904,description+example+allOf). So a per-propertyexamplecan go straight back onlive/sandbox. -
The two new timestamps carry neither
formatnor an example —:7505,:7509.accountCreatedAt/accountUpdatedAtaretype: stringdescribed as "ISO timestamp". In fairness the file is genuinely split on this (10*Atstring props withformat: date-time, 10 without), so it's not a convention violation — but these two are the only*Atproperties with neither, leaving the wire format in prose alone. Same foraccountTypeandadditionalInformation, which have no examples. -
One bare
type: objectstill renders shapeless —:3074, the 200 response ofGET /organizations/{orgId}/ai-catalog.json. The description's claim of "zero structurelesstype: objectproperties" is accurate as worded (this is a response body, not a property), but it's the last thing in the spec rendering without structure — worth either sweeping in or explicitly scoping out.
📌 Repo hygiene (unrelated to this diff)
CLAUDE.md:392 and :590 both say not to modify docs/api-reference/, and the "Generated files — never edit by hand" list (:21-27) doesn't mention organizations-openapi.json. #253–#256 have established the opposite in practice. One line naming organizations-openapi.json and openapi.json as hand-maintained mirrors of the NestJS Swagger output would stop the next contributor (or agent) either refusing to touch this file or clobbering it with a fresh export.
✅ Strengths
- The accuracy claim in the description genuinely holds — every field of all three schemas checked against source, including optionality and nullability, with no drift found.
- Correctly did not add branding/stripe fields to
UpdateOrganizationDto: the backend has none, so the asymmetry is faithful rather than an oversight. - The absent
requiredarrays onBrandingDto/OrganizationStripeConfigDtoare right, not sloppy — every backing field is@ApiPropertyOptional. - Modelling
metadataasobject+additionalProperties: trueis the correct call for a genuinely free-form payload, and strictly better for readers than the previous bareobject. - Measurably zero regression: 4 lint findings fixed, 0 introduced.
- The upstream PR pairs this with a regression guard (registering these DTOs in the
response-envelopes.spec.tsobject sweep), so this defect class shouldn't quietly return.
…ivitySubjectDto
Mirrors nvm-monorepo #2538 review fix: the activity `subject` is now an
inline open object (`{ kind, id }` + additionalProperties) matching its
description, instead of a `$ref` to a closed DTO that dropped the extra keys.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
Mirrors nevermined-io/nvm-monorepo#2538 — completes the Organizations OpenAPI object-type cleanup so no field renders as a bare
type: object.Adds 3 component schemas (
BrandingDto,OrganizationStripeConfigDto,OrganizationActivitySubjectDto) and re-types 10 fields:OrganizationMemberWithProfileDto.name/.stripeAccountIdobjectstring, nullableCustomerResponseDto.userName/.userEmailobjectstring, nullableOrganizationActivityEventResponseDto.actorUserIdobjectstring, nullableCreateOrganizationDto.brandingobject$ref BrandingDtoCreateOrganizationDto.live/.sandboxobject$ref OrganizationStripeConfigDtoOrganizationActivityEventResponseDto.subjectobject$ref OrganizationActivitySubjectDtoOrganizationActivityEventResponseDto.metadataobjectobject+additionalProperties(free-form)Field defs copied verbatim from the generated schemas (verified against the backend DTOs). All
$refs resolve; a full scan confirms zero structurelesstype: objectproperties remain in the spec.Test plan
$refs resolve; 89 schemas; 0 remaining baretype: object