fix: skip missing API/group links in gateway/product associations instead of aborting - #267
Aleksey Zheltov (Alexey-Zheltov) wants to merge 18 commits into
Conversation
Co-authored-by: azaslonov <2320302+azaslonov@users.noreply.github.com>
Co-authored-by: azaslonov <2320302+azaslonov@users.noreply.github.com>
…tings PUT, and current-revision extract duplication Bug 1 — Cannot delete the current revision: - apim-client.ts: deleteResource appends &deleteRevisions=true for base API deletes (id without ;rev=N), removing the API and all revisions in one call instead of erroring on the current revision. - publish-service.ts: filterRevisionDeletesHandledByBaseApi strips individual ;rev=N deletes when the base API is also being deleted. Bug 2 — policy fragment referenced by service policy: - apim-client.ts: deleteResource treats 'is used by the following entities' as non-fatal — resource is logged WARN and marked 'skipped' instead of failing the run. Bug 3 (Closes Azure#258) — extract mishandles APIs whose current revision is not rev=1: - api-extractor.ts: extractApiRevisions skips the current revision via isCurrent === true instead of hard-coding revision number '1'. - Renamed/updated test to explicitly cover current=2, non-current=1, asserting the non-current revision (;rev=1) is what gets extracted. Bug 4 — root API create collides with source ;rev=N artifact: - api-publisher.ts: resolveRootApiPutDescriptor detects when the source's current revision number is > 1 and the API does not yet exist on the target; in that case the root is PUT at apis/{name};rev=N instead of apis/{name} (which APIM always creates as revision 1), preventing it from colliding with and silently absorbing the real ;rev=1 artifact. Existing APIs keep the plain root PUT since their current revision cannot be renumbered. - api-publisher.ts: publishApiRevisions now throws when a revision publish fails, instead of silently swallowing the error and returning exit code 0. Bug 5 — authenticationSettings PUT rejected when legacy and collection fields are both present: - resource-publisher.ts: normalizeApiAuthenticationSettings drops the legacy oAuth2/openid fields when the newer oAuth2AuthenticationSettings/openidAuthenticationSettings collections are non-empty (APIM GET returns both, but PUT rejects the combination), keeping the singular fields only when the collections are empty. Verified via unit tests (new cases added for all scenarios) and manual dry-run/live extract+publish against dev-apim-uk-01 with a multi-revision API (webapitest;rev=1, ;rev=2).
…ce scope Addresses Copilot review: filterRevisionDeletesHandledByBaseApi is now shared via delete-unmatched-service, applied to both dry-run delete paths (incremental and delete-unmatched), and keys base APIs by (workspace, name).
Revision creation via sourceApiId copies apiRevisionDescription from the current revision; send an explicit empty string when the artifact omits it. properties.description is never sent for ;rev=N PUTs (APIM rejects Description changes on non-current revisions); an explicit override of it now logs a warning instead of being silently dropped.
…root auth normalization - resolveRootApiPutDescriptor checks API existence via the deployed (env-mapped) name and appends ;rev=N after affixing so suffix mappings cannot corrupt the revision suffix - root API normalization passes preferLegacyFields (same explicit-override signal as publishResource) so root and revision APIs honor authenticationSettings overrides consistently - add regression tests: revision publish failure propagates to a failed publishApi result; env-mapped existence check and rev suffix placement
…n publishApi Derive the env-mapped base descriptor once and use it for the existence check, root PUT, ;rev=N fresh-create target, revision alignment PUT, and imported-operation reconciliation. Previously only the fresh rev>1 branch was mapped, so publishes with envMapping could create or align an un-affixed API alongside the mapped one.
…e precedence - env-mapper affixes the base API name and re-appends ;rev=N in both directions (toDeployedName/toCanonicalName/isInEnvNamespace), fixing revision PUT targets and delete-unmatched canonicalization under suffix mappings - prefersLegacyAuthOverride inspects which authenticationSettings representation the override explicitly supplies; collection and metadata-only overrides keep collection precedence
… key, revision-collision guard, tests
…hed revision count
There was a problem hiding this comment.
🔵 Needs a closer look
It contains broad publishing changes and an unresolved authentication override issue that can prevent explicit clearing.
Pull request overview
Fixes missing gateway/product association handling while also changing API revision publishing, authentication normalization, deletion, mapping, and retry behavior.
Changes:
- Skips missing API/group links while continuing valid associations.
- Revises API revision publishing, mapping, extraction, and deletion.
- Adds authentication normalization, conflict retries, and expanded tests.
File summaries
| File | Description |
|---|---|
tests/unit/services/resource-publisher.test.ts |
Tests associations, authentication, and revision payloads. |
tests/unit/services/publish-service.test.ts |
Tests publishing order and revision deletion. |
tests/unit/services/env-mapper.test.ts |
Tests revision-aware environment mapping. |
tests/unit/services/dry-run-reporter.test.ts |
Tests filtered dry-run deletions. |
tests/unit/services/delete-unmatched-service.test.ts |
Tests revision deletion deduplication. |
tests/unit/services/api-publisher.test.ts |
Tests revision and mapped publishing behavior. |
tests/unit/services/api-product-extractor.test.ts |
Tests current-revision extraction behavior. |
tests/unit/clients/apim-client.test.ts |
Tests retries and deletion behavior. |
src/services/resource-publisher.ts |
Handles missing links and normalizes API payloads. |
src/services/publish-service.ts |
Reorders publishing and filters revision deletions. |
src/services/env-mapper.ts |
Preserves revision suffixes during mapping. |
src/services/dry-run-reporter.ts |
Aligns deletion previews with execution. |
src/services/delete-unmatched-service.ts |
Prevents redundant revision deletions. |
src/services/api-publisher.ts |
Changes revision creation and mapped publishing. |
src/services/api-extractor.ts |
Excludes the current API revision from child artifacts. |
src/lib/resource-path.ts |
Adds API revision-name helpers. |
src/clients/apim-client.ts |
Adds conflict retries and deletion handling. |
package-lock.json |
Updates the fast-uri dependency. |
Review details
- Files reviewed: 17/18 changed files
- Comments generated: 2
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The built-in 'managed' gateway is not returned by GET /gateways, so its per-API assignments were never extracted, published, or reconciled. This left dev/prod divergence (e.g. an API removed from managed in dev remained assigned in prod). - extract: query gateways/managed/apis and write gateways/managed/apis.json (only when non-empty) - publish: skip PUT of the built-in managed Gateway resource (its GatewayApi associations still publish) - delete-unmatched: reconcile GatewayApi assignments (incl. managed), scoped to gateways that own a local assignment, so untracked gateways are never touched - add MANAGED_GATEWAY_NAME constant; tests for extract/publish/delete-unmatched
computeGatewayApiDeleteActions compared deployed [gateway,api] descriptors against localSet, which only holds aggregate GatewayApi [gateway] keys (api names live in apis.json content, not the path). Every deployed assignment therefore missed the set and was deleted regardless of desired state - e.g. an API present in the gateway's apis.json was both PUT and DELETEd. Read the desired API set per gateway via store.readAssociation and delete only deployed assignments absent from it. Update tests to use aggregate local descriptors + readAssociation.
The merge-base changed after approval.
The merge-base changed after approval.
The merge-base changed after approval.
The merge-base changed after approval.
There was a problem hiding this comment.
🟡 Changes recommended
Critical managed-gateway mapping and reconciliation defects, plus unresolved extraction and scope issues, block approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
src/services/api-publisher.ts:75
- The PR title/body describe missing association handling, but this change also alters fresh API revision creation, and the branch additionally changes authentication payloads, deletion semantics, managed-gateway reconciliation, publish ordering, HTTP retries, and a dependency lock. These materially different behaviors need to be documented in the PR description or split into separately scoped PRs so their rollout and review are traceable.
const putDescriptor = await resolveRootApiPutDescriptor(
client,
store,
context,
descriptor,
deployedDescriptor,
config
- Files reviewed: 20/21 changed files
- Comments generated: 4
- Review effort level: Balanced
The merge-base changed after approval.
|
Closing in favor of #268 |
Fixes the pre-existing bug where publishAssociation re-throws on the first
"API not found"/"Group not found", aborting the whole gateway/product association
so present APIs never get linked. Now skips the missing entry with a warning and
continues, matching product-association behavior.
Closes #264
Closes #266