Skip to content

fix: skip missing API/group links in gateway/product associations instead of aborting - #267

Closed
Aleksey Zheltov (Alexey-Zheltov) wants to merge 18 commits into
Azure:mainfrom
Alexey-Zheltov:fix/gateway-association-skip-missing
Closed

Aleksey Zheltov (Alexey-Zheltov) wants to merge 18 commits into
Azure:mainfrom
Alexey-Zheltov:fix/gateway-association-skip-missing

Conversation

@Alexey-Zheltov

@Alexey-Zheltov Aleksey Zheltov (Alexey-Zheltov) commented Sep 4, 2026 •

Copy link
Copy Markdown
Collaborator

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

Copilot AI and others added 16 commits August 31, 2026 23:05
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
Copilot AI balanced review requested due to automatic review settings September 4, 2026 13:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 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.

Comment thread src/services/resource-publisher.ts
Comment thread src/services/api-publisher.ts
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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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

Comment thread src/services/delete-unmatched-service.ts
Comment thread src/services/extract-service.ts
Comment thread src/services/extract-service.ts
Comment thread src/services/extract-service.ts
@azaslonov

Copy link
Copy Markdown
Member

Closing in favor of #268

@Alexey-Zheltov
Aleksey Zheltov (Alexey-Zheltov) deleted the fix/gateway-association-skip-missing branch September 14, 2026 15:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

6 participants