Command that triggered the bug
apiops extract
Summary
During a single extract run, API revisions are written by two code paths:
- The top-level API list (
GET /apis) returns the current revision (under the base API
name) and every non-current revision (as ;rev=N). Each of these is extracted in
full (spec, operations, schemas). This path is correct.
extractApiRevisions additionally walks GET /apis/{id}/revisions and writes an
apiInformation.json-only folder for every revision except the one literally
numbered 1.
Because path 2 skips only the revision hard-coded as '1' (assuming revision 1 is always
the current/main API), an API whose current revision is not 1 (e.g. 2) gets a
redundant, incomplete folder: the current revision is re-emitted as a ;rev=2 folder that
contains only apiInformation.json — a thin duplicate of the main API folder.
Affected component
- Package:
@azure-tools/apiops-cli (v1.0.0)
- File:
src/services/api-extractor.ts
- Function:
extractApiRevisions
Root cause
In extractApiRevisions the loop skips only the revision literally numbered 1, on the
assumption that revision 1 is the main API:
const revNumber = revision.apiRevision ?? revision.revisionNumber;
if (!revNumber || revNumber === '1') {
// Skip revision 1 — it's the main API
continue;
}
...
const revJson = await client.getResource(context, descriptor); // API entity only
await store.writeResource(outputDir, descriptor, revJson); // writes ONLY apiInformation.json
When the current revision is not 1, the === '1' guard skips the wrong revision:
- The current revision (e.g.
2) does not match the guard, so extractApiRevisions
re-writes it as a ;rev=2 folder — but this path only persists the API entity
(apiInformation.json), not children (spec/operations/schemas). The result is a thin
duplicate of the main API folder.
- Revision
1 (matched by the guard) is skipped by this path, but that is harmless here
because non-current revisions are already extracted in full by the top-level /apis
list path.
So the main folder and the full non-current revision folders are correct; the only defect is
the redundant, apiInformation.json-only folder emitted for the current revision.
Steps to reproduce
- In APIM, take an API with multiple revisions where the current revision is not
1
(e.g. create revision 2 and make it current).
- Run
apiops extract against that service.
- Inspect the output for that API.
Expected behavior
- The current revision is written once, as the main API folder, with full content.
- Each non-current revision (including revision
1 when it is not current) is written
as a ;rev=N folder.
- No
;rev=N folder is created for the current revision.
Actual behavior
Example for an API named my-api (current revision = 2, all produced in one extract run):
apis/
my-api/ # rev 2 (current) — full: spec + operations + schemas (via /apis list)
my-api;rev=1/ # rev 1 (non-current) — full: spec + operations + schemas (via /apis list)
my-api;rev=2/ # rev 2 again — ONLY apiInformation.json (redundant thin duplicate)
- The current revision (
2) is duplicated as a thin ;rev=2 folder that has no children.
Downstream impact
publish picks up the redundant ;rev=2 folder and attempts to re-create the current
revision, causing confusion and potential concurrency conflicts on large APIs.
- The extra
apiInformation.json-only folder is misleading and pollutes the artifacts /
diffs for every API whose current revision is not 1.
Suggested fix
Skip the current revision (via isCurrent === true) instead of the hard-coded revision
number 1. This correctly excludes whichever revision is current and extracts all others as
;rev=N:
const isCurrent = revision.isCurrent === true; // from revision metadata
if (isCurrent) {
// Current revision is represented by the main API folder — skip here.
continue;
}
Additionally, consider whether extractApiRevisions is needed at all: non-current revisions
are already extracted in full via the top-level /apis list, so this path may be entirely
redundant (and only ever produces incomplete, apiInformation.json-only folders).
Environment
@azure-tools/apiops-cli v1.0.0
- Command:
extract
apiops CLI version
v1.0.0
Environment details
@azure-tools/apiops-cli v1.0.0
- Command:
extract
CI/CD environment
None
Is this bug blocking you?
None
Command that triggered the bug
apiops extract
Summary
During a single
extractrun, API revisions are written by two code paths:GET /apis) returns the current revision (under the base APIname) and every non-current revision (as
;rev=N). Each of these is extracted infull (spec, operations, schemas). This path is correct.
extractApiRevisionsadditionally walksGET /apis/{id}/revisionsand writes anapiInformation.json-only folder for every revision except the one literallynumbered
1.Because path 2 skips only the revision hard-coded as
'1'(assuming revision1is alwaysthe current/main API), an API whose current revision is not
1(e.g.2) gets aredundant, incomplete folder: the current revision is re-emitted as a
;rev=2folder thatcontains only
apiInformation.json— a thin duplicate of the main API folder.Affected component
@azure-tools/apiops-cli(v1.0.0)src/services/api-extractor.tsextractApiRevisionsRoot cause
In
extractApiRevisionsthe loop skips only the revision literally numbered1, on theassumption that revision
1is the main API:When the current revision is not
1, the=== '1'guard skips the wrong revision:2) does not match the guard, soextractApiRevisionsre-writes it as a
;rev=2folder — but this path only persists the API entity(
apiInformation.json), not children (spec/operations/schemas). The result is a thinduplicate of the main API folder.
1(matched by the guard) is skipped by this path, but that is harmless herebecause non-current revisions are already extracted in full by the top-level
/apislist path.
So the main folder and the full non-current revision folders are correct; the only defect is
the redundant,
apiInformation.json-only folder emitted for the current revision.Steps to reproduce
1(e.g. create revision
2and make it current).apiops extractagainst that service.Expected behavior
1when it is not current) is writtenas a
;rev=Nfolder.;rev=Nfolder is created for the current revision.Actual behavior
Example for an API named
my-api(current revision =2, all produced in one extract run):2) is duplicated as a thin;rev=2folder that has no children.Downstream impact
publishpicks up the redundant;rev=2folder and attempts to re-create the currentrevision, causing confusion and potential concurrency conflicts on large APIs.
apiInformation.json-only folder is misleading and pollutes the artifacts /diffs for every API whose current revision is not
1.Suggested fix
Skip the current revision (via
isCurrent === true) instead of the hard-coded revisionnumber
1. This correctly excludes whichever revision is current and extracts all others as;rev=N:Additionally, consider whether
extractApiRevisionsis needed at all: non-current revisionsare already extracted in full via the top-level
/apislist, so this path may be entirelyredundant (and only ever produces incomplete,
apiInformation.json-only folders).Environment
@azure-tools/apiops-cliv1.0.0extractapiops CLI version
v1.0.0
Environment details
@azure-tools/apiops-cliv1.0.0extractCI/CD environment
None
Is this bug blocking you?
None