feat!: Generate organizations APIs using oagen#1628
Conversation
Greptile SummaryThis PR regenerates the Organizations API module from the OpenAPI spec via oagen, introducing breaking signature changes (positional → object params,
Confidence Score: 3/5The update path has a data-loss bug: any partial org update will silently clear metadata and externalId on the server. Needs fixes before merging. The serializeUpdateOrganization serializer defaults omitted metadata and externalId to null instead of omitting them, so a routine call like updateOrganization({ id, name }) will send metadata: null to the API and wipe existing org metadata. A parallel issue removes null support from stripeCustomerId, making it impossible to clear the Stripe customer ID that was previously a tested feature. src/organizations/serializers/update-organization.serializer.ts and src/organizations/interfaces/update-organization.interface.ts need fixes before this ships. Important Files Changed
|
| metadata: model.metadata ?? null, | ||
| external_id: model.externalId ?? null, |
There was a problem hiding this comment.
Null defaults silently clear existing org data on every partial update
metadata: model.metadata ?? null and external_id: model.externalId ?? null mean that whenever a caller omits those fields — e.g. updateOrganization({ id, name: 'New Name' }) — the serializer emits "metadata": null and "external_id": null in the JSON body. The old test confirms the WorkOS API interprets explicit null as "clear this field" (see the deleted stripe_customer_id: null test that verified subject.stripeCustomerId became undefined). The previous serializer passed metadata: options.metadata, which serialized to undefined and was dropped by JSON.stringify, leaving server state unchanged. The fix is to pass the raw values without the ?? null fallback so unset fields are omitted from the request body entirely.
| /** The domains associated with the organization, including verification state. */ | ||
| domainData?: OrganizationDomainData[]; | ||
| /** The Stripe customer ID associated with the organization. */ | ||
| stripeCustomerId?: string; |
There was a problem hiding this comment.
stripeCustomerId: null support silently dropped
stripeCustomerId?: string no longer accepts null, which was the documented mechanism to clear a Stripe customer ID (updateOrganization({ organization: id, stripeCustomerId: null })). The deleted test 'clears the organization's Stripe customer ID with a null value' proves this was a supported, tested flow. Users who rely on clearing the Stripe customer ID will now get a TypeScript error and have no API alternative. The type should remain stripeCustomerId?: string | null, and the corresponding UpdateOrganizationResponse.stripe_customer_id wire type on line 33 needs the same fix.
| * @throws {ConflictException} 409 | ||
| * @throws {UnprocessableEntityException} 422 | ||
| */ | ||
| async createOrganization( | ||
| payload: CreateOrganizationOptions, | ||
| requestOptions: CreateOrganizationRequestOptions = {}, | ||
| options: CreateOrganizationOptions, | ||
| ): Promise<Organization> { | ||
| const { data } = await this.workos.post<OrganizationResponse>( | ||
| '/organizations', | ||
| serializeCreateOrganizationOptions(payload), | ||
| requestOptions, | ||
| ); | ||
|
|
||
| const payload = options; | ||
| const { data } = await this.workos.post< |
There was a problem hiding this comment.
Idempotency key support removed from
createOrganization
The previous signature accepted a second requestOptions: CreateOrganizationRequestOptions parameter carrying an idempotencyKey. This was the only safe way to retry a createOrganization call without risk of creating duplicate organizations on network failure. The PR drops this parameter entirely and removes the corresponding test. Is this intentional? If so, is there a replacement mechanism? Without idempotency key support, callers can no longer safely retry failed creates.
| /** The current state of the Audit Log Stream. */ | ||
| state: AuditLogConfigurationLogStreamState; | ||
| /** ISO-8601 timestamp of when the last event was successfully synced, or null if no events have been synced. */ | ||
| lastSyncedAt: string | null; |
There was a problem hiding this comment.
lastSyncedAt stays a raw string while createdAt is a Date
createdAt: Date is deserialized via new Date(response.created_at) in the log-stream serializer, but lastSyncedAt: string | null is passed through as-is. Both fields hold ISO 8601 timestamps; keeping one as a raw string while the other is a Date is inconsistent and surprising for consumers. Consider converting lastSyncedAt to Date | null for consistency.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Description
This PR generates the Organizations category in the Node SDK from the OpenAPI spec.
Several of the methods had their signatures changed:
As well, the following output responses changed:
createdAt/updatedAtare now Date objects, not ISO strings.as JSON, or comparing it as a string will break at runtime.
allowProfilesOutsideOrganizationwent from requiredbooleanto optional (boolean | undefined).externalIdandmetadataare now optional on the typeBecause of this, the PR should be treated as a breaking change.