feat(sdks): zod schemas for accounts and realms, and realms request bodies that match the service - #622
Merged
Conversation
…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>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 6 Skipped Deployments
|
🦋 Changeset detectedLatest commit: 92059e9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
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>
…-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>
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>
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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-accountsdid 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-realmspublished/zod, but TypeScript could not find the types undermoduleResolution: "node". The package now has atypesVersionsmap, 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.
name. The service requires onlytype. A caller could not send a partial update.New overrides in
packages/sdks/specs/overrides/authentication_realms_request_shapes.yamlgive 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
typevalues 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-authenticationsent 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
pnpm packoutput. The/zodimport typechecks undermoduleResolutionset tonode,bundler,node16andnodenext. It loads with bothimportandrequire.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 withoutclient_secretand an OIDC profile info create withoutsubjectwere rejected, as the schemas require. Every response parsed with its Zod schema. All test resources were deleted afterwards.purposevalue, 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.