fix: repair extract→publish round trip for SOAP APIs, gateway associations, and concurrent deletes - #249
Merged
Conversation
…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.
Alexander Zaslonov (azaslonov)
approved these changes
Aug 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🎯 Summary
This PR fixes three defects discovered while testing the full
extract → publishround 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.publish --dry-runGatewayApidescriptorsextract(SOAP/WSDL)publish --delete-unmatchedPreconditionFailedon deletesnpm audit)npm audit fix🔍 Changes
1. Dry-run crashed on gateway→API associations
File:
src/services/dry-run-reporter.tspublish --dry-runfailed fatally withformatTemplatePath: 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
GatewayApidescriptor (nameParts = [gateway]) fromgateways/{gw}/apis.json, whilebuildResourceLabelexpects both name parts. The real publish path handles aggregates inpublishAssociation; 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.tsAPIM 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'wsdl:portemitted per configured proxy hostname →ValidationError: Multiple service endpoints available, only one can be imported at a timeFix:
normalizeWsdl()runs during extract forwsdl-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 firstwsdl:portperwsdl:service, drops the rest.3. Retry on delete concurrency conflicts
File:
src/clients/apim-client.tsWith
--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:
deleteResourceretries 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.jsonResolved 4 high-severity vulnerabilities reported by
npm auditvianpm 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 passthroughtests/unit/services/dry-run-reporter.test.ts— regression test for aggregateGatewayApiexpansionLive verification (dev-APIM → prod-APIM):
--delete-unmatchedover 35 resources — previously 2 APIs failed withPreconditionFailed; retry resolves it📝 Notes for reviewers