@nospt/backstage-plugin-apigee-backend 1.1.0-rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # @nospt/backstage-plugin-apigee-backend
2
+
3
+ Backend package for the bootstrap stage of the Backstage Apigee plugin.
4
+
5
+ ## Included
6
+
7
+ - New Backend System plugin registration
8
+ - Minimal router mounted at `/api/apigee`
9
+ - Placeholder health endpoint at `/api/apigee/health`
10
+
11
+ ## Development
12
+
13
+ ```bash
14
+ cd plugins/apigee-backend
15
+ yarn start
16
+ yarn test
17
+ yarn lint
18
+ yarn build
19
+ ```
20
+
21
+ ## Repository Stitching
22
+
23
+ A Backstage `CatalogProcessor` (`ApigeeStitchingProcessor`) links a backend
24
+ `Component` to its proxy `API` entity by emitting the native `providesApi` /
25
+ `apiProvidedBy` relations. This surfaces the standard **"Provided APIs"** card on
26
+ the component page and the **"Providers"** card on the API page — with zero custom
27
+ UI. A repository opts in by adding `nos.pt/apigee-*` annotations to its
28
+ `catalog-info.yaml`.
29
+
30
+ ### Annotation styles
31
+
32
+ **Direct mapping (active in this release):**
33
+
34
+ ```yaml
35
+ apiVersion: backstage.io/v1alpha1
36
+ kind: Component
37
+ metadata:
38
+ name: orders-service
39
+ annotations:
40
+ nos.pt/apigee-api-name: my-proxy
41
+ nos.pt/apigee-project-id: apigee-x-public-p-123
42
+ spec:
43
+ type: service
44
+ owner: team-orders
45
+ lifecycle: production
46
+ ```
47
+
48
+ **TargetServer-based mapping (documented; resolution planned, not yet active):**
49
+
50
+ ```yaml
51
+ metadata:
52
+ annotations:
53
+ nos.pt/apigee-targetserver-hostname: backend.nos.pt
54
+ nos.pt/apigee-targetserver-port: "443"
55
+ ```
56
+
57
+ ### Precedence rule
58
+
59
+ When both styles are present, the **TargetServer-based mapping is the preferred**
60
+ resolution path (STITCH-02). The direct API-name mapping is the **fallback**, used
61
+ when no TargetServer annotation is present or when TargetServer resolution is
62
+ ambiguous (for example, a generic load balancer shared by many proxies).
63
+
64
+ > **In this release only the direct-mapping path is implemented.** TargetServer
65
+ > resolution is planned but not yet active — no TargetServer data is captured by the
66
+ > entity provider yet (see `04-CONTEXT` D-01). The processor is structured around a
67
+ > resolution-strategy seam so the TargetServer branch slots in without rework.
68
+
69
+ ### Apigee → Backstage mapping
70
+
71
+ | Apigee concept | Backstage entity |
72
+ |----------------|------------------|
73
+ | API proxy (implementation) | `kind: Component`, `spec.type: api-proxy` |
74
+ | API proxy (contract) | `kind: API`, `spec.type` from API Hub (default `openapi`) |
75
+ | Sharedflow | `kind: Component`, `spec.type: library` |
76
+ | Backend repo component ↔ proxy API | `providesApi` / `apiProvidedBy` relation |
77
+
78
+ ### Resolution detail
79
+
80
+ The target API entity ref is deterministic:
81
+ `api:default/<slugify(nos.pt/apigee-api-name)>`. The processor reads the annotations,
82
+ synthesizes the ref, and emits the relation pair — no live catalog lookup is needed.
83
+ A partial annotation set (e.g. `nos.pt/apigee-api-name` without
84
+ `nos.pt/apigee-project-id`) or an empty/unresolvable value is skipped with a warning
85
+ (warn-and-skip), so no malformed relations are emitted.
86
+
87
+ ### Example fixture
88
+
89
+ See [`examples/components.yaml`](./examples/components.yaml) for ready-to-register
90
+ sample `Component` entities exercising both annotation styles — useful for local
91
+ smoke testing of the stitching processor.
92
+
93
+ ## API Hub enrichment
94
+
95
+ `kind: API` entities discovered from Apigee are enriched with API Hub metadata
96
+ (owner, description, categories, department, spec). The **"Source repository"** link
97
+ on the API entity page (`metadata.links`) is resolved in this order:
98
+
99
+ 1. API Hub **Documentation** field (`Api.documentation.externalUri`) — NOS records
100
+ the proxy's GitHub repository URL here (e.g.
101
+ `https://github.com/nosportugal/apigee-x-proxy-<proxy-name>`).
102
+ 2. The first `sourceMetadata[]` entry whose value is an `http(s)` URL
103
+ (`repo`/`git`/`source` types preferred when several qualify).
104
+ 3. The `apigee.orgs[].githubOrgSlug` convention:
105
+ `https://github.com/<slug>/<entity-name>`.
106
+
107
+ Only `http(s)` URLs are ever emitted; `javascript:`/`data:` values are rejected to
108
+ prevent link injection into rendered entity pages.
109
+
110
+ ### Department label
111
+
112
+ Both the proxy `kind: Component` and its `kind: API` entity carry a `department`
113
+ label (`metadata.labels.department`) sourced from the Cloud API Hub **Business
114
+ unit** attribute. Business unit is a top-level API Hub `AttributeValues` field; a
115
+ user-modelled `business-unit` attribute is scanned only as a fallback. The raw
116
+ value is slugified and capped at 63 characters so it is always a valid Backstage
117
+ label value. The frontend catalog page exposes it as a **Department** filter
118
+ facet.
package/config.d.ts ADDED
@@ -0,0 +1,85 @@
1
+ export interface Config {
2
+ /** Apigee catalog module configuration */
3
+ apigee?: {
4
+ /**
5
+ * List of Apigee X organisations to sync.
6
+ * At least one entry is required; the module fails fast at startup if missing.
7
+ * @visibility backend
8
+ */
9
+ orgs: Array<{
10
+ /** Human-readable label for this org */
11
+ name: string;
12
+ /** GCP project ID that owns this Apigee X organisation */
13
+ projectId: string;
14
+ /**
15
+ * Optional list of environments to include (e.g. ["prod", "staging"]).
16
+ * If omitted, all environments are synced.
17
+ * @visibility backend
18
+ */
19
+ environments?: string[];
20
+ /**
21
+ * Optional GitHub organisation slug used to derive GitHub repo links
22
+ * when API Hub does not supply a repositoryUri (FR17).
23
+ * @visibility backend
24
+ */
25
+ githubOrgSlug?: string;
26
+ /**
27
+ * Explicit proxy-name -> API Hub apiId overrides for proxies whose slug
28
+ * does not match their API Hub apiId (D-01).
29
+ * @visibility backend
30
+ */
31
+ apiHubMappings?: { [proxyName: string]: string };
32
+ }>;
33
+ /**
34
+ * Catalog sync interval in minutes. Default: 60.
35
+ * @visibility backend
36
+ */
37
+ syncIntervalMinutes?: number;
38
+ /**
39
+ * Default spec.owner for API entities whose API Hub ownerTeam is absent.
40
+ * Must be a Backstage entity reference, e.g. "group:default/platform-team".
41
+ * @visibility backend
42
+ */
43
+ defaultOwner?: string;
44
+ /**
45
+ * Maximum inline spec size in bytes; specs larger than this are linked via
46
+ * apigee.com/spec-url instead of embedded. Default 512000 (500 KB).
47
+ * @visibility backend
48
+ */
49
+ specMaxBytes?: number;
50
+ /**
51
+ * Optional map of Apigee environment name to Backstage lifecycle
52
+ * ("production" | "experimental" | "deprecated"). When set, a proxy's
53
+ * lifecycle is derived from the environments it is deployed to, with the
54
+ * highest-ranked lifecycle winning (production > experimental > deprecated).
55
+ * Environments not listed default to "experimental". When omitted, any
56
+ * ready deployment marks the proxy as "production".
57
+ * @visibility backend
58
+ */
59
+ environmentLifecycle?: { [environment: string]: string };
60
+ /**
61
+ * API Hub location (GCP region) to query. Defaults to "global".
62
+ * Use this when your API Hub instance is regional, e.g. "europe-west1".
63
+ * @visibility backend
64
+ */
65
+ apiHubLocation?: string;
66
+ /**
67
+ * Optional custom schedule for the catalog sync task.
68
+ * When provided, overrides syncIntervalMinutes.
69
+ * @visibility backend
70
+ */
71
+ schedule?: {
72
+ /** How often to run the sync. */
73
+ frequency?: {
74
+ cron?: string;
75
+ minutes?: number;
76
+ hours?: number;
77
+ seconds?: number;
78
+ };
79
+ /** Maximum time allowed for a single sync run. */
80
+ timeout?: { minutes?: number; seconds?: number };
81
+ /** Delay before the first run after startup. */
82
+ initialDelay?: { minutes?: number; seconds?: number };
83
+ };
84
+ };
85
+ }
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var plugin = require('./plugin.cjs.js');
6
+ var module$1 = require('./module.cjs.js');
7
+ var apigeeClient = require('./lib/apigee-client.cjs.js');
8
+ var apiHubClient = require('./lib/api-hub-client.cjs.js');
9
+ var entityProvider = require('./lib/entity-provider.cjs.js');
10
+ var sharedflowClient = require('./lib/sharedflow-client.cjs.js');
11
+ var apigeeStitchingProcessor = require('./lib/apigee-stitching-processor.cjs.js');
12
+
13
+
14
+
15
+ exports.default = plugin.apigeePlugin;
16
+ exports.catalogModuleApigee = module$1.catalogModuleApigee;
17
+ exports.ApigeeClient = apigeeClient.ApigeeClient;
18
+ exports.ApiHubClient = apiHubClient.ApiHubClient;
19
+ exports.ApigeeEntityProvider = entityProvider.ApigeeEntityProvider;
20
+ exports.SharedflowClient = sharedflowClient.SharedflowClient;
21
+ exports.ApigeeStitchingProcessor = apigeeStitchingProcessor.ApigeeStitchingProcessor;
22
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;"}