Repository navigation
Description
Command that triggered the bug
apiops publish
Expected behavior
normalizeSubscriptionScope in src/services/resource-publisher.ts documents the intent clearly:
Strip the service base ARM path prefix when it is present so that the extract → publish round-trip works without manual edits.
A subscription extracted from one instance and published to another should not need manual edits. properties.ownerId is a service-scoped ARM id in exactly the same way properties.scope is, so I would expect it to be normalized the same way.
Microsoft documents ownerId as relative:
ownerId — The user resource identifier of the subscription owner. The value is a valid relative URL in the format of
/users/{userId}where{userId}is a user identifier.
Actual behavior
scope is normalized on publish, ownerId is not, so a subscription published to a different instance than it was extracted from carries the source instance's ownerId.
Extraction stores the ARM response as-is, which is deliberate:
// src/services/resource-extractor.ts
const safeJson = redactSecrets(descriptor, json);
// Write to artifact store (preserves opaque JSON per FR-009)
await store.writeResource(outputDir, descriptor, safeJson);So the artifact on disk holds both ids absolute:
{
"properties": {
"scope": "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.ApiManagement/service/{src-svc}/products/starter",
"ownerId": "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.ApiManagement/service/{src-svc}/users/1"
}
}On publish, resource-publisher.ts:426 calls normalizeSubscriptionScope, which rewrites scope to /products/starter. Nothing does the equivalent for ownerId, so it is sent unchanged, still naming {src-svc}.
ownerId does not appear anywhere in src/. The only occurrence in the repository is a test fixture, tests/unit/services/resource-publisher.test.ts:1309, which sets it absolute alongside an absolute scope.
Suggested fix
Extend normalizeSubscriptionScope to cover ownerId, or add a sibling that applies the same service-prefix strip to it. The existing serviceScopeMatch regex already isolates the trailing APIM-relative segment, so the transform is the same shape: .../service/{svc}/users/1 becomes /users/1.
Worth noting ownerId differs from scope in one respect: envMapping affixes should almost certainly not apply to it, since a user id is not an affixed resource name.
apiops CLI version
1.0.0
Identified by reading the source at commit 420b993, not from a failed run. See the note below.
Environment details
I have not reproduced this against a live instance. This was found while reviewing how v1.0.0 handles cross-instance ids, after hitting the equivalent gap in the archived Azure/apiops (where scope is relativized on extract and ownerId is not, for which I opened Azure/apiops#877 before noticing that repository is being archived).
Happy to confirm against a real pair of instances if that would help, and happy to send a PR if you agree with the shape of the fix.
CI/CD environment
N/A (running locally)
Is this bug blocking you?
No
Activity
fix: normalize subscription ownerId to relative /users path on publish