feat: v2 for create preflight job to support session token#1887
feat: v2 for create preflight job to support session token#1887
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
This PR will trigger a minor release when merged. |
| $ref: './project-api.yaml#/project-sites-by-name' | ||
| /preflight/jobs: | ||
| $ref: './preflight-api.yaml#/preflight-jobs' | ||
| /preflight/jobs-v2: |
There was a problem hiding this comment.
This versioning feels really off from industry standards. The suggestion from the api first group was that the existing api should be backwards compatible. Why do we need to introduce a new api here. If it is mandatory then I would consider /v2/preflight/jobs. This is a larger question for @solaris007 and how spacecat apis should be versioned.
If the presence of x-promise-token is the header, the could we not take that path in the code and keep the /preflight/jobs endpoint as it is.
There was a problem hiding this comment.
Strongly agree. The backward-compatible approach is the right call here - details in the PR review.
| - api_key: [ ] | ||
| - ims_key: [ ] | ||
| summary: Submit a new preflight async job | ||
| summary: Submit a new preflight async job (deprecated) |
There was a problem hiding this comment.
Not sure if we really want to deprecate this or make the api backwards compatible.
| const promiseToken = context.request?.headers?.get?.('x-promise-token'); | ||
| if (!hasText(promiseToken)) { | ||
| return badRequest('x-promise-token header is required and must be a non-empty string'); | ||
| } |
There was a problem hiding this comment.
Why not just look for this logic in createPreflightJob above?
solaris007
left a comment
There was a problem hiding this comment.
Hey @ravverma,
Thanks for tackling the promise token handoff - moving away from the server-side IMS exchange toward a client-supplied token is a reasonable direction. However, I have concerns about the approach taken here. Agreeing with @bhellema's comments - details below.
API Versioning
The /preflight/jobs-v2 URL suffix is a non-standard versioning pattern. Industry conventions for API versioning (when actually warranted) are path-prefix (/v2/preflight/jobs), header-based (Accept: application/vnd.adobe.v2+json), or query-param. The suffix approach doesn't scale (-v3? -v4?) and breaks URL semantics.
More importantly, versioning isn't warranted here at all. The request/response contract is identical between v1 and v2 - same request body, same 202 response shape. The only difference is an implementation detail: where the promise token comes from. This is a textbook case for backward-compatible evolution, not a new endpoint.
Code Duplication
createPreflightJobV2 is ~107 lines nearly identical to createPreflightJob. The actual behavioral delta is ~7 lines (header read + token assignment). Everything else - validateRequestData, site lookup, HEAD probe, AsyncJob.create, SQS message construction, rollback on SQS failure, poll URL formatting - is character-for-character identical. This means every future bug fix or enhancement to the preflight flow must be applied in two places.
The 13 new tests similarly mirror the existing v1 suite, re-testing the same shared business logic through a different entry point.
Token Shape Mismatch
The v1 flow passes the full IMS response object to SQS:
// v1: from IMS
{ promise_token, expires_in, token_type }The v2 flow constructs only:
// v2: manual wrap
{ promise_token: promiseToken }If the downstream audit worker reads expires_in or token_type, v2 will silently break it. This needs to be verified before merging.
Encryption Mismatch
In v1, ImsPromiseClient.getPromiseToken() can optionally encrypt the promise token before it goes to SQS (controlled by AUTOFIX_CRYPT_SECRET / AUTOFIX_CRYPT_SALT). In v2, the raw header value goes directly into the SQS message without encryption.
If the audit worker environment has encryption enabled, the consumer will attempt to decrypt the raw (unencrypted) token and fail. This likely breaks v2 entirely in environments where encryption is configured.
Token Required for Non-Promise-Based Sites
v2 requires x-promise-token for ALL requests, but only uses it for promise-based authoring types (CS, CS_CW, AMS). For non-promise-based sites, the token is validated as present but discarded. This forces callers who don't need a promise token to supply one anyway - a confusing API contract that violates least-privilege.
Recommendation
Adopt the backward-compatible approach @bhellema suggested. Detect the x-promise-token header in the existing handler and branch:
if (promiseBasedTypes.includes(site.getAuthoringType())) {
const headerToken = context.request?.headers?.get?.('x-promise-token');
if (hasText(headerToken)) {
promiseTokenResponse = { promise_token: headerToken };
} else {
promiseTokenResponse = await getIMSPromiseToken(context);
}
}This eliminates the new endpoint, the deprecation, ~100 lines of duplicated code, the duplicated OpenAPI spec, and most of the duplicated tests. Net change should be well under 30 lines, plus 2-3 new test cases for the header-present path.
Before any approach is merged, the encryption and token shape questions above need answers from whoever owns the audit worker consumer side.
|
@solaris007 @bhellema Thanks, indeed it was lame approach :) |
Please also take another iteration on #1863 |
Summary
Add
POST /preflight/jobs-v2endpoint (createPreflightJobV2) that reads the promise token from thex-promise-tokenrequest header instead of creating one from the Authorization header via IMS. Mark the existingPOST /preflight/jobsas deprecated.What changed
src/controllers/preflight.jscreatePreflightJobV2— validatesx-promise-tokenheader upfront (early exit), forwards it to SQS for promise-based authoring types (CS, CS_CW, AMS). Rest of the logic is identical to v1.createPreflightJobas@deprecated.src/routes/index.js— AddedPOST /preflight/jobs-v2route.docs/openapi/preflight-api.yamlPOST /preflight/jobsasdeprecated: truewith deprecation notice.POST /preflight/jobs-v2with requiredx-promise-tokenheader parameter.docs/openapi/api.yaml— Added path reference for/preflight/jobs-v2.test/controllers/preflight.test.js— Added 13 unit tests forcreatePreflightJobV2covering all code paths (missing/empty header, all authoring types, site lookup, auth flag, CI/prod URL, error handling, SQS rollback).test/routes/index.test.js— Added mock and route key for v2.Key differences from v1
POST /preflight/jobs(deprecated)POST /preflight/jobs-v2Authorizationheader via IMSx-promise-tokenheaderAuthorization(only for promise-based sites)x-promise-token(required for all requests)getIMSPromiseToken)Test plan
npm run docs:lint)npm run docs:build)