Skip to content

fix: repair extract→publish round trip for SOAP APIs, gateway associations, and concurrent deletes - #249

Merged
Alexander Zaslonov (azaslonov) merged 2 commits into
Azure:mainfrom
Alexey-Zheltov:main
Aug 31, 2026
Merged

fix: repair extract→publish round trip for SOAP APIs, gateway associations, and concurrent deletes#249
Alexander Zaslonov (azaslonov) merged 2 commits into
Azure:mainfrom
Alexey-Zheltov:main

Conversation

@Alexey-Zheltov

Copy link
Copy Markdown
Collaborator

🎯 Summary

This PR fixes three defects discovered while testing the full extract → publish round trip against real APIM instances (dev → prod), plus 4 high-severity npm audit vulnerabilities. Each functional fix was reproduced and verified end-to-end on live services.

# Area Symptom Fix
1 publish --dry-run Fatal crash on gateway→API associations Expand aggregate GatewayApi descriptors
2 extract (SOAP/WSDL) APIM re-import rejects its own WSDL export New WSDL normalizer
3 publish --delete-unmatched Intermittent PreconditionFailed on deletes Retry with backoff
4 Dependencies 4 high-severity vulnerabilities (npm audit) npm audit fix

🔍 Changes

1. Dry-run crashed on gateway→API associations

File: src/services/dry-run-reporter.ts

publish --dry-run failed fatally with formatTemplatePath: nameParts[1] is undefined for template "gateways/{0}/apis/{1}" whenever artifacts contained a gateway with associated APIs.

Root cause: artifact discovery produces an aggregate GatewayApi descriptor (nameParts = [gateway]) from gateways/{gw}/apis.json, while buildResourceLabel expects both name parts. The real publish path handles aggregates in publishAssociation; the dry-run reporter did not.

Fix: the reporter now expands aggregate descriptors into one action per associated API (reading apis.json), mirroring exactly what publish would PUT. APIs linked to multiple gateways produce one action per (gateway, api) pair.

2. WSDL export normalization

Files: src/lib/wsdl-normalizer.ts (new), src/services/api-extractor.ts

APIM regenerates WSDL on export and produces documents its own importer rejects, systematically breaking the GitOps round trip for SOAP APIs with multi-namespace schemas:

  • wsdl:part element="tns:X" is qualified with the WSDL targetNamespace even when the element is declared in a different inline schema namespace → ValidationError: Could not resolve type '{ns}X'
  • ❌ One wsdl:port emitted per configured proxy hostname → ValidationError: Multiple service endpoints available, only one can be imported at a time

Fix: normalizeWsdl() runs during extract for wsdl-format specifications only:

  • normalizeWsdlPartReferences — rewrites a part reference only when it is unresolvable as-is and the element is declared in exactly one inline schema (depth-aware scan ignores nested local elements). Ambiguous or unknown references are left untouched. Adds a root xmlns declaration when no prefix exists for the target namespace.
  • normalizeWsdlServicePorts — keeps the first wsdl:port per wsdl:service, drops the rest.

3. Retry on delete concurrency conflicts

File: src/clients/apim-client.ts

With --delete-unmatched, cascade deletes (subscriptions, product/gateway associations) run close together; APIM's async DELETE then intermittently fails with [PreconditionFailed] Resource was modified since last retrieval, leaving the target partially cleaned and requiring a manual re-run.

Fix: deleteResource retries up to 3 times with linear backoff (2s × attempt) on [PreconditionFailed] from async operation polling or a direct HTTP 412. All other errors propagate unchanged.

4. Dependency security fixes

Files: package.json, package-lock.json

Resolved 4 high-severity vulnerabilities reported by npm audit via npm audit fix (no breaking version bumps; test suite passes unchanged).

✅ Testing

Unit: 1,163 tests pass (npm test), lint clean. New coverage:

  • tests/unit/lib/wsdl-normalizer.test.ts — 10 tests: prefix rewrite, no-op on correct refs, generated xmlns declaration, ambiguous/undeclared/local-element skip paths, port dedup, combined normalization, non-WSDL passthrough
  • tests/unit/services/dry-run-reporter.test.ts — regression test for aggregate GatewayApi expansion

Live verification (dev-APIM → prod-APIM):

  • ✔️ dry-run over 84 resources incl. gateway associations — no crash, per-API labels
  • ✔️ SOAP API (WSDL, 4 namespaces, 2 custom hostnames) — extract emits normalized WSDL, publish succeeds (previously failed with both ValidationErrors)
  • ✔️ --delete-unmatched over 35 resources — previously 2 APIs failed with PreconditionFailed; retry resolves it

📝 Notes for reviewers

  • The WSDL normalizer is regex/scanner-based (no new XML parser dependency), consistent with existing raw-XML policy handling; it is deliberately conservative — only provably broken references are rewritten.
  • Dry-run cannot catch WSDL validation errors (no PUT bodies are sent) — pre-existing behavior, unchanged here.

…tions, and concurrent deletes

- dry-run: expand aggregate GatewayApi descriptors (nameParts=[gateway])
  from gateways/{gw}/apis.json into per-API actions instead of crashing
  in buildResourceLabel
- extract: add WSDL normalizer fixing two APIM export defects that its
  own importer rejects — wsdl:part references qualified with the wrong
  namespace prefix, and multiple wsdl:port endpoints per service
- client: retry deleteResource up to 3x with backoff on transient
  [PreconditionFailed]/HTTP 412 concurrency conflicts during cascade
  deletes with --delete-unmatched

Verified end-to-end against live dev/prod APIM instances.
@azaslonov
Alexander Zaslonov (azaslonov) merged commit b8d6179 into Azure:main Aug 31, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants