@solo.io/platform-portal-backstage-plugin-backend 0.0.0

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,56 @@
1
+ # @solo.io/platform-portal-backstage-plugin-backend
2
+
3
+ ## Installation
4
+
5
+ 1. Install the plugin to your backend package.
6
+
7
+ 2. Update your backend plugin in `packages/backend/src/plugins/catalog.ts` with the following code. The parts that you will need to update should similar to what is described in the Backstage docs [here](https://backstage.io/docs/features/software-catalog/external-integrations/#installing-the-provider):
8
+
9
+ ```ts
10
+ // ...
11
+ import { GlooPlatformPortalProvider } from '@solo.io/platform-portal-backstage-plugin-backend';
12
+ // ...
13
+
14
+ export default async function createPlugin(
15
+ env: PluginEnvironment,
16
+ providers?: Array<EntityProvider>,
17
+ ): Promise<Router> {
18
+ const builder = await CatalogBuilder.create(env);
19
+ // ...
20
+
21
+ const gppp = new GlooPlatformPortalProvider(
22
+ 'production',
23
+ env.logger,
24
+ env.config,
25
+ );
26
+ builder.addEntityProvider(gppp);
27
+
28
+ const { processingEngine, router } = await builder.build();
29
+ await processingEngine.start();
30
+
31
+ await env.scheduler.scheduleTask({
32
+ id: 'run_gloo_platform_portal_refresh',
33
+ fn: async () => {
34
+ await gppp.run();
35
+ },
36
+ frequency: { minutes: 30 },
37
+ timeout: { minutes: 10 },
38
+ });
39
+
40
+ //...
41
+
42
+ return router;
43
+ }
44
+ ```
45
+
46
+ 3. Update the `app-config.local` file for your backstage instance to include the following values, which should match your authorization server deployment:
47
+
48
+ ```yaml
49
+ glooPlatformPortal:
50
+ portalServerUrl: http://localhost:31080/v1
51
+ clientId: // Update with your client id
52
+ clientSecret: // Update with your client secret
53
+ tokenEndpoint: // Update with your token endpoint
54
+ serviceAccountUsername: // The username of the service account that can access your APIs.
55
+ serviceAccountPassword: // The password of the service account that can access your APIs.
56
+ ```
package/config.d.ts ADDED
@@ -0,0 +1,61 @@
1
+ /*
2
+ * Copyright 2023 The Backstage Authors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ export interface Config {
17
+ glooPlatformPortal: {
18
+ /**
19
+ * @visibility frontend
20
+ */
21
+ portalServerUrl: string;
22
+
23
+ /**
24
+ * The oauth client id.
25
+ * In keycloak, this is shown in the client settings
26
+ * of your keycloak instances UI.
27
+ * @visibility frontend
28
+ */
29
+ clientId: string;
30
+
31
+ /**
32
+ * This is the endpoint to get the oauth token.
33
+ * In keycloak, this is the `token_endpoint` property from:
34
+ * <your-keycloak-url>/realms/<your-realm>/.well-known/openid-configuration
35
+ * @visibility frontend
36
+ */
37
+ tokenEndpoint: string;
38
+
39
+ /**
40
+ * The oauth client secret.
41
+ * In keycloak, this is shown in the client settings
42
+ * of your keycloak instances UI.
43
+ * @visibility backend
44
+ */
45
+ clientSecret: string;
46
+
47
+ /**
48
+ * The username of the service account account which can access the
49
+ * APIs that will be synced with Backstage.
50
+ * @visibility backend
51
+ */
52
+ serviceAccountUsername: string;
53
+
54
+ /**
55
+ * The password of the service account account which can access the
56
+ * APIs that will be synced with Backstage.
57
+ * @visibility backend
58
+ */
59
+ serviceAccountPassword: string;
60
+ };
61
+ }
@@ -0,0 +1,373 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var fetch = require('node-fetch');
6
+
7
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
+
9
+ var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
10
+
11
+ const getPortalServerUrl = (_, config) => {
12
+ let value = config.getOptionalString("glooPlatformPortal.portalServerUrl");
13
+ if (!!value && value.at(-1) === "/")
14
+ value = value.substring(0, value.length - 1);
15
+ return value != null ? value : "http://localhost:31080/v1";
16
+ };
17
+ const getClientSecret = (logWarning, config) => {
18
+ const value = config.getOptionalString("glooPlatformPortal.clientSecret");
19
+ if (!value) {
20
+ logWarning(
21
+ "No glooPlatformPortal.clientSecret found in app-config.local.yaml"
22
+ );
23
+ }
24
+ return value != null ? value : "";
25
+ };
26
+ const getClientId = (logWarning, config) => {
27
+ const value = config.getOptionalString("glooPlatformPortal.clientId");
28
+ if (!value) {
29
+ logWarning("No glooPlatformPortal.clientId found in app-config.local.yaml");
30
+ }
31
+ return value != null ? value : "";
32
+ };
33
+ const getTokenEndpoint = (logWarning, config) => {
34
+ const value = config.getOptionalString("glooPlatformPortal.tokenEndpoint");
35
+ if (!value) {
36
+ logWarning(
37
+ "No glooPlatformPortal.tokenEndpoint found in app-config.local.yaml"
38
+ );
39
+ }
40
+ return value != null ? value : "";
41
+ };
42
+ const getServiceAccountPassword = (logWarning, config) => {
43
+ const value = config.getOptionalString(
44
+ "glooPlatformPortal.serviceAccountPassword"
45
+ );
46
+ if (!value) {
47
+ logWarning(
48
+ "No glooPlatformPortal.serviceAccountPassword found in app-config.local.yaml"
49
+ );
50
+ }
51
+ return value != null ? value : "";
52
+ };
53
+ const getServiceAccountUsername = (logWarning, config) => {
54
+ const value = config.getOptionalString(
55
+ "glooPlatformPortal.serviceAccountUsername"
56
+ );
57
+ if (!value) {
58
+ logWarning(
59
+ "No glooPlatformPortal.serviceAccountUsername found in app-config.local.yaml"
60
+ );
61
+ }
62
+ return value != null ? value : "";
63
+ };
64
+
65
+ function parseJwt(token) {
66
+ const base64Url = token.split(".")[1];
67
+ const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
68
+ const jsonPayload = decodeURIComponent(
69
+ atob(base64).split("").map((c) => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`).join("")
70
+ );
71
+ return JSON.parse(jsonPayload);
72
+ }
73
+ function objectToUrlFormEncodedPayload(requestJSON) {
74
+ const formBodyPieces = [];
75
+ for (var property in requestJSON) {
76
+ var encodedKey = encodeURIComponent(property);
77
+ var encodedValue = encodeURIComponent(
78
+ requestJSON[property]
79
+ );
80
+ formBodyPieces.push(encodedKey + "=" + encodedValue);
81
+ }
82
+ const formBodyString = formBodyPieces.join("&");
83
+ return formBodyString;
84
+ }
85
+ async function doAccessTokenRequest(grantType, tokenEndpoint, clientId, clientSecret, username, password, refreshToken) {
86
+ const formData = {};
87
+ formData.grant_type = grantType;
88
+ formData.client_id = clientId;
89
+ formData.client_secret = clientSecret;
90
+ if (grantType === "refresh_token") {
91
+ if (!refreshToken) {
92
+ return void 0;
93
+ }
94
+ formData.refresh_token = refreshToken;
95
+ }
96
+ if (grantType === "password") {
97
+ formData.username = username;
98
+ formData.password = password;
99
+ }
100
+ const rawRes = await fetch__default["default"](tokenEndpoint, {
101
+ headers: {
102
+ "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"
103
+ },
104
+ method: "POST",
105
+ body: objectToUrlFormEncodedPayload(formData)
106
+ });
107
+ let resJSON;
108
+ try {
109
+ resJSON = await rawRes.json();
110
+ } catch {
111
+ throw new Error("Error parsing oauth response.");
112
+ }
113
+ if (!!resJSON.error_description) {
114
+ throw new Error(resJSON.error_description);
115
+ }
116
+ if (!!resJSON.error) {
117
+ throw new Error(resJSON.error);
118
+ }
119
+ if (!resJSON.access_token) {
120
+ throw new Error(
121
+ "No 'access_token' property was found in the oauth response body."
122
+ );
123
+ }
124
+ return resJSON;
125
+ }
126
+
127
+ class GlooPlatformPortalProvider {
128
+ //
129
+ // 1. Init class
130
+ //
131
+ constructor(env, logger, config) {
132
+ this.log = (s) => this.logger.info(`gloo-platform-portal: ${s}`);
133
+ this.warn = (s) => this.logger.warn(`gloo-platform-portal: ${s}`);
134
+ this.error = (s) => this.logger.error(`gloo-platform-portal: ${s}`);
135
+ this.getProviderName = () => `gloo-platform-portal-${this.env}`;
136
+ this.env = env;
137
+ this.logger = logger;
138
+ this.config = config;
139
+ this.log("Initializing GlooPlatformPortalProvider.");
140
+ this.startTokensRequests();
141
+ }
142
+ async connect(connection) {
143
+ this.connection = connection;
144
+ }
145
+ //
146
+ // 2. Get access_token
147
+ //
148
+ async startTokensRequests() {
149
+ const res = await doAccessTokenRequest(
150
+ "password",
151
+ getTokenEndpoint(this.warn, this.config),
152
+ getClientId(this.warn, this.config),
153
+ getClientSecret(this.warn, this.config),
154
+ getServiceAccountUsername(this.warn, this.config),
155
+ getServiceAccountPassword(this.warn, this.config)
156
+ );
157
+ this.latestTokensResponse = res;
158
+ this.refreshTheToken();
159
+ }
160
+ /**
161
+ *
162
+ * 3. Get refresh_tokens.
163
+ *
164
+ * Calling this will refresh the access_token when it is expiring soon,
165
+ * using the refresh_token in the access tokens response.
166
+ * */
167
+ async refreshTheToken() {
168
+ const restartAccessTokenRequests = () => {
169
+ this.warn("No latest access token. Re-requesting the access_token.");
170
+ setTimeout(this.startTokensRequests, 5e3);
171
+ };
172
+ if (!this.latestTokensResponse) {
173
+ restartAccessTokenRequests();
174
+ return;
175
+ }
176
+ const parsedToken = parseJwt(this.latestTokensResponse.access_token);
177
+ if (!parsedToken.exp) {
178
+ this.warn("No `exp` property found in the access_token JWT.");
179
+ }
180
+ const nowDate = /* @__PURE__ */ new Date();
181
+ const expiresDate = new Date(parsedToken.exp * 1e3);
182
+ const millisUntilExpires = expiresDate.getTime() - nowDate.getTime();
183
+ if (millisUntilExpires <= 0) {
184
+ this.warn("access token is expired!");
185
+ this.latestTokensResponse = void 0;
186
+ return;
187
+ }
188
+ setTimeout(
189
+ async () => {
190
+ if (!this.latestTokensResponse) {
191
+ restartAccessTokenRequests();
192
+ return;
193
+ }
194
+ try {
195
+ const res = await doAccessTokenRequest(
196
+ "refresh_token",
197
+ getTokenEndpoint(this.warn, this.config),
198
+ getClientId(this.warn, this.config),
199
+ getClientSecret(this.warn, this.config),
200
+ getServiceAccountUsername(this.warn, this.config),
201
+ getServiceAccountPassword(this.warn, this.config),
202
+ this.latestTokensResponse.refresh_token
203
+ );
204
+ this.latestTokensResponse = res;
205
+ this.refreshTheToken();
206
+ } catch (e) {
207
+ this.warn(e);
208
+ }
209
+ },
210
+ // Don't make this request more than once a second,
211
+ // and do the refresh 5 seconds early.
212
+ Math.max(1e3, millisUntilExpires - 5e3)
213
+ );
214
+ }
215
+ /**
216
+ *
217
+ * 4. Return new Backstage entities.
218
+ *
219
+ * Requests API information from the Gloo Platform Portal REST server,
220
+ * and transforms the response into Backstage API entities.
221
+ */
222
+ async run() {
223
+ if (!this.connection || !this.latestTokensResponse) {
224
+ throw new Error("Not initialized");
225
+ }
226
+ let entities = [];
227
+ const bsGroupName = "solo-io-service-accounts";
228
+ const bsServiceAccountName = "gloo-platform-portal-service-account";
229
+ const bsSystemName = "gloo-platform-portal-apis";
230
+ const portalServerUrl = getPortalServerUrl(this.warn, this.config);
231
+ const apisEndpoint = portalServerUrl + "/apis";
232
+ try {
233
+ const res = await fetch__default["default"](apisEndpoint, {
234
+ headers: {
235
+ Authorization: "Bearer " + this.latestTokensResponse.access_token
236
+ }
237
+ });
238
+ const apiProducts = await res.json();
239
+ for (let i = 0; i < apiProducts.length; i++) {
240
+ const apiProduct = apiProducts[i];
241
+ for (let j = 0; j < apiProduct.apiVersions.length; j++) {
242
+ const apiVersion = apiProduct.apiVersions[j];
243
+ const schemaRes = await fetch__default["default"](
244
+ apisEndpoint + "/" + apiVersion.apiId + "/schema",
245
+ {
246
+ headers: {
247
+ Authorization: "Bearer " + this.latestTokensResponse.access_token
248
+ }
249
+ }
250
+ );
251
+ const schema = await schemaRes.json();
252
+ entities.push({
253
+ apiVersion: "backstage.io/v1alpha1",
254
+ kind: "API",
255
+ metadata: {
256
+ tags: ["gloo-platform", "api-version", apiVersion.apiVersion],
257
+ name: apiVersion.apiId,
258
+ title: apiVersion.apiId,
259
+ description: apiVersion.description,
260
+ annotations: {
261
+ "backstage.io/managed-by-location": "url:" + apisEndpoint,
262
+ "backstage.io/managed-by-origin-location": "url:" + apisEndpoint
263
+ }
264
+ },
265
+ spec: {
266
+ type: "openapi",
267
+ lifecycle: "production",
268
+ system: bsSystemName,
269
+ owner: "user:" + bsServiceAccountName,
270
+ // definition: 'openapi: "3.0.0"',
271
+ definition: JSON.stringify(schema)
272
+ }
273
+ });
274
+ }
275
+ }
276
+ } catch (e) {
277
+ this.error(
278
+ `Could not get APIs from the portal server endpoint (${apisEndpoint}). Error: ${JSON.stringify(
279
+ e
280
+ )}`
281
+ );
282
+ }
283
+ const locationKey = `gloo-platform-portal-provider:${this.env}`;
284
+ await this.connection.applyMutation({
285
+ type: "full",
286
+ entities: [
287
+ {
288
+ locationKey,
289
+ entity: {
290
+ apiVersion: "backstage.io/v1alpha1",
291
+ kind: "Group",
292
+ metadata: {
293
+ name: bsGroupName,
294
+ annotations: {
295
+ "backstage.io/managed-by-location": "url:" + portalServerUrl,
296
+ "backstage.io/managed-by-origin-location": "url:" + portalServerUrl
297
+ }
298
+ },
299
+ spec: {
300
+ type: "service-account-group",
301
+ children: [],
302
+ members: [bsServiceAccountName]
303
+ }
304
+ }
305
+ },
306
+ {
307
+ locationKey,
308
+ entity: {
309
+ apiVersion: "backstage.io/v1alpha1",
310
+ kind: "User",
311
+ metadata: {
312
+ name: bsServiceAccountName,
313
+ annotations: {
314
+ "backstage.io/managed-by-location": "url:" + portalServerUrl,
315
+ "backstage.io/managed-by-origin-location": "url:" + portalServerUrl
316
+ }
317
+ },
318
+ spec: {
319
+ displayName: "Solo.io Service Account",
320
+ email: "",
321
+ picture: "",
322
+ memberOf: [bsGroupName]
323
+ }
324
+ }
325
+ },
326
+ // {
327
+ // locationKey,
328
+ // entity: {
329
+ // apiVersion: 'backstage.io/v1alpha1',
330
+ // kind: 'Domain',
331
+ // metadata: {
332
+ // tags: ['gloo-platform'],
333
+ // name: 'api-product',
334
+ // description: 'Gloo Platform Portal ApiProduct resources.',
335
+ // annotations: {
336
+ // 'backstage.io/managed-by-location': 'url:' + apisEndpoint,
337
+ // 'backstage.io/managed-by-origin-location':
338
+ // 'url:' + apisEndpoint,
339
+ // },
340
+ // } as EntityMeta,
341
+ // spec: {
342
+ // owner: 'user:' + bsServiceAccountName,
343
+ // },
344
+ // },
345
+ // },
346
+ {
347
+ locationKey,
348
+ entity: {
349
+ apiVersion: "backstage.io/v1alpha1",
350
+ kind: "System",
351
+ metadata: {
352
+ tags: ["gloo-platform"],
353
+ name: bsSystemName,
354
+ title: "Gloo Platform Portal APIs",
355
+ annotations: {
356
+ "backstage.io/managed-by-location": "url:" + apisEndpoint,
357
+ "backstage.io/managed-by-origin-location": "url:" + apisEndpoint
358
+ }
359
+ },
360
+ spec: {
361
+ owner: "user:" + bsServiceAccountName
362
+ // domain: 'api-product',
363
+ }
364
+ }
365
+ },
366
+ ...entities.map((entity) => ({ locationKey, entity }))
367
+ ]
368
+ });
369
+ }
370
+ }
371
+
372
+ exports.GlooPlatformPortalProvider = GlooPlatformPortalProvider;
373
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/provider/configHelpers.ts","../src/provider/utility.ts","../src/provider/GlooPlatformPortalProvider.ts"],"sourcesContent":["import { Config } from '@backstage/config';\n\ntype logFn = (s: string) => any;\n\nexport const getPortalServerUrl = (_: logFn, config: Config) => {\n let value = config.getOptionalString('glooPlatformPortal.portalServerUrl');\n // Remove trailing slash if supplied.\n if (!!value && value.at(-1) === '/')\n value = value.substring(0, value.length - 1);\n return value ?? 'http://localhost:31080/v1';\n};\n\nexport const getClientSecret = (logWarning: logFn, config: Config) => {\n const value = config.getOptionalString('glooPlatformPortal.clientSecret');\n if (!value) {\n logWarning(\n 'No glooPlatformPortal.clientSecret found in app-config.local.yaml',\n );\n }\n return value ?? '';\n};\n\nexport const getClientId = (logWarning: logFn, config: Config) => {\n const value = config.getOptionalString('glooPlatformPortal.clientId');\n if (!value) {\n logWarning('No glooPlatformPortal.clientId found in app-config.local.yaml');\n }\n return value ?? '';\n};\n\nexport const getTokenEndpoint = (logWarning: logFn, config: Config) => {\n const value = config.getOptionalString('glooPlatformPortal.tokenEndpoint');\n if (!value) {\n logWarning(\n 'No glooPlatformPortal.tokenEndpoint found in app-config.local.yaml',\n );\n }\n return value ?? '';\n};\n\nexport const getServiceAccountPassword = (\n logWarning: logFn,\n config: Config,\n) => {\n const value = config.getOptionalString(\n 'glooPlatformPortal.serviceAccountPassword',\n );\n if (!value) {\n logWarning(\n 'No glooPlatformPortal.serviceAccountPassword found in app-config.local.yaml',\n );\n }\n return value ?? '';\n};\n\nexport const getServiceAccountUsername = (\n logWarning: logFn,\n config: Config,\n) => {\n const value = config.getOptionalString(\n 'glooPlatformPortal.serviceAccountUsername',\n );\n if (!value) {\n logWarning(\n 'No glooPlatformPortal.serviceAccountUsername found in app-config.local.yaml',\n );\n }\n return value ?? '';\n};\n","import { AccessTokensResponse } from '@solo.io/dev-portal-backstage-plugin/src/Apis/api-types';\nimport fetch from 'node-fetch';\n\nexport function parseJwt(token: string) {\n const base64Url = token.split('.')[1];\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n const jsonPayload = decodeURIComponent(\n atob(base64)\n .split('')\n .map(c => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`)\n .join(''),\n );\n return JSON.parse(jsonPayload);\n}\n\nexport function objectToUrlFormEncodedPayload(\n requestJSON: Record<string, string>,\n) {\n const formBodyPieces = [] as string[];\n for (var property in requestJSON) {\n var encodedKey = encodeURIComponent(property);\n var encodedValue = encodeURIComponent(\n requestJSON[property as keyof typeof requestJSON],\n );\n formBodyPieces.push(encodedKey + '=' + encodedValue);\n }\n const formBodyString = formBodyPieces.join('&');\n return formBodyString;\n}\n\nexport async function doAccessTokenRequest(\n grantType: 'refresh_token' | 'password',\n tokenEndpoint: string,\n clientId: string,\n clientSecret: string,\n username: string,\n password: string,\n refreshToken?: string,\n) {\n const formData = {} as Record<string, string>;\n //\n // Build the request payload for a new oauth access token.\n //\n formData.grant_type = grantType;\n formData.client_id = clientId;\n formData.client_secret = clientSecret;\n if (grantType === 'refresh_token') {\n if (!refreshToken) {\n return undefined;\n }\n formData.refresh_token = refreshToken;\n }\n if (grantType === 'password') {\n formData.username = username;\n formData.password = password;\n }\n //\n // Make the request\n //\n const rawRes = await fetch(tokenEndpoint, {\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',\n },\n method: 'POST',\n body: objectToUrlFormEncodedPayload(formData),\n });\n let resJSON: any;\n try {\n resJSON = await rawRes.json();\n } catch {\n throw new Error('Error parsing oauth response.');\n }\n if (!!resJSON.error_description) {\n throw new Error(resJSON.error_description);\n }\n if (!!resJSON.error) {\n throw new Error(resJSON.error);\n }\n //\n // Check for the access token in the response.\n //\n if (!resJSON.access_token) {\n throw new Error(\n \"No 'access_token' property was found in the oauth response body.\",\n );\n }\n return resJSON as AccessTokensResponse;\n}\n","import { Entity, EntityMeta } from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\n\nimport {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-node';\nimport { AccessTokensResponse } from '@solo.io/dev-portal-backstage-plugin/src/Apis/api-types';\nimport fetch from 'node-fetch';\nimport winston from 'winston';\nimport { APIProduct, APISchema } from './api-types';\nimport {\n getClientId,\n getClientSecret,\n getPortalServerUrl,\n getServiceAccountPassword,\n getServiceAccountUsername,\n getTokenEndpoint,\n} from './configHelpers';\nimport { doAccessTokenRequest, parseJwt } from './utility';\n\n/**\n * Provides API entities from the Gloo Platform Portal REST server.\n */\nexport class GlooPlatformPortalProvider implements EntityProvider {\n private readonly env: string;\n private connection?: EntityProviderConnection;\n private logger: winston.Logger;\n private config: Config;\n private latestTokensResponse?: AccessTokensResponse;\n\n log = (s: string) => this.logger.info(`gloo-platform-portal: ${s}`);\n warn = (s: string) => this.logger.warn(`gloo-platform-portal: ${s}`);\n error = (s: string) => this.logger.error(`gloo-platform-portal: ${s}`);\n getProviderName = () => `gloo-platform-portal-${this.env}`;\n async connect(connection: EntityProviderConnection): Promise<void> {\n this.connection = connection;\n }\n\n //\n // 1. Init class\n //\n constructor(env: string, logger: winston.Logger, config: Config) {\n this.env = env;\n this.logger = logger;\n this.config = config;\n this.log('Initializing GlooPlatformPortalProvider.');\n this.startTokensRequests();\n }\n\n //\n // 2. Get access_token\n //\n async startTokensRequests() {\n //\n // Make the initial request for the access_token.\n // this.log('Making the initial access_token request.');\n const res = await doAccessTokenRequest(\n 'password',\n getTokenEndpoint(this.warn, this.config),\n getClientId(this.warn, this.config),\n getClientSecret(this.warn, this.config),\n getServiceAccountUsername(this.warn, this.config),\n getServiceAccountPassword(this.warn, this.config),\n );\n this.latestTokensResponse = res;\n // this.log('Got the initial access_token.');\n //\n // Set up a timeout to get refresh tokens this\n // updates this.latestToken on each callback.\n this.refreshTheToken();\n }\n\n /**\n *\n * 3. Get refresh_tokens.\n *\n * Calling this will refresh the access_token when it is expiring soon,\n * using the refresh_token in the access tokens response.\n * */\n async refreshTheToken() {\n const restartAccessTokenRequests = () => {\n // If there's a problem, wait to restart the access token\n // requests so as to not overload the auth server.\n this.warn('No latest access token. Re-requesting the access_token.');\n setTimeout(this.startTokensRequests, 5000);\n };\n if (!this.latestTokensResponse) {\n restartAccessTokenRequests();\n return;\n }\n //\n // Parse the access_token JWT to find when it expires.\n const parsedToken = parseJwt(this.latestTokensResponse.access_token);\n if (!parsedToken.exp) {\n this.warn('No `exp` property found in the access_token JWT.');\n }\n const nowDate = new Date();\n const expiresDate = new Date(parsedToken.exp * 1000);\n const millisUntilExpires = expiresDate.getTime() - nowDate.getTime();\n if (millisUntilExpires <= 0) {\n this.warn('access token is expired!');\n this.latestTokensResponse = undefined;\n return;\n }\n // this.log('Setting a timeout to refresh the token.');\n // Set the timeout to request new tokens.\n setTimeout(\n async () => {\n if (!this.latestTokensResponse) {\n restartAccessTokenRequests();\n return;\n }\n try {\n // this.log('Making a refresh_token request.');\n const res = await doAccessTokenRequest(\n 'refresh_token',\n getTokenEndpoint(this.warn, this.config),\n getClientId(this.warn, this.config),\n getClientSecret(this.warn, this.config),\n getServiceAccountUsername(this.warn, this.config),\n getServiceAccountPassword(this.warn, this.config),\n this.latestTokensResponse.refresh_token,\n );\n this.latestTokensResponse = res;\n // this.log('Got a new refresh_token.');\n // Recurse\n this.refreshTheToken();\n } catch (e) {\n this.warn(e);\n }\n },\n // Don't make this request more than once a second,\n // and do the refresh 5 seconds early.\n Math.max(1000, millisUntilExpires - 5000),\n );\n }\n\n /**\n *\n * 4. Return new Backstage entities.\n *\n * Requests API information from the Gloo Platform Portal REST server,\n * and transforms the response into Backstage API entities.\n */\n async run(): Promise<void> {\n if (!this.connection || !this.latestTokensResponse) {\n throw new Error('Not initialized');\n }\n\n let entities: Entity[] = [];\n const bsGroupName = 'solo-io-service-accounts';\n const bsServiceAccountName = 'gloo-platform-portal-service-account';\n const bsSystemName = 'gloo-platform-portal-apis';\n const portalServerUrl = getPortalServerUrl(this.warn, this.config);\n const apisEndpoint = portalServerUrl + '/apis';\n //\n // Make API request\n try {\n // TODO: Update this request once the server can optionally include the schema string in the response.\n const res = await fetch(apisEndpoint, {\n headers: {\n Authorization: 'Bearer ' + this.latestTokensResponse.access_token,\n },\n });\n const apiProducts = (await res.json()) as APIProduct[];\n // this.log(JSON.stringify(apiProducts));\n //\n // Convert the APIs to entities\n for (let i = 0; i < apiProducts.length; i++) {\n const apiProduct = apiProducts[i];\n // entities.push({\n // apiVersion: 'backstage.io/v1alpha1',\n // kind: 'Component',\n // metadata: {\n // tags: ['gloo-platform', 'api-product'],\n // name: apiProduct.apiProductId,\n // title: apiProduct.apiProductDisplayName,\n // description: 'This is a Gloo Platform Portal ApiProduct.',\n // annotations: {\n // 'backstage.io/managed-by-location': 'url:' + apisEndpoint,\n // 'backstage.io/managed-by-origin-location': 'url:' + apisEndpoint,\n // },\n // } as EntityMeta,\n // spec: {\n // type: 'service',\n // lifecycle: 'production',\n // owner: 'user:' + bsServiceAccountName,\n // providesApis: apiProduct.apiVersions.map(api => api.apiId),\n // system: bsSystemName,\n // },\n // });\n for (let j = 0; j < apiProduct.apiVersions.length; j++) {\n const apiVersion = apiProduct.apiVersions[j];\n // TODO: Remove this once the schema is fetched along with the rest of the api info.\n const schemaRes = await fetch(\n apisEndpoint + '/' + apiVersion.apiId + '/schema',\n {\n headers: {\n Authorization:\n 'Bearer ' + this.latestTokensResponse.access_token,\n },\n },\n );\n const schema = (await schemaRes.json()) as APISchema;\n // this.log(JSON.stringify(schema));\n entities.push({\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'API',\n metadata: {\n tags: ['gloo-platform', 'api-version', apiVersion.apiVersion],\n name: apiVersion.apiId,\n title: apiVersion.apiId,\n description: apiVersion.description,\n annotations: {\n 'backstage.io/managed-by-location': 'url:' + apisEndpoint,\n 'backstage.io/managed-by-origin-location':\n 'url:' + apisEndpoint,\n },\n } as EntityMeta,\n spec: {\n type: 'openapi',\n lifecycle: 'production',\n system: bsSystemName,\n owner: 'user:' + bsServiceAccountName,\n // definition: 'openapi: \"3.0.0\"',\n definition: JSON.stringify(schema),\n },\n });\n }\n }\n // this.log(JSON.stringify(entities));\n } catch (e) {\n this.error(\n `Could not get APIs from the portal server endpoint (${apisEndpoint}). Error: ${JSON.stringify(\n e,\n )}`,\n );\n }\n\n const locationKey = `gloo-platform-portal-provider:${this.env}`;\n await this.connection.applyMutation({\n type: 'full',\n entities: [\n {\n locationKey,\n entity: {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'Group',\n metadata: {\n name: bsGroupName,\n annotations: {\n 'backstage.io/managed-by-location': 'url:' + portalServerUrl,\n 'backstage.io/managed-by-origin-location':\n 'url:' + portalServerUrl,\n },\n },\n spec: {\n type: 'service-account-group',\n children: [],\n members: [bsServiceAccountName],\n },\n },\n },\n {\n locationKey,\n entity: {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'User',\n metadata: {\n name: bsServiceAccountName,\n annotations: {\n 'backstage.io/managed-by-location': 'url:' + portalServerUrl,\n 'backstage.io/managed-by-origin-location':\n 'url:' + portalServerUrl,\n },\n },\n spec: {\n displayName: 'Solo.io Service Account',\n email: '',\n picture: '',\n memberOf: [bsGroupName],\n },\n },\n },\n // {\n // locationKey,\n // entity: {\n // apiVersion: 'backstage.io/v1alpha1',\n // kind: 'Domain',\n // metadata: {\n // tags: ['gloo-platform'],\n // name: 'api-product',\n // description: 'Gloo Platform Portal ApiProduct resources.',\n // annotations: {\n // 'backstage.io/managed-by-location': 'url:' + apisEndpoint,\n // 'backstage.io/managed-by-origin-location':\n // 'url:' + apisEndpoint,\n // },\n // } as EntityMeta,\n // spec: {\n // owner: 'user:' + bsServiceAccountName,\n // },\n // },\n // },\n {\n locationKey,\n entity: {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'System',\n metadata: {\n tags: ['gloo-platform'],\n name: bsSystemName,\n title: 'Gloo Platform Portal APIs',\n annotations: {\n 'backstage.io/managed-by-location': 'url:' + apisEndpoint,\n 'backstage.io/managed-by-origin-location':\n 'url:' + apisEndpoint,\n },\n } as EntityMeta,\n spec: {\n owner: 'user:' + bsServiceAccountName,\n // domain: 'api-product',\n },\n },\n },\n ...entities.map(entity => ({ locationKey, entity })),\n ],\n });\n }\n}\n"],"names":["fetch"],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { Config } from '@backstage/config';
2
+ import { EntityProvider, EntityProviderConnection } from '@backstage/plugin-catalog-node';
3
+ import winston from 'winston';
4
+
5
+ /**
6
+ * Provides API entities from the Gloo Platform Portal REST server.
7
+ */
8
+ declare class GlooPlatformPortalProvider implements EntityProvider {
9
+ private readonly env;
10
+ private connection?;
11
+ private logger;
12
+ private config;
13
+ private latestTokensResponse?;
14
+ log: (s: string) => winston.Logger;
15
+ warn: (s: string) => winston.Logger;
16
+ error: (s: string) => winston.Logger;
17
+ getProviderName: () => string;
18
+ connect(connection: EntityProviderConnection): Promise<void>;
19
+ constructor(env: string, logger: winston.Logger, config: Config);
20
+ startTokensRequests(): Promise<void>;
21
+ /**
22
+ *
23
+ * 3. Get refresh_tokens.
24
+ *
25
+ * Calling this will refresh the access_token when it is expiring soon,
26
+ * using the refresh_token in the access tokens response.
27
+ * */
28
+ refreshTheToken(): Promise<void>;
29
+ /**
30
+ *
31
+ * 4. Return new Backstage entities.
32
+ *
33
+ * Requests API information from the Gloo Platform Portal REST server,
34
+ * and transforms the response into Backstage API entities.
35
+ */
36
+ run(): Promise<void>;
37
+ }
38
+
39
+ export { GlooPlatformPortalProvider };
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@solo.io/platform-portal-backstage-plugin-backend",
3
+ "version": "0.0.0",
4
+ "main": "dist/index.cjs.js",
5
+ "types": "dist/index.d.ts",
6
+ "license": "Apache-2.0",
7
+ "publishConfig": {
8
+ "access": "public",
9
+ "main": "dist/index.cjs.js",
10
+ "types": "dist/index.d.ts"
11
+ },
12
+ "backstage": {
13
+ "role": "backend-plugin"
14
+ },
15
+ "scripts": {
16
+ "start": "backstage-cli package start",
17
+ "build": "backstage-cli package build",
18
+ "lint": "backstage-cli package lint",
19
+ "test": "backstage-cli package test",
20
+ "clean": "backstage-cli package clean",
21
+ "prepack": "backstage-cli package prepack",
22
+ "postpack": "backstage-cli package postpack"
23
+ },
24
+ "dependencies": {
25
+ "@backstage/backend-common": "workspace:^",
26
+ "@backstage/config": "workspace:^",
27
+ "@types/express": "*",
28
+ "express": "^4.18.1",
29
+ "express-promise-router": "^4.1.0",
30
+ "node-fetch": "2",
31
+ "winston": "^3.2.1",
32
+ "yn": "^4.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "@backstage/cli": "workspace:^",
36
+ "@types/supertest": "^2.0.8",
37
+ "msw": "^0.49.0",
38
+ "supertest": "^6.2.4"
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "config.d.ts"
43
+ ],
44
+ "configSchema": "config.d.ts"
45
+ }