Skip to content

extract mishandles APIs whose current revision is not rev=1 #258

Closed

Description

Command that triggered the bug

apiops extract

Summary

During a single extract run, API revisions are written by two code paths:

  1. 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.
  2. 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

  1. In APIM, take an API with multiple revisions where the current revision is not 1
    (e.g. create revision 2 and make it current).
  2. Run apiops extract against that service.
  3. 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

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

Metadata

Metadata

Labels

close:fixedFixed by a previous PR or releasetype:bugSomething broken

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions