Skip to content

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

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

Activity

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