Skip to content

feat!: generate OrganizationDomains off the OpenAPI spec#1620

Open
gjtorikian wants to merge 2 commits into
mainfrom
oagen/own-organization-domains
Open

feat!: generate OrganizationDomains off the OpenAPI spec#1620
gjtorikian wants to merge 2 commits into
mainfrom
oagen/own-organization-domains

Conversation

@gjtorikian

Copy link
Copy Markdown
Contributor

Description

This PR generates the OrganizationDomain category in the Node SDK from the OpenAPI spec.

Three of the four methods switched from a positional id: string to an options object:

┌──────────────────────────┬────────────────────────────────────────────┬────────────────────────────────────────────┐
│          Method          │                   Before                   │                   After                    │
├──────────────────────────┼────────────────────────────────────────────┼────────────────────────────────────────────┤
│ getOrganizationDomain    │ (id: string)                               │ (options: { id: string })                  │
├──────────────────────────┼────────────────────────────────────────────┼────────────────────────────────────────────┤
│ verifyOrganizationDomain │ (id: string)                               │ (options: { id: string })                  │
├──────────────────────────┼────────────────────────────────────────────┼────────────────────────────────────────────┤
│ deleteOrganizationDomain │ (id: string)                               │ (options: { id: string })                  │
├──────────────────────────┼────────────────────────────────────────────┼────────────────────────────────────────────┤

As well, the following output responses changed:

  • createdAt / updatedAt: string → Date. The serializer now does new Date(response.created_at). Anyone treating these as ISO strings (e.g. string ops, direct JSON passthrough) will break.
  • state and verificationStrategy are now optional (state?, verificationStrategy?) — they can be undefined where before they were guaranteed present.

Because of this, the PR should be treated as a breaking change.

oagen now owns OrganizationDomains: timestamps are typed Date (not string)
and the response-only model emits a deserializer only (no
serializeOrganizationDomain). Update the hand-owned modules that embed the
model so the SDK builds and tests pass:

- organization-domain-verification-failed.serializer.ts: drop the dead
  serialize path that depended on the removed serializeOrganizationDomain
- events.spec.ts: assert organization_domain event timestamps as Date
- organizations get-organization fixture + spec: add the spec-required
  organization_id/created_at/updated_at to the embedded domain

These files are hand-owned (not in .oagen-manifest.json); committing them
keeps the reconciliation from being wiped by the regen flow's `git restore`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gjtorikian gjtorikian requested review from a team as code owners June 16, 2026 17:59
@gjtorikian gjtorikian requested a review from mattgd June 16, 2026 17:59
@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR migrates the OrganizationDomains category to be generated from the OpenAPI spec via oagen. It is intentionally breaking: three methods switch from a positional id: string to an options object, createdAt/updatedAt change from string to Date, and state/verificationStrategy become optional.

  • getOrganizationDomain and verifyOrganizationDomain now return OrganizationDomainStandAlone (a new interface), while createOrganizationDomain continues to return OrganizationDomain — these two types are parallel but structurally incompatible in TypeScript despite representing the same API resource.
  • Domain IDs are now URI-encoded via encodeURIComponent before being interpolated into request paths, which is a correctness improvement.
  • Downstream serializers (organization.serializer.ts, event.serializer.ts) and their tests are updated consistently to expect Date values for timestamps.

Confidence Score: 5/5

Safe to merge; all intentional breaking changes are well-documented in the PR description and tests have been updated consistently across the affected call sites.

The production code paths — serializers, HTTP method/URL construction, and downstream event/organization serializers — are all correctly updated and covered by tests. The only defects found are in auto-generated smoke tests that pass trivially without validating actual mapping correctness.

src/organization-domains/serializers.spec.ts — the CreateOrganizationDomainSerializer test uses a mismatched fixture and would not catch a regression in organizationId mapping.

Important Files Changed

Filename Overview
src/organization-domains/organization-domains.ts Core service class regenerated; methods switched from positional id: string to options objects, getOrganizationDomain and verifyOrganizationDomain now return OrganizationDomainStandAlone instead of OrganizationDomain, and IDs are now URI-encoded.
src/organization-domains/serializers/organization-domain.serializer.ts Simplified to only deserializeOrganizationDomain; createdAt/updatedAt now converted to Date; optional fields always assigned (keys present with undefined) vs. previously absent — minor behavioral difference for consumers using in operator.
src/organization-domains/serializers/organization-domain-stand-alone.serializer.ts New serializer for the stand-alone response shape; correctly maps snake_case to camelCase and converts timestamps to Date.
src/organization-domains/serializers.spec.ts New serializer smoke-test file; the CreateOrganizationDomainSerializer test passes snake_case fixture data via as any to a camelCase-expecting serializer, causing organization_id to serialize as undefined while the toBeDefined() assertion still passes.
src/organization-domains/interfaces/organization-domain.interface.ts state and verificationStrategy made optional; createdAt/updatedAt changed from string to Date; enums extracted to dedicated files.
src/organization-domains/interfaces/organization-domain-stand-alone.interface.ts New interface for GET/verify response shape; optional fields correctly marked and timestamps typed as Date.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[SDK Caller] -->|createOrganizationDomain| B[serializeCreateOrganizationDomain]
    B -->|POST /organization_domains| C[WorkOS API]
    C -->|OrganizationDomainResponse| D[deserializeOrganizationDomain]
    D -->|OrganizationDomain| A

    A -->|getOrganizationDomain| E[GET /organization_domains/id]
    E -->|OrganizationDomainStandAloneResponse| F[deserializeOrganizationDomainStandAlone]
    F -->|OrganizationDomainStandAlone| A

    A -->|verifyOrganizationDomain| G[POST /organization_domains/id/verify]
    G -->|OrganizationDomainStandAloneResponse| F

    A -->|deleteOrganizationDomain| H[DELETE /organization_domains/id]
    H -->|204 void| A
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[SDK Caller] -->|createOrganizationDomain| B[serializeCreateOrganizationDomain]
    B -->|POST /organization_domains| C[WorkOS API]
    C -->|OrganizationDomainResponse| D[deserializeOrganizationDomain]
    D -->|OrganizationDomain| A

    A -->|getOrganizationDomain| E[GET /organization_domains/id]
    E -->|OrganizationDomainStandAloneResponse| F[deserializeOrganizationDomainStandAlone]
    F -->|OrganizationDomainStandAlone| A

    A -->|verifyOrganizationDomain| G[POST /organization_domains/id/verify]
    G -->|OrganizationDomainStandAloneResponse| F

    A -->|deleteOrganizationDomain| H[DELETE /organization_domains/id]
    H -->|204 void| A
Loading

Reviews (2): Last reviewed commit: "Autogenerate OrganizationDomain" | Re-trigger Greptile

Comment on lines +32 to 41
async createOrganizationDomain(
options: CreateOrganizationDomainOptions,
): Promise<OrganizationDomain> {
const payload = options;
const { data } = await this.workos.post<
OrganizationDomainResponse,
CreateOrganizationDomainResponse
>('/organization_domains', serializeCreateOrganizationDomain(payload));
return deserializeOrganizationDomain(data);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Inconsistent return type across CRUD methods

createOrganizationDomain returns Promise<OrganizationDomain> (deserialized via deserializeOrganizationDomain), while getOrganizationDomain and verifyOrganizationDomain return Promise<OrganizationDomainStandAlone>. Despite representing the same API resource, these two interfaces are TypeScript-incompatible: OrganizationDomain.state is typed as the OrganizationDomainState string enum, while OrganizationDomainStandAlone.state uses the OrganizationDomainStandAloneState const-object type. A user who writes a helper function handle(d: OrganizationDomainStandAlone) and passes the result of createOrganizationDomain to it will receive a TypeScript compile error, even though both values are structurally identical at runtime.

Comment on lines +1 to +9
// This file is auto-generated by oagen. Do not edit.

export enum OrganizationDomainState {
Failed = 'failed',
Pending = 'pending',
Verified = 'verified',
LegacyVerified = 'legacy_verified',
Unverified = 'unverified',
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Duplicate parallel type hierarchies on the public SDK surface

OrganizationDomainState (string enum, 5 members) and OrganizationDomainStandAloneState (const object type, identical 5 members) represent the same domain concept with the same runtime string values. The same duplication exists for OrganizationDomainVerificationStrategy (enum) vs OrganizationDomainStandAloneVerificationStrategy (const type). Because TypeScript string enums are not mutually assignable with string-literal union types, code that holds the two representations side-by-side will require explicit casts, even though the underlying strings are identical. Consolidating to a single representation per concept would eliminate the ambiguity.

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!

Comment on lines +1 to +14
// This file is auto-generated by oagen. Do not edit.

export * from './create-organization-domain-options.interface';
export * from './organization-domain.interface';
export * from './create-organization-domain.interface';
export * from './delete-organization-domain-options.interface';
export * from './get-organization-domain-options.interface';
export * from './organization-domain-stand-alone-state.interface';
export * from './organization-domain-stand-alone-verification-strategy.interface';
export * from './organization-domain-stand-alone.interface';
export * from './organization-domain-state.interface';
export * from './organization-domain-verification-failed.interface';
export * from './organization-domain-verification-strategy.interface';
export * from './organization-domain.interface';
export * from './verify-organization-domain-options.interface';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Non-manifest interface file may be dropped on the next oagen regeneration

interfaces/index.ts (listed in the oagen manifest) currently exports organization-domain-verification-failed.interface.ts, but that interface file itself is not in the manifest. If oagen regenerates index.ts it will only emit entries for files it tracks, silently dropping the export * from './organization-domain-verification-failed.interface' line. organization-domain-verification-failed.serializer.ts imports its types from this barrel and would fail to compile after such a regeneration. Either add the verification-failed interface to the manifest, or import it directly in the serializer rather than through the auto-generated barrel.

@gjtorikian gjtorikian force-pushed the oagen/own-organization-domains branch from 9da89e3 to 7031028 Compare June 16, 2026 18:11
@gregsabo gregsabo requested a review from kevinferri June 16, 2026 23:47
OrganizationDomain,
OrganizationDomainResponse,
} from './interfaces';
import { serializeCreateOrganizationDomainOptions } from './serializers/create-organization-domain-options.serializer';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is serializeCreateOrganizationDomainOptions dead code now?

} from '../common/utils/test-utils';
import { WorkOS } from '../workos';
import getOrganizationDomainPending from './fixtures/get-organization-domain-pending.json';
import getOrganizationDomainVerified from './fixtures/get-organization-domain-verified.json';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are getOrganizationDomainPending and getOrganizationDomainVerified dead code now?

// This file is auto-generated by oagen. Do not edit.

import type { WorkOS } from '../workos';
import type { CreateOrganizationDomainOptions } from './interfaces/create-organization-domain-options.interface';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serializeCreateOrganizationDomain takes the generated CreateOrganizationDomain, but this method types its input as the hand-owned CreateOrganizationDomainOptions which has the same shape ({ domain, organizationId }), so we now carry two interfaces for one input type (and SerializedCreateOrganizationDomainOptions is now fully orphaned). Can the client use CreateOrganizationDomain directly so we can delete create-organization-domain-options.interface.ts?

const fixture =
createOrganizationDomainFixture as CreateOrganizationDomainResponse;
const serialized = serializeCreateOrganizationDomain(fixture as any);
expect(serialized).toBeDefined();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we assert the actual output here? toBeDefined() would pass even if the mapping were off. The fixture is snake_case (organization_id) but the serializer reads model.organizationId, so serialized.organization_id ends up undefined.

@gjtorikian gjtorikian added the autogenerated Autogenerated code or content label Jun 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autogenerated Autogenerated code or content

Development

Successfully merging this pull request may close these issues.

2 participants