Skip to content

Subscription ownerId is not normalized on publish, unlike scope #273

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.

SubscriptionContract.ownerId

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    close:fixedFixed by a previous PR or release

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions