Skip to content

feat(sdks): zod schemas for accounts and realms, and realms request bodies that match the service - #622

Merged
field123 merged 7 commits into
mainfrom
claude/issue-611-spec-c477fd
Sep 23, 2026
Merged

field123 merged 7 commits into
mainfrom
claude/issue-611-spec-c477fd

Conversation

@field123

@field123 field123 commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #621. Parent issue: #620.

Background

This repository publishes TypeScript SDK packages for the Elastic Path APIs. It generates the code in them from OpenAPI specifications, which are files that describe an API.

Elastic Path publishes its own copy of each specification, and a scheduled job copies those into this repository. When our copy must differ from the published one, the difference lives in an override file rather than in the specification. A refresh then cannot undo it.

This pull request makes three changes.

1. Zod schemas for two packages

Zod is a library that checks data at runtime. Some packages publish generated Zod schemas at a separate import path that ends in /zod.

  • @epcc-sdk/sdks-accounts did not publish Zod schemas. It now publishes them at @epcc-sdk/sdks-accounts/zod, set up the same way as @epcc-sdk/sdks-pricebooks.
  • @epcc-sdk/sdks-authentication-realms published /zod, but TypeScript could not find the types under moduleResolution: "node". The package now has a typesVersions map, so the import typechecks.

Zod is an optional peer dependency in both packages. You need it only if you import /zod. The main entry point of each package does not import Zod.

2. Request bodies that match the service

The authentication realms service checks each request body against a class in its own code. Create requests and update requests use different classes. The published specification describes both with one schema, so the generated types required the wrong fields.

  • An update required name. The service requires only type. A caller could not send a partial update.
  • Some create requests did not require fields that the service requires. A caller could send a request that the service rejects.

New overrides in packages/sdks/specs/overrides/authentication_realms_request_shapes.yaml give each operation the fields its class requires. Six schemas are added. No export is removed. The shopper SDK does not change, because it does not include these operations.

#609 fixed the paths and the type values for the same package. The first version of this pull request fixed those by editing the specification. The merge with the main branch replaced those edits with the ones from #609, which is the correct place for them.

3. Password reset example

The example in examples/shopper-accounts-authentication sent the text "shopper" as the realm identifier. The API requires the UUID of the realm, so the reset flow always failed. The example also used the wrong identifier for the user.

The example now reads the realm identifier from the account authentication settings of the store. The reset link carries the realm identifier and the user identifier from the one-time password event.

Testing

  • A test project installed both packages from pnpm pack output. The /zod import typechecks under moduleResolution set to node, bundler, node16 and nodenext. It loads with both import and require.
  • A script read the required fields of all 12 request bodies from the bundled specification. All 12 match the service.
  • Calls to a live store used a shopper token. Realm, OIDC profile and password profile responses parse with the Zod schemas.
  • Calls to a live test store used an admin token, through the SDKs. Each changed request body was sent for real. The realm update sent only type, and the store realm did not change. Password profiles, user authentication info, password profile info, OIDC profiles and OIDC profile info were each created, then updated with only the fields the new schemas require. The service accepted all of them. An OIDC profile create without client_secret and an OIDC profile info create without subject were rejected, as the schemas require. Every response parsed with its Zod schema. All test resources were deleted afterwards.
  • The password reset calls reached the correct endpoints. The token request used an invalid purpose value, so the API rejected it and created no token. The reset call returned 403, because it carried no member token.

A complete password reset passed against the live test store, with a throwaway shopper and a temporary webhook to the example app. The webhook reached the example's route, and its reset link carried the real realm, user and password profile info IDs. The example's own functions exchanged the one-time token and set a new password. The new password logged in, and the old one was rejected. The shopper and the webhook were deleted afterwards.

Note for the reviewer

The published specifications describe these request bodies incorrectly as well. A fix for them is in progress with the team that owns the authentication realms service (external-authentication.svc!668). When those land, the overrides here become unnecessary and somebody can remove them.

Squash this pull request when you merge it. Its history holds two commits that the merge with #609 replaced.

…types

sdks-accounts gains the pricebooks zod wiring: the zod plugin
(compatibilityVersion 3), a /zod entry, zod external in tsup, the ./zod
export, a typesVersions map, and zod as a devDependency plus an optional
^3.22.0 peer. The client is regenerated from the existing spec; only
zod.gen.ts is new. The root entry still never imports zod.

sdks-authentication-realms adds the missing typesVersions map and optional
zod peer, so /zod typechecks under moduleResolution "node".

Closes #621

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

6 Skipped Deployments
Project Deployment Actions Updated
commerce-essentials Ignored Ignored Preview Sep 23, 2026 5:59pm UTC
composable-frontend-algolia Ignored Ignored Sep 23, 2026 5:59pm UTC
composable-frontend-core Ignored Ignored Preview Sep 23, 2026 5:59pm UTC
composable-frontend-docs Ignored Ignored Preview Sep 23, 2026 5:59pm UTC
composable-frontend-simple Ignored Ignored Preview Sep 23, 2026 5:59pm UTC
composable-frontend-subscriptions Ignored Ignored Preview Sep 23, 2026 5:59pm UTC

Request Review

@changeset-bot

changeset-bot Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 92059e9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@epcc-sdk/sdks-accounts Minor
@epcc-sdk/sdks-authentication-realms Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

The realms spec was hand-written in #359 and never checked against the
service. Live calls and external-authentication.svc show four errors:

- OIDC profiles are served at /oidc-profiles, not /openid-connect-profiles.
- user-authentication-oidc-profile-info is nested under
  /user-authentication-info/{userAuthenticationInfoId}, not the realm.
- A realm's type is authentication-realm, not authentication_realm.
- An OIDC profile's type is oidc-profile, not openid_connect_profile.

The gateway had no route for either path, so those functions returned 404,
and the service's request validators reject the old type values. Operation
and schema names are unchanged, so no export moved. The shopper SDK joins
this spec and picks up the corrected types.

The divergence note claimed the opposite naming, so it is rewritten.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The request and resource schemas were hand-written in #359 and several
were invented. They now follow external-authentication.svc: the
*CreateData / *UpdateData validators for bodies, *ReadData for resources.

- Every body is wrapped in `data`. The one-time password token request
  was not, so the service rejected it with "The data field is required"
  (checked live). It gets OneTimePasswordTokenRequestWrapper.
- Updates are partial and require only `type` (plus `id` for password
  profile info). The realm update body lacked `type`, which the service
  requires.
- OIDC profiles use name, discovery_url, client_id and client_secret, not
  redirect_uris. OIDC profile info uses subject, issuer and
  oidc_profile_id, not username. Both response schemas required fields
  the service never sends.
- Password profiles and realms gain the fields the service returns
  (checked live) and lose the invented `description`.

No export is removed. The password reset example wraps its body to match.
Upstream canonical has the same POST/PUT required-field mismatch; reported
in commerce-cloud/external-authentication.svc issue 2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@field123 field123 changed the title feat(sdks): expose zod schemas from sdks-accounts and fix realms zod types fix(sdks): zod subpaths for accounts and realms; match realms spec to the service Sep 23, 2026
…-c477fd

# Conflicts:
#	packages/sdks/authentication-realms/src/client/index.ts
#	packages/sdks/authentication-realms/src/client/sdk.gen.ts
#	packages/sdks/authentication-realms/src/client/types.gen.ts
#	packages/sdks/authentication-realms/src/client/zod.gen.ts
#	packages/sdks/shopper/src/client/types.gen.ts
#	packages/sdks/specs/authentication-realms.yaml
#	packages/sdks/specs/config/canonical-map.json
#	packages/sdks/specs/patches/README.md
#	packages/sdks/specs/shopper.yaml
…rvice's shapes

The published spec sends each resource's own schema as both the create
and the update body, so both carry the resource's required list. The
service validates them separately (external-authentication.svc,
*CreateData / *UpdateData):

- Realm, OIDC profile, password profile and OIDC profile info updates
  are partial and require `type` alone. The OIDC profile info update
  takes no oidc_profile_id.
- OIDC profile create also requires discovery_url, client_id and
  client_secret. OIDC profile info create also requires subject and
  issuer.
- The realm update's duplicate_email_policy accepts allowed and
  api_only only.

These are redocly overrides, beside #609's, so a spec refresh keeps
them. Each is a full copy of the canonical schema, because
component-merge rejects refs into a component's properties and the
generator drops an allOf that adds only `required`.

Reported upstream as commerce-cloud/external-authentication.svc#2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@field123 field123 changed the title fix(sdks): zod subpaths for accounts and realms; match realms spec to the service feat(sdks): zod subpaths for accounts and realms; realms request bodies match the service Sep 23, 2026
The example sent `realmId: "shopper"`, but the realms API validates the
realm id as a UUID, so both the token request and the reset returned 422.
It also passed the password profile info id as the user authentication
info id.

- The token request reads the realm id from the store's account
  authentication settings.
- The webhook puts `authentication_realm_id` and
  `user_authentication_info.id` from the one-time password event into
  the reset link, and the page, form and action carry them through to
  the reset call.
- The reset sends only `id`, `type` and `password`; the update is
  partial.
- The unused `simulatePasswordResetEmail` is removed: it duplicated the
  webhook's link.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@field123 field123 changed the title feat(sdks): zod subpaths for accounts and realms; realms request bodies match the service feat(sdks): zod schemas for accounts and realms, and realms request bodies that match the service Sep 23, 2026
The realm, OIDC profile and password profile update handlers take an
optional `id` and reject one that does not match the path (checked
live). The update schemas now model it. The OIDC profile info update
leaves it out: its handler compares `id` with the user authentication
info ID, so the resource's own ID is rejected.

resetUserPassword in the password reset example takes an options object,
so its three IDs cannot be passed in the wrong order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@field123
field123 merged commit 58b870b into main Sep 23, 2026
7 checks passed
@field123
field123 deleted the claude/issue-611-spec-c477fd branch September 23, 2026 18:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose zod schemas from sdks-accounts and fix sdks-authentication-realms zod types

1 participant