From 2b4f52caa5aecbf685510a7444f08e1824f600b9 Mon Sep 17 00:00:00 2001 From: "Dawson, Matt" Date: Mon, 6 Jul 2026 14:18:28 +1000 Subject: [PATCH 1/5] Non-existent branch --- .../apis/precedence-demo/precedence-demo-catalog-info.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/labs/lab-04-auto-registration/apis/precedence-demo/precedence-demo-catalog-info.yaml b/labs/lab-04-auto-registration/apis/precedence-demo/precedence-demo-catalog-info.yaml index 6e79bf3..cb8b426 100644 --- a/labs/lab-04-auto-registration/apis/precedence-demo/precedence-demo-catalog-info.yaml +++ b/labs/lab-04-auto-registration/apis/precedence-demo/precedence-demo-catalog-info.yaml @@ -18,4 +18,4 @@ spec: lifecycle: production owner: group:default/museum-team definition: - $text: https://raw.githubusercontent.com/DawMatt/backstage-apiportal-lab/004-lab-4-auto-registration/labs/lab-04-auto-registration/apis/precedence-demo/precedence-demo-openapi.yaml + $text: https://raw.githubusercontent.com/DawMatt/backstage-apiportal-lab/main/labs/lab-04-auto-registration/apis/precedence-demo/precedence-demo-openapi.yaml From 8b0645e4e6ef3af3b0a5af275c43badd6c158045 Mon Sep 17 00:00:00 2001 From: "Dawson, Matt" Date: Mon, 6 Jul 2026 16:00:59 +1000 Subject: [PATCH 2/5] Windows compatibility updates --- labs/lab-05-mocking-testing/README.md | 2 +- labs/lab-06-api-lifecycle-management/README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/labs/lab-05-mocking-testing/README.md b/labs/lab-05-mocking-testing/README.md index a1d30a7..e7f3b28 100644 --- a/labs/lab-05-mocking-testing/README.md +++ b/labs/lab-05-mocking-testing/README.md @@ -164,7 +164,7 @@ credential below) — do not reach for this setting for a proxy target that fron In the root `package.json`: ```json -"start": "concurrently -n app,mocks \"backstage-cli repo start\" \"node scripts/mock-gateway.mjs\"" +"start": "concurrently -n front,back,mocks \"yarn workspace app start\" \"yarn workspace backend start\" \"node scripts/mock-gateway.mjs\"" ``` No `wait-on` sequencing is needed — the gateway's port and proxy target are static, committed diff --git a/labs/lab-06-api-lifecycle-management/README.md b/labs/lab-06-api-lifecycle-management/README.md index 9ab1296..83ca027 100644 --- a/labs/lab-06-api-lifecycle-management/README.md +++ b/labs/lab-06-api-lifecycle-management/README.md @@ -138,8 +138,8 @@ to Backstage or "the API Portal" specifically. Every team at `examplecorp` uses regardless of which repo or catalog source picks their specs up; introducing a per-tool namespace (e.g. `x-apiportal`) would wrongly imply the metadata exists *for* Backstage, when other systems (a CLI linter, an internal API gateway, a docs generator) are equally valid consumers of the same -`x-examplecorp` block. Lab 2's original single "Museum REST API" `catalog.locations` entry has -also been **removed** — this lab's two auto-registered versions supersede it (see "Why Supersede +`x-examplecorp` block. Lab 2's original single "Museum REST API" `catalog.locations` entry must +also be **removed** — this lab's two auto-registered versions supersede it (see "Why Supersede Lab 2's Entry, Not Edit It" below). ## Step 3 — Add the API Versions Frontend Module From 47f041d2da32b937520a791173516eed7ef8422c Mon Sep 17 00:00:00 2001 From: "Dawson, Matt" Date: Mon, 6 Jul 2026 17:07:13 +1000 Subject: [PATCH 3/5] Clearly identify sub-steps in labs --- labs/lab-02-users-roles/README.md | 8 +-- labs/lab-03-api-quality/README.md | 24 ++++--- labs/lab-04-auto-registration/README.md | 13 +++- labs/lab-05-mocking-testing/README.md | 21 ++++-- .../lab-06-api-lifecycle-management/README.md | 65 +++++++++++++------ 5 files changed, 92 insertions(+), 39 deletions(-) diff --git a/labs/lab-02-users-roles/README.md b/labs/lab-02-users-roles/README.md index c1ea96a..47bb5c1 100644 --- a/labs/lab-02-users-roles/README.md +++ b/labs/lab-02-users-roles/README.md @@ -307,7 +307,7 @@ This step also demonstrates Backstage's declarative frontend extension system: No new packages are required — all imports are already installed in the Backstage instance from Lab 1. -#### Create the module directory +#### Step 2b.1 — Create the module directory | Platform | Command | |----------|---------| @@ -317,7 +317,7 @@ from Lab 1. Run this command from inside your Backstage root directory (`labs/lab-01-base-backstage/backstage/`). -#### Create `ApiVisibilityCard.tsx` +#### Step 2b.2 — Create `ApiVisibilityCard.tsx` Create `packages/app/src/modules/apiVisibility/ApiVisibilityCard.tsx` — its full content is committed alongside this README at @@ -326,7 +326,7 @@ copy it in as-is. It reads the `example.com/visibility` annotation off the entit "Shared"/"Private" chip, with a fallback message when the annotation is absent (treated as private by the permission policy). -#### Create `index.ts` +#### Step 2b.3 — Create `index.ts` Create `packages/app/src/modules/apiVisibility/index.ts` — its full content is committed at [`code/packages/app/src/modules/apiVisibility/index.ts`](./code/packages/app/src/modules/apiVisibility/index.ts); @@ -340,7 +340,7 @@ it into a `createFrontendModule`. > but it is stable enough for this lab and follows the same pattern Backstage itself uses > internally. -#### Update `App.tsx` +#### Step 2b.4 — Update `App.tsx` Open `packages/app/src/App.tsx` and update it as follows: diff --git a/labs/lab-03-api-quality/README.md b/labs/lab-03-api-quality/README.md index f5d59d8..1672d1f 100644 --- a/labs/lab-03-api-quality/README.md +++ b/labs/lab-03-api-quality/README.md @@ -228,6 +228,8 @@ Replace `` and `` with your GitHub username and branch name. ## Step 7 — Install the Spectral Linter Plugin +### Step 7a — Run the install command + Unlike the api-grade plugins, `@dweber019/backstage-plugin-api-docs-spectral-linter` IS published to npm and can be installed directly: @@ -238,7 +240,7 @@ yarn --cwd packages/app add @dweber019/backstage-plugin-api-docs-spectral-linter No source build is required. The package is compatible with React 18 and react-router-dom 6, both already present in this Backstage instance. -### Dedupe two Spectral libraries so linting works in the browser (required) +### Step 7b — Dedupe conflicting Spectral libraries (required) The Spectral linter (`0.5.2`) pins **exact** versions of two `@stoplight` libraries, so yarn installs a second, *nested* copy of each under the plugin's own `node_modules`. Both nested @@ -337,11 +339,17 @@ mode. Importing it directly sidesteps the incompatibility entirely instead of wo it. Because this subpath has no TypeScript declaration file, add a small ambient module -declaration. Create `packages/app/src/modules/spectralLinter/spectral-linter-content.d.ts` — its +declaration. + +### Step 8a — Create `spectral-linter-content.d.ts` + +Create `packages/app/src/modules/spectralLinter/spectral-linter-content.d.ts` — its full content is committed alongside this README at [`code/packages/app/src/modules/spectralLinter/spectral-linter-content.d.ts`](./code/packages/app/src/modules/spectralLinter/spectral-linter-content.d.ts); copy it in as-is. +### Step 8b — Create `SpectralLinterContent.tsx` + Create the file `packages/app/src/modules/spectralLinter/SpectralLinterContent.tsx` — its full content is committed at [`code/packages/app/src/modules/spectralLinter/SpectralLinterContent.tsx`](./code/packages/app/src/modules/spectralLinter/SpectralLinterContent.tsx); @@ -454,9 +462,9 @@ for `group:default/platform-team`'s member list. Since the group entity is defin second `platform-team` Group descriptor in Lab 3 would produce a duplicate entity error in the catalog. -The Lab 2 file at `labs/lab-02-users-roles/catalog/teams.yaml` has already been updated to -add `eve` to `platform-team`'s members list. Backstage will pick this up on the next catalog -refresh. +The Lab 2 file at `labs/lab-02-users-roles/catalog/teams.yaml` already includes `eve` in +`platform-team`'s `members` list — no changes are needed to that file. Backstage will pick +up the updated membership on the next catalog refresh. Add the following catalog location to `app-config.yaml`: @@ -478,7 +486,7 @@ Replace `` and `` with your GitHub username and branch name. --- -## Step 12a — Grant the Platform Team Catalog Read Access +## Step 13 — Grant the Platform Team Catalog Read Access **Why this step is needed**: Registering Eve as a `platform-team` member (Step 12) and configuring `apiGrade.visibility.groups` (Step 6) are not enough on their own. Lab 2's @@ -677,7 +685,7 @@ verify Charlie is not a member of platform-team her in the APIs catalog list and search **Fail (Museum/Streetlights don't appear in the APIs catalog list or search for Eve, or her entity-page requests 404)**: This is a catalog *read* permission problem, not an `apiGrade` -display problem — Eve can't reach the entity at all. Confirm Step 12a's platform-team rule was +display problem — Eve can't reach the entity at all. Confirm Step 13's platform-team rule was added to `permissionPolicy.ts` and that it checks `user.info.ownershipEntityRefs` (populated from Eve's `memberOf: [platform-team]`) before falling through to the ownership-only conditional decision; restart the backend after editing @@ -744,7 +752,7 @@ condition. Because Backstage can't even load a private entity a user isn't allow it never appears in the catalog list, search, or a direct-link entity page — and `apiGrade`'s own summary/detail visibility split never gets a chance to run. -**Fix**: Add the platform-team check to `permissionPolicy.ts` from Step 12a: before falling +**Fix**: Add the platform-team check to `permissionPolicy.ts` from Step 13: before falling through to the existing `anyOf` conditional decision, unconditionally allow `catalog-entity` reads when `user.info.ownershipEntityRefs` includes `group:default/platform-team`. Restart the backend after editing — permission policy changes require a backend restart to take diff --git a/labs/lab-04-auto-registration/README.md b/labs/lab-04-auto-registration/README.md index 92f3b90..9be2dfa 100644 --- a/labs/lab-04-auto-registration/README.md +++ b/labs/lab-04-auto-registration/README.md @@ -106,6 +106,8 @@ this provider, the provider skips its own auto-sourced candidate for that name e lets you hand-author precise metadata for a specific API — pinning an owner, tightening visibility — without the auto-registration mechanism fighting you for it every cycle. +### Step 2a — Create the database migration file + Create the database migration first, at `packages/backend/src/extensions/autoApiRegistrationMigrations/001_scan_state_cache.ts`. Its full content is committed alongside this README — @@ -116,6 +118,8 @@ content is committed alongside this README — since this file lives alongside the module's TypeScript source rather than in a package-level `migrations/` folder resolved at runtime. +### Step 2b — Create the auto-registration module + Now the module itself, at `packages/backend/src/extensions/autoApiRegistration.ts` — this is the heart of the lab. Its full content (~750 lines: config normalization, filesystem discovery and YAML parsing, `x-*` candidate mapping with owner/visibility/collision validation, the scan-state @@ -167,10 +171,11 @@ or vendor namespace it never opted into. --- -## Step 5 — Add the Sample API Files +## Step 5 — Review the Sample API Files and Register the Precedence Demo -Two sample files are already committed alongside this README (nothing to create in this step — -just take a look): +Two sample files are already committed alongside this README — review them to understand +the auto-discovery and precedence concepts, then register the precedence demo's hand-authored +catalog entry as described below: - **`apis/galaxy/galaxy-openapi.yaml`** — a vendored copy of the MIT-licensed [Scalar Galaxy API](https://github.com/scalar/scalar), with an `info.x-examplecorp` object @@ -182,6 +187,8 @@ just take a look): values (owner `platform-team`, `visibility: shared`). This demonstrates the precedence rule: the hand-authored file wins. +### Step 5a — Register the precedence demo catalog location + The hand-authored `precedence-demo-catalog-info.yaml` needs its own catalog location, the same way Lab 2/3's hand-authored files do — add it to `app-config.yaml`'s `catalog.locations`, replacing `` and `` with your fork and the current branch name: diff --git a/labs/lab-05-mocking-testing/README.md b/labs/lab-05-mocking-testing/README.md index e7f3b28..a4e7212 100644 --- a/labs/lab-05-mocking-testing/README.md +++ b/labs/lab-05-mocking-testing/README.md @@ -177,9 +177,15 @@ prevent by tearing down the whole session the moment the gateway exits. ## Step 5 — Add the `apiMocking` Frontend Module +### Step 5a — Copy the module file + Copy [`code/packages/app/src/modules/apiMocking/index.tsx`](code/packages/app/src/modules/apiMocking/index.tsx) -to `packages/app/src/modules/apiMocking/index.tsx`, then register it in `packages/app/src/App.tsx` -alongside Lab 3's modules: +to `packages/app/src/modules/apiMocking/index.tsx`. + +### Step 5b — Register the module in `App.tsx` + +In `packages/app/src/App.tsx`, add the import and module to the `features` array alongside +Lab 3's modules: ```ts import { apiMockingModule } from './modules/apiMocking'; @@ -214,8 +220,11 @@ confirmed by reading its source rather than assumed: ``, *after* its own internal `spec={definitionString}` — so an extra `spec` prop passed through the widget wins over the string-derived one. -This new config key also needs to be declared visible to the frontend bundle. Add -`packages/app/config.d.ts`: +This new config key also needs to be declared visible to the frontend bundle. + +### Step 5c — Create `packages/app/config.d.ts` + +Add `packages/app/config.d.ts`: ```ts export interface Config { @@ -230,7 +239,9 @@ export interface Config { } ``` -and add `"configSchema": "config.d.ts"` to `packages/app/package.json`. Without both, Backstage's +### Step 5d — Declare the config schema in `packages/app/package.json` + +Add `"configSchema": "config.d.ts"` to `packages/app/package.json`. Without both Steps 5c and 5d, Backstage's config-visibility filter silently strips `mocking.defaultCredential` before it reaches the browser — `configApi.getOptional(...)` will just return `undefined`, with no error anywhere. This was found by checking the `