feat(tokowaka-client): add CloudFront Optimize-at-Edge control-plane#1722
feat(tokowaka-client): add CloudFront Optimize-at-Edge control-plane#1722ABHA61 wants to merge 3 commits into
Conversation
Move the BYOCDN "Optimize at Edge" CloudFront control-plane into the shared client as free functions, re-exported from the package root: assume-role, distribution discovery, origin/routing-function/cache/Lambda@Edge provisioning, behavior association, routing verification, and the step-on-poll deploy orchestrator + dry-run plan. Adds @aws-sdk/client-sts, -iam, -lambda. Migrated verbatim from spacecat-api-service src/support/edge-optimize.js (+ edge-optimize-edge-code.js). Option A (Adobe-managed assume-role) only: drops cleanupHeaderValue; routing verification probes the real forwarded host. 100% line/branch/statement/function coverage; full package suite (938) passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
This PR will trigger a minor release when merged. |
| @@ -0,0 +1,149 @@ | |||
| /* | |||
| * Copyright 2026 Adobe. All rights reserved. | |||
There was a problem hiding this comment.
pls move all this to cloudfront folder.
There was a problem hiding this comment.
Shall i move to src/cdn/cloudfront/ ? and by means all this , all these changes? or just this lambda and function code?
…e create
When createEdgeOptimizeLambda creates the IAM exec role in the same call and the
function is still missing, return { status: 'provisioning', functionArn: null }
immediately instead of waiting ~12s for role propagation and then running
CreateFunction in the same request. The next poll (role now exists) performs the
create after the role has propagated. This keeps the first deploy poll well under
the CDN ~60s first-byte timeout, the root cause of the intermittent 503. Removes
the now-unused roleWaitMs option; tests updated, 100% coverage retained.
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Hey @ABHA61,
⚠ Degraded review - no spec document was found for this change (searched the PR links, the touched repos’ docs, the architecture/guidelines docs, and linked Jira). This review covers code-level quality but could not validate the change against an agreed design, so confidence is reduced. Add a spec link (PR template section 4) and re-request review for a full-confidence pass.
Verdict: Request changes - functional blocker (nodejs24.x runtime) and several reliability/correctness gaps that should be fixed before this ships to customers.
Complexity: HIGH - large diff; new dependency surface + cross-account AWS control-plane.
Changes: Moves the BYOCDN “Optimize at Edge” CloudFront control-plane into @adobe/spacecat-shared-tokowaka-client as free functions with full TypeScript declarations (7 files).
Must fix before merge
- [Critical]
nodejs24.xLambda runtime does not exist - will hard-fail every fresh Lambda@Edge deploy -src/edge-optimize/index.js:823(details inline) - [Important]
verifyEdgeOptimizeRoutinghas no fetch timeout - can hang the poll request and cause 503 from gateway -src/edge-optimize/index.js:1146(details inline) - [Important]
listCloudFrontDistributionsdoes not paginate - customers with 100+ distributions hang at propagation -src/edge-optimize/index.js:167(details inline) - [Important]
planEdgeOptimizeDeployreports "blocked" when already EO-associated - success state reads as error -src/edge-optimize/index.js:1710(details inline) - [Important] Template string injection in code generators - values interpolated into JS without escaping -
src/edge-optimize/edge-code.js:83(details inline) - [Important] Lambda code never updated once a published version exists - template fixes won’t reach existing customers -
src/edge-optimize/index.js:872(details inline)
Non-blocking (8): minor issues and suggestions
- nit: JSDoc for
createEdgeOptimizeRoutingFunctionmissingdistributionIdparam - source docs disagree with implementation -src/edge-optimize/index.js:378 - suggestion: Add URL scheme validation (
https://only) and metadata-endpoint blocklist toverifyEdgeOptimizeRoutingfor SSRF defense-in-depth -src/edge-optimize/index.js:1173 - suggestion: Scope the
logs:CreateLogGroupIAM resource fromarn:aws:logs:*:${accountId}:*to the specific log group path -src/edge-optimize/index.js:623 - nit:
EDGE_OPTIMIZE_CACHE_POLICY_NAMEexported but never used -src/edge-optimize/index.js:79 - suggestion: 1729-line
index.jsmixes IAM, Lambda, CF Function, cache policy, and orchestration concerns - consider splitting into focused submodules -src/edge-optimize/index.js - suggestion: Consider a
package.json"exports"map so consumers not using edge-optimize avoid loading 3 new AWS SDK clients -package.json - nit: Bot UA in verify probe is
'chatgpt-user'while routing function list uses'ChatGPT-User'- works via.toLowerCase()but may drift -src/edge-optimize/index.js:1177 - suggestion: Add a zip roundtrip decompression assertion in tests - hand-rolled binary format needs a decode-and-compare test -
test/edge-optimize/index.test.js:1017
Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 0m 23s | Cost: $8.71 | Commit: 7550bee85494f194260e9505d1e948ca1561f533
If this code review was useful, please react with 👍. Otherwise, react with 👎.
| for (let attempt = 0; attempt < 3; attempt += 1) { | ||
| try { | ||
| const created = await lambda.send(new LambdaCreateFunctionCommand({ | ||
| FunctionName: lambdaName, |
There was a problem hiding this comment.
issue (blocking): nodejs24.x Lambda runtime does not exist in AWS. Runtime: "nodejs24.x" will fail with InvalidParameterValueException on every fresh Lambda@Edge deployment. As of mid-2025, the latest Lambda@Edge-supported Node.js runtime is nodejs20.x (or nodejs22.x in some regions). Why it matters: This is a hard functional failure - no customer can onboard until this is fixed. Fix: Replace with nodejs20.x (current LTS, broadly supported for Lambda@Edge). Consider extracting it to a named constant.
| } | ||
|
|
||
| async function fetchEdgeOptimizeHeaders(url, userAgent) { | ||
| const response = await fetch(url, { |
There was a problem hiding this comment.
issue (blocking): No timeout on outbound fetch calls in fetchEdgeOptimizeHeaders. Both fetch calls have no AbortController/signal timeout. If the customer distribution is still deploying or has a non-responsive origin, fetch can hang indefinitely. Since the orchestrator sits behind a CDN/gateway with a ~60s first-byte timeout, a single hung probe causes a 503. Fix: Add an AbortController with a 10-15s timeout per probe. Treat a timeout as passed: false so the next poll retries.
| * @returns {Promise<Array<object>>} distributions projected to the fields the wizard needs. | ||
| */ | ||
| export async function listCloudFrontDistributions(credentials, region = EDGE_OPTIMIZE_REGION) { | ||
| const client = new CloudFrontClient({ region, credentials }); |
There was a problem hiding this comment.
issue (blocking): listCloudFrontDistributions does not paginate. ListDistributionsCommand({}) returns at most 100 items per call. The propagation step uses this to find the distribution by ID - customers with 100+ distributions will hang at "waiting for the distribution to appear" forever. Fix: Either paginate with NextMarker/IsTruncated, or replace the propagation list-and-find with a direct GetDistribution call which returns Status directly.
| const assocBehavior = config ? getBehaviorFromConfig(config, behavior) : null; | ||
| const assocConflict = assocBehavior | ||
| ? findEdgeOptimizeAssociationConflict(assocBehavior, behavior) : null; | ||
| if (await isBehaviorAlreadyAssociated(client, distributionId, behavior)) { |
There was a problem hiding this comment.
issue (blocking): planEdgeOptimizeDeploy reports canProceed: false when the behavior is already fully EO-associated. A customer who completed the wizard will see a "blocked" state when they re-run the plan. The deploy orchestrator correctly handles this (marks associate: done), but the plan endpoint rejects it. Fix: Return canProceed: true with steps[associate].action = "exists" when all associations are EO-owned. Keep the blocker only for the conflict case.
|
|
||
| console.log("Routing to Default origin for userAgent: " + userAgent); | ||
| return request; | ||
| }`; |
There was a problem hiding this comment.
issue (blocking): Template string injection - defaultOriginId interpolated into generated JS without escaping. The value is interpolated directly into JavaScript source that becomes a CloudFront Function. Same applies to eoOriginDomain in buildEdgeOptimizeLambdaCode. Why it matters: Defense-in-depth at a code-generation trust boundary. Fix: Validate with a strict character set before interpolation, or use JSON.stringify(value) for proper escaping.
| } | ||
|
|
||
| // Active and idle. If a numbered version already exists, reuse it (idempotent). | ||
| const existingVersion = await getLatestLambdaVersion(lambda, lambdaName); |
There was a problem hiding this comment.
issue (blocking): Lambda code is never updated once a published version exists. Once getLatestLambdaVersion finds a numbered version, createEdgeOptimizeLambda returns alreadyExisted: true without comparing the code hash. If the template changes, redeploying to an existing customer will never update their Lambda code. Fix: Compute the expected CodeSha256 from the zip buffer, compare to existingVersion.codeSha256, and if they differ, call UpdateFunctionCode + PublishVersion.
Warning
DO NOT MERGE — review only. Shared-client half of the CloudFront
"Optimize at Edge" control-plane migration. This must merge (and publish a
new client version) before the paired
spacecat-api-servicePR can bumpto the published version.
Paired PR: adobe/spacecat-api-service#2682
What
Moves the BYOCDN "Optimize at Edge" CloudFront control-plane into
@adobe/spacecat-shared-tokowaka-clientas free functions, re-exported from thepackage root.
Why
The control-plane currently lives in
spacecat-api-service/src/support/edge-optimize.js.Moving it into the shared client (which already ships the AWS-SDK CloudFront
client) lets it be reused and keeps the api-service controller thin.
Changes
src/edge-optimize/index.js— moved control plane: assume-role, distributiondiscovery, origin / routing-function / cache / Lambda@Edge provisioning,
behavior association, routing verification, and the step-on-poll deploy
orchestrator + dry-run plan.
src/edge-optimize/edge-code.js— moved Lambda@Edge + CloudFront-Functionsource builders (verbatim).
src/index.js/src/index.d.ts— re-export 12 functions + builders +constants; full typings added.
package.json— adds@aws-sdk/client-sts,-iam,-lambda.Option A only
Drops
cleanupHeaderValue(helix dep); routing verification probes the realforwarded host (the demo "verify-by-distribution" hack is not carried over).
Tests
100% line/branch/statement/function coverage on the moved code; full package
suite (938 tests) passes.
🤖 Generated with Claude Code