@solo.io/platform-portal-backstage-plugin-backend 0.0.30 → 0.0.31

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/dist/index.cjs.js CHANGED
@@ -6,61 +6,79 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
6
6
 
7
7
  var fetch__default = /*#__PURE__*/_interopDefaultCompat(fetch);
8
8
 
9
- const getPortalServerUrl = (logErr, _, config) => {
10
- if (!config) {
11
- logErr("No backstage config found when getting portal server url.");
12
- return "";
13
- }
14
- let value = config.getOptionalString(
15
- "glooPlatformPortal.backend.portalServerUrl"
16
- );
17
- if (!!value && value.at(-1) === "/")
18
- value = value.substring(0, value.length - 1);
19
- return value != null ? value : "http://localhost:31080/v1";
9
+ var __defProp$2 = Object.defineProperty;
10
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
+ var __publicField$2 = (obj, key, value) => {
12
+ __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
13
+ return value;
20
14
  };
21
- const getClientSecret = (logErr, logWarning, config) => {
22
- if (!config) {
23
- logErr("No backstage config found when getting client secret.");
24
- return "";
15
+ class ConfigUtil {
16
+ constructor(logErr, logWarning, config) {
17
+ __publicField$2(this, "logErr");
18
+ __publicField$2(this, "logWarning");
19
+ __publicField$2(this, "config");
20
+ this.logErr = logErr;
21
+ this.logWarning = logWarning;
22
+ this.config = config;
25
23
  }
26
- const value = config.getOptionalString(
27
- "glooPlatformPortal.backend.clientSecret"
28
- );
29
- if (!value) {
30
- logWarning(
31
- "No glooPlatformPortal.backend.clientSecret found in app-config.local.yaml"
24
+ getPortalServerUrl() {
25
+ if (!this.config) {
26
+ this.logErr("No backstage config found when getting portal server url.");
27
+ return "";
28
+ }
29
+ let value = this.config.getOptionalString(
30
+ "glooPlatformPortal.backend.portalServerUrl"
32
31
  );
32
+ if (!!value && value.at(-1) === "/")
33
+ value = value.substring(0, value.length - 1);
34
+ return value != null ? value : "http://localhost:31080/v1";
33
35
  }
34
- return value != null ? value : "";
35
- };
36
- const getClientId = (logErr, logWarning, config) => {
37
- if (!config) {
38
- logErr("No backstage config found when getting client id.");
39
- return "";
40
- }
41
- const value = config.getOptionalString("glooPlatformPortal.backend.clientId");
42
- if (!value) {
43
- logWarning(
44
- "No glooPlatformPortal.backend.clientId found in app-config.local.yaml"
36
+ getClientSecret() {
37
+ if (!this.config) {
38
+ this.logErr("No backstage config found when getting client secret.");
39
+ return "";
40
+ }
41
+ const value = this.config.getOptionalString(
42
+ "glooPlatformPortal.backend.clientSecret"
45
43
  );
44
+ if (!value) {
45
+ this.logWarning(
46
+ "No glooPlatformPortal.backend.clientSecret found in app-config.local.yaml"
47
+ );
48
+ }
49
+ return value != null ? value : "";
46
50
  }
47
- return value != null ? value : "";
48
- };
49
- const getTokenEndpoint = (logErr, logWarning, config) => {
50
- if (!config) {
51
- logErr("No backstage config found when getting token endpoint.");
52
- return "";
51
+ getClientId() {
52
+ if (!this.config) {
53
+ this.logErr("No backstage config found when getting client id.");
54
+ return "";
55
+ }
56
+ const value = this.config.getOptionalString(
57
+ "glooPlatformPortal.backend.clientId"
58
+ );
59
+ if (!value) {
60
+ this.logWarning(
61
+ "No glooPlatformPortal.backend.clientId found in app-config.local.yaml"
62
+ );
63
+ }
64
+ return value != null ? value : "";
53
65
  }
54
- const value = config.getOptionalString(
55
- "glooPlatformPortal.backend.tokenEndpoint"
56
- );
57
- if (!value) {
58
- logWarning(
59
- "No glooPlatformPortal.backend.tokenEndpoint found in app-config.local.yaml"
66
+ getTokenEndpoint() {
67
+ if (!this.config) {
68
+ this.logErr("No backstage config found when getting token endpoint.");
69
+ return "";
70
+ }
71
+ const value = this.config.getOptionalString(
72
+ "glooPlatformPortal.backend.tokenEndpoint"
60
73
  );
74
+ if (!value) {
75
+ this.logWarning(
76
+ "No glooPlatformPortal.backend.tokenEndpoint found in app-config.local.yaml"
77
+ );
78
+ }
79
+ return value != null ? value : "";
61
80
  }
62
- return value != null ? value : "";
63
- };
81
+ }
64
82
 
65
83
  function parseJwt(token) {
66
84
  const base64Url = token.split(".")[1];
@@ -137,6 +155,148 @@ const sanitizeStringForEntity = (propertyType, propertyValue) => {
137
155
  );
138
156
  };
139
157
 
158
+ var __defProp$1 = Object.defineProperty;
159
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
160
+ var __publicField$1 = (obj, key, value) => {
161
+ __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
162
+ return value;
163
+ };
164
+ class EntityBuilder {
165
+ constructor() {
166
+ // Backstage catalog metadata.
167
+ __publicField$1(this, "bsGroupName", "solo-io-service-accounts");
168
+ __publicField$1(this, "bsServiceAccountName", "gloo-platform-portal-service-account");
169
+ __publicField$1(this, "bsSystemName", "gloo-platform-portal-apis");
170
+ __publicField$1(this, "apisEndpoint", "");
171
+ __publicField$1(this, "portalServerUrl", "");
172
+ __publicField$1(this, "onApisEndpointChange", (apisEndpoint) => this.apisEndpoint = apisEndpoint);
173
+ __publicField$1(this, "onPortalServerUrlChange", (portalServerUrl) => this.portalServerUrl = portalServerUrl);
174
+ }
175
+ /**
176
+ * A helper function to return a Backstage catalog entity for an API.
177
+ */
178
+ buildApiVersionEntity(apiId, apiVersion, apiDescription, schema) {
179
+ const newEntity = {
180
+ apiVersion: "backstage.io/v1alpha1",
181
+ kind: "API",
182
+ metadata: {
183
+ tags: [
184
+ "gloo-platform",
185
+ ...!!apiVersion ? ["api-version-" + sanitizeStringForEntity("tag", apiVersion)] : []
186
+ ],
187
+ name: sanitizeStringForEntity("name", apiId),
188
+ title: apiId,
189
+ description: apiDescription,
190
+ annotations: {
191
+ "backstage.io/managed-by-location": `url:${this.apisEndpoint}`,
192
+ "backstage.io/managed-by-origin-location": `url:${this.apisEndpoint}`
193
+ }
194
+ },
195
+ spec: {
196
+ type: "openapi",
197
+ lifecycle: "production",
198
+ system: this.bsSystemName,
199
+ owner: `user:${this.bsServiceAccountName}`,
200
+ definition: JSON.stringify(schema)
201
+ }
202
+ };
203
+ return newEntity;
204
+ }
205
+ /**
206
+ * A helper function to return a Backstage catalog EntityProviderMutation object for the GlooPlatformPortalProvider plugin.
207
+ * The returned object includes entities that will be added to the catalog.
208
+ */
209
+ buildEntityProviderMutation(entities) {
210
+ const locationKey = `gloo-platform-portal-provider`;
211
+ const mutationObj = {
212
+ type: "full",
213
+ entities: [
214
+ {
215
+ locationKey,
216
+ entity: {
217
+ apiVersion: "backstage.io/v1alpha1",
218
+ kind: "Group",
219
+ metadata: {
220
+ name: this.bsGroupName,
221
+ annotations: {
222
+ "backstage.io/managed-by-location": `url:${this.portalServerUrl}`,
223
+ "backstage.io/managed-by-origin-location": `url:${this.portalServerUrl}`
224
+ }
225
+ },
226
+ spec: {
227
+ type: "service-account-group",
228
+ children: [],
229
+ members: [this.bsServiceAccountName]
230
+ }
231
+ }
232
+ },
233
+ {
234
+ locationKey,
235
+ entity: {
236
+ apiVersion: "backstage.io/v1alpha1",
237
+ kind: "User",
238
+ metadata: {
239
+ name: this.bsServiceAccountName,
240
+ annotations: {
241
+ "backstage.io/managed-by-location": `url:${this.portalServerUrl}`,
242
+ "backstage.io/managed-by-origin-location": `url:${this.portalServerUrl}`
243
+ }
244
+ },
245
+ spec: {
246
+ displayName: "Solo.io Service Account",
247
+ email: "",
248
+ picture: "",
249
+ memberOf: [this.bsGroupName]
250
+ }
251
+ }
252
+ },
253
+ // {
254
+ // locationKey,
255
+ // entity: {
256
+ // apiVersion: 'backstage.io/v1alpha1',
257
+ // kind: 'Domain',
258
+ // metadata: {
259
+ // tags: ['gloo-platform'],
260
+ // name: 'api-product',
261
+ // description: 'Gloo Platform Portal ApiProduct resources.',
262
+ // annotations: {
263
+ // 'backstage.io/managed-by-location': 'url:' + this.apisEndpoint,
264
+ // 'backstage.io/managed-by-origin-location':
265
+ // 'url:' + this.apisEndpoint,
266
+ // },
267
+ // } as EntityMeta,
268
+ // spec: {
269
+ // owner: 'user:' + bsServiceAccountName,
270
+ // },
271
+ // },
272
+ // },
273
+ {
274
+ locationKey,
275
+ entity: {
276
+ apiVersion: "backstage.io/v1alpha1",
277
+ kind: "System",
278
+ metadata: {
279
+ tags: ["gloo-platform"],
280
+ name: this.bsSystemName,
281
+ title: "Gloo Platform Portal APIs",
282
+ annotations: {
283
+ "backstage.io/managed-by-location": `url:${this.portalServerUrl}`,
284
+ "backstage.io/managed-by-origin-location": `url:${this.portalServerUrl}`
285
+ }
286
+ },
287
+ spec: {
288
+ owner: `user:${this.bsServiceAccountName}`
289
+ // domain: 'api-product',
290
+ }
291
+ }
292
+ },
293
+ ...entities.map((entity) => ({ locationKey, entity }))
294
+ ]
295
+ };
296
+ return mutationObj;
297
+ }
298
+ }
299
+
140
300
  var __defProp = Object.defineProperty;
141
301
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
142
302
  var __publicField = (obj, key, value) => {
@@ -153,6 +313,17 @@ class GlooPlatformPortalProvider {
153
313
  __publicField(this, "config");
154
314
  __publicField(this, "latestTokensResponse");
155
315
  __publicField(this, "debugLogging", false);
316
+ __publicField(this, "isInitialApisRequest", true);
317
+ // Helper classes
318
+ __publicField(this, "configUtil");
319
+ __publicField(this, "entityBuilder");
320
+ /**
321
+ * Defaults to gloo-mesh-gateway backend.
322
+ * This is updated to gloo-gateway if that's what the backend response type uses.
323
+ */
324
+ __publicField(this, "_portalServerType", "gloo-mesh-gateway");
325
+ __publicField(this, "_portalServerUrl", "");
326
+ __publicField(this, "_apisEndpoint", "");
156
327
  __publicField(this, "log", (s) => {
157
328
  var _a;
158
329
  return (_a = this.logger) == null ? void 0 : _a.info(`gloo-platform-portal: ${s}`);
@@ -169,15 +340,39 @@ class GlooPlatformPortalProvider {
169
340
  var _a;
170
341
  this.logger = logger;
171
342
  this.config = config;
343
+ this.configUtil = new ConfigUtil(this.error, this.warn, this.config);
344
+ this.entityBuilder = new EntityBuilder();
172
345
  this.debugLogging = !!((_a = this.config) == null ? void 0 : _a.getOptionalBoolean(
173
346
  "glooPlatformPortal.backend.debugLogging"
174
347
  ));
175
348
  this.log("Initializing GlooPlatformPortalProvider.");
176
349
  this.startTokensRequests().then(() => this.startScheduler(scheduler));
177
350
  }
351
+ get portalServerType() {
352
+ return this._portalServerType;
353
+ }
354
+ get portalServerUrl() {
355
+ return this._portalServerUrl;
356
+ }
357
+ get apisEndpoint() {
358
+ return this._apisEndpoint;
359
+ }
178
360
  async connect(connection) {
179
361
  this.connection = connection;
180
362
  }
363
+ updatePortalServerUrl() {
364
+ this._portalServerUrl = this.configUtil.getPortalServerUrl();
365
+ this.entityBuilder.onPortalServerUrlChange(this.portalServerUrl);
366
+ }
367
+ updateApisEndpoint() {
368
+ let apisPath = this.portalServerType === "gloo-gateway" ? "/api-products" : "/apis";
369
+ this._apisEndpoint = `${this.portalServerUrl}${apisPath}`;
370
+ this.entityBuilder.onApisEndpointChange(this.apisEndpoint);
371
+ }
372
+ updatePortalServerType(newType) {
373
+ this._portalServerType = newType;
374
+ this.updateApisEndpoint();
375
+ }
181
376
  //
182
377
  // 2. Get access_token
183
378
  //
@@ -193,9 +388,9 @@ class GlooPlatformPortalProvider {
193
388
  }
194
389
  const res = await doAccessTokenRequest(
195
390
  "client_credentials",
196
- getTokenEndpoint(this.error, this.warn, this.config),
197
- getClientId(this.error, this.warn, this.config),
198
- getClientSecret(this.error, this.warn, this.config)
391
+ this.configUtil.getTokenEndpoint(),
392
+ this.configUtil.getClientId(),
393
+ this.configUtil.getClientSecret()
199
394
  );
200
395
  this.latestTokensResponse = res;
201
396
  if (!this.latestTokensResponse) {
@@ -301,36 +496,11 @@ class GlooPlatformPortalProvider {
301
496
  throw new Error("Not initialized");
302
497
  }
303
498
  const entities = [];
304
- const bsGroupName = "solo-io-service-accounts";
305
- const bsServiceAccountName = "gloo-platform-portal-service-account";
306
- const bsSystemName = "gloo-platform-portal-apis";
307
- const portalServerUrl = getPortalServerUrl(
308
- this.error,
309
- this.warn,
310
- this.config
311
- );
312
- const apisEndpoint = `${portalServerUrl}/apis`;
499
+ this.updatePortalServerUrl();
500
+ this.updateApisEndpoint();
313
501
  try {
314
- const fullRequestURI = apisEndpoint + "?includeSchema=true";
315
- if (this.debugLogging) {
316
- this.log(
317
- `Fetching APIs from ${fullRequestURI} with header: "Authorization: Bearer ${this.latestTokensResponse.access_token}"`
318
- );
319
- }
320
- const res = await fetch__default.default(fullRequestURI, {
321
- headers: {
322
- Authorization: `Bearer ${this.latestTokensResponse.access_token}`
323
- }
324
- });
325
- const resText = await res.text();
326
- if (this.debugLogging) {
327
- this.log("Performed fetch and recieved the response text: " + resText);
328
- }
329
- let processedAPIs = JSON.parse(resText);
330
- if (this.debugLogging) {
331
- this.log("Parsed the text into JSON.");
332
- }
333
- if (!!(processedAPIs == null ? void 0 : processedAPIs.length) && "apiVersions" in processedAPIs[0]) {
502
+ let processedAPIs = await this.fetchAPIs();
503
+ if (this.portalServerType === "gloo-mesh-gateway" && !!(processedAPIs == null ? void 0 : processedAPIs.length) && "apiVersions" in processedAPIs[0]) {
334
504
  const apiProducts = processedAPIs;
335
505
  processedAPIs = apiProducts.reduce((accum, curProd) => {
336
506
  accum.push(
@@ -364,45 +534,14 @@ class GlooPlatformPortalProvider {
364
534
  }, []);
365
535
  }
366
536
  for (let i = 0; i < processedAPIs.length; i++) {
367
- const apiVersion = processedAPIs[i];
368
- let schema = apiVersion.openapiSpec;
369
- if (!schema && !apiVersion.openapiSpecFetchErr) {
370
- const schemaRes = await fetch__default.default(
371
- `${apisEndpoint}/${apiVersion.apiId}/schema`,
372
- {
373
- headers: {
374
- Authorization: `Bearer ${this.latestTokensResponse.access_token}`
375
- }
376
- }
377
- );
378
- schema = await schemaRes.json();
537
+ const api = processedAPIs[i];
538
+ if ("id" in api) {
539
+ this.updatePortalServerType("gloo-gateway");
540
+ entities.push(await this.getGlooGatewayApiEntity(api));
541
+ } else if ("apiProductId" in api) {
542
+ this.updatePortalServerType("gloo-mesh-gateway");
543
+ entities.push(await this.getGlooMeshGatewayApiEntity(api));
379
544
  }
380
- entities.push({
381
- apiVersion: "backstage.io/v1alpha1",
382
- kind: "API",
383
- metadata: {
384
- tags: [
385
- "gloo-platform",
386
- ...!!apiVersion.apiVersion ? [
387
- "api-version-" + sanitizeStringForEntity("tag", apiVersion.apiVersion)
388
- ] : []
389
- ],
390
- name: sanitizeStringForEntity("name", apiVersion.apiId),
391
- title: apiVersion.apiId,
392
- description: apiVersion.description,
393
- annotations: {
394
- "backstage.io/managed-by-location": `url:${apisEndpoint}`,
395
- "backstage.io/managed-by-origin-location": `url:${apisEndpoint}`
396
- }
397
- },
398
- spec: {
399
- type: "openapi",
400
- lifecycle: "production",
401
- system: bsSystemName,
402
- owner: `user:${bsServiceAccountName}`,
403
- definition: JSON.stringify(schema)
404
- }
405
- });
406
545
  }
407
546
  if (this.debugLogging) {
408
547
  this.log(
@@ -411,97 +550,138 @@ class GlooPlatformPortalProvider {
411
550
  }
412
551
  } catch (e) {
413
552
  this.error(
414
- `Could not get APIs from the portal server endpoint or their schemas or transform them into entities (${apisEndpoint}). Error: ${JSON.stringify(
415
- e
416
- )}`
553
+ `Could not get APIs from the portal server endpoint or their schemas or transform them into entities (${this.apisEndpoint}). Error: ${JSON.stringify(e)}`
417
554
  );
418
555
  }
419
- const locationKey = `gloo-platform-portal-provider`;
420
- await this.connection.applyMutation({
421
- type: "full",
422
- entities: [
423
- {
424
- locationKey,
425
- entity: {
426
- apiVersion: "backstage.io/v1alpha1",
427
- kind: "Group",
428
- metadata: {
429
- name: bsGroupName,
430
- annotations: {
431
- "backstage.io/managed-by-location": `url:${portalServerUrl}`,
432
- "backstage.io/managed-by-origin-location": `url:${portalServerUrl}`
433
- }
434
- },
435
- spec: {
436
- type: "service-account-group",
437
- children: [],
438
- members: [bsServiceAccountName]
439
- }
440
- }
441
- },
442
- {
443
- locationKey,
444
- entity: {
445
- apiVersion: "backstage.io/v1alpha1",
446
- kind: "User",
447
- metadata: {
448
- name: bsServiceAccountName,
449
- annotations: {
450
- "backstage.io/managed-by-location": `url:${portalServerUrl}`,
451
- "backstage.io/managed-by-origin-location": `url:${portalServerUrl}`
452
- }
453
- },
454
- spec: {
455
- displayName: "Solo.io Service Account",
456
- email: "",
457
- picture: "",
458
- memberOf: [bsGroupName]
459
- }
460
- }
461
- },
462
- // {
463
- // locationKey,
464
- // entity: {
465
- // apiVersion: 'backstage.io/v1alpha1',
466
- // kind: 'Domain',
467
- // metadata: {
468
- // tags: ['gloo-platform'],
469
- // name: 'api-product',
470
- // description: 'Gloo Platform Portal ApiProduct resources.',
471
- // annotations: {
472
- // 'backstage.io/managed-by-location': 'url:' + apisEndpoint,
473
- // 'backstage.io/managed-by-origin-location':
474
- // 'url:' + apisEndpoint,
475
- // },
476
- // } as EntityMeta,
477
- // spec: {
478
- // owner: 'user:' + bsServiceAccountName,
479
- // },
480
- // },
481
- // },
556
+ await this.connection.applyMutation(
557
+ this.entityBuilder.buildEntityProviderMutation(entities)
558
+ );
559
+ }
560
+ /**
561
+ * Returns the Backstage catalog entity for the "gloo-gateway" Portal Server API response.
562
+ */
563
+ async getGlooGatewayApiEntity(api) {
564
+ return this.entityBuilder.buildApiVersionEntity(
565
+ api.id,
566
+ api.name,
567
+ api.apiProductDescription,
568
+ api.apiSpec
569
+ );
570
+ }
571
+ /**
572
+ * Returns the Backstage catalog entity for the "gloo-mesh-gateway" Portal Server API response.
573
+ */
574
+ async getGlooMeshGatewayApiEntity(api) {
575
+ if (!this.connection || !this.latestTokensResponse || !this.apisEndpoint) {
576
+ throw new Error("Unable to getGlooMeshGatewayApiEntity");
577
+ }
578
+ let schema = api.openapiSpec;
579
+ if (!schema && !api.openapiSpecFetchErr) {
580
+ const schemaRes = await fetch__default.default(
581
+ `${this.apisEndpoint}/${api.apiId}/schema`,
482
582
  {
483
- locationKey,
484
- entity: {
485
- apiVersion: "backstage.io/v1alpha1",
486
- kind: "System",
487
- metadata: {
488
- tags: ["gloo-platform"],
489
- name: bsSystemName,
490
- title: "Gloo Platform Portal APIs",
491
- annotations: {
492
- "backstage.io/managed-by-location": `url:${apisEndpoint}`,
493
- "backstage.io/managed-by-origin-location": `url:${apisEndpoint}`
494
- }
495
- },
496
- spec: {
497
- owner: `user:${bsServiceAccountName}`
498
- // domain: 'api-product',
499
- }
583
+ headers: {
584
+ Authorization: `Bearer ${this.latestTokensResponse.access_token}`
500
585
  }
501
- },
502
- ...entities.map((entity) => ({ locationKey, entity }))
503
- ]
504
- });
586
+ }
587
+ );
588
+ schema = await schemaRes.json();
589
+ }
590
+ return this.entityBuilder.buildApiVersionEntity(
591
+ api.apiId,
592
+ api.apiVersion,
593
+ api.description,
594
+ schema
595
+ );
596
+ }
597
+ /**
598
+ * A helper function for getting the API's from the Portal Server.
599
+ * This abstracts away the "gloo-gateway"/"gloo-mesh-gateway" portal server details.
600
+ */
601
+ async fetchAPIs() {
602
+ if (!this.connection || !this.latestTokensResponse || !this.apisEndpoint) {
603
+ throw new Error("Unable to fetch APIs");
604
+ }
605
+ let processedAPIs = [];
606
+ let res;
607
+ let resText = "";
608
+ const headers = {
609
+ Authorization: `Bearer ${this.latestTokensResponse.access_token}`
610
+ };
611
+ if (this.debugLogging) {
612
+ const portalServerInfo = this.isInitialApisRequest ? "" : ` (identified as ${this.portalServerType})`;
613
+ this.log(
614
+ `Fetching APIs from ${this.apisEndpoint}${portalServerInfo} with header: "Authorization: Bearer ${this.latestTokensResponse.access_token}"`
615
+ );
616
+ }
617
+ this.isInitialApisRequest = false;
618
+ if (this.portalServerType === "gloo-mesh-gateway") {
619
+ const fullRequestURI = this.apisEndpoint + "?includeSchema=true";
620
+ try {
621
+ res = await fetch__default.default(fullRequestURI, { headers });
622
+ resText = await res.text();
623
+ if (this.debugLogging) {
624
+ this.log(
625
+ "Performed fetch and recieved the response text: " + resText
626
+ );
627
+ }
628
+ processedAPIs = JSON.parse(resText);
629
+ if (this.debugLogging) {
630
+ this.log("Parsed the text into JSON.");
631
+ }
632
+ if (processedAPIs.length > 0 && "versionsCount" in processedAPIs[0]) {
633
+ this.updatePortalServerType("gloo-gateway");
634
+ }
635
+ } catch (e) {
636
+ this.updatePortalServerType("gloo-gateway");
637
+ }
638
+ }
639
+ if (this.portalServerType === "gloo-gateway") {
640
+ res = await fetch__default.default(this.apisEndpoint, { headers });
641
+ resText = await res.text();
642
+ if (this.debugLogging) {
643
+ this.log("Performed fetch and recieved the response text: " + resText);
644
+ }
645
+ const parsedResponse = JSON.parse(resText);
646
+ if (parsedResponse.length > 0 && "apiProductDisplayName" in parsedResponse[0]) {
647
+ this.updatePortalServerType("gloo-mesh-gateway");
648
+ return parsedResponse;
649
+ }
650
+ const summaries = parsedResponse;
651
+ if (this.debugLogging) {
652
+ this.log("Parsed the ApiProductSummary text into JSON.");
653
+ }
654
+ processedAPIs = [];
655
+ for (let i = 0; i < summaries.length; i++) {
656
+ const apiProductSummary = summaries[i];
657
+ const getVersionsUrl = `${this.portalServerUrl}/apis/${apiProductSummary.id}/versions`;
658
+ if (this.debugLogging) {
659
+ this.log(
660
+ `Fetching API versions from ${getVersionsUrl} (identified as ${this.portalServerType}).`
661
+ );
662
+ }
663
+ const versionsRes = await fetch__default.default(getVersionsUrl, { headers });
664
+ const resText2 = await versionsRes.text();
665
+ if (this.debugLogging) {
666
+ this.log(
667
+ `Fetched ${getVersionsUrl} (identified as ${this.portalServerType}) and recieved the response text: ${resText2}`
668
+ );
669
+ }
670
+ const versions = JSON.parse(resText2);
671
+ if (this.debugLogging) {
672
+ this.log("Parsed the ApiVersion text into JSON.");
673
+ }
674
+ if (!!(versions == null ? void 0 : versions.length)) {
675
+ processedAPIs.push(
676
+ ...versions.map((v) => ({
677
+ ...v,
678
+ apiProductDescription: apiProductSummary.description
679
+ }))
680
+ );
681
+ }
682
+ }
683
+ }
684
+ return processedAPIs;
505
685
  }
506
686
  }
507
687
 
@@ -1 +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 = (\n logErr: logFn,\n _: logFn,\n config: Config | undefined,\n) => {\n if (!config) {\n logErr('No backstage config found when getting portal server url.');\n return '';\n }\n let value = config.getOptionalString(\n 'glooPlatformPortal.backend.portalServerUrl',\n );\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 = (\n logErr: logFn,\n logWarning: logFn,\n config: Config,\n) => {\n if (!config) {\n logErr('No backstage config found when getting client secret.');\n return '';\n }\n const value = config.getOptionalString(\n 'glooPlatformPortal.backend.clientSecret',\n );\n if (!value) {\n logWarning(\n 'No glooPlatformPortal.backend.clientSecret found in app-config.local.yaml',\n );\n }\n return value ?? '';\n};\n\nexport const getClientId = (\n logErr: logFn,\n logWarning: logFn,\n config: Config,\n) => {\n if (!config) {\n logErr('No backstage config found when getting client id.');\n return '';\n }\n const value = config.getOptionalString('glooPlatformPortal.backend.clientId');\n if (!value) {\n logWarning(\n 'No glooPlatformPortal.backend.clientId found in app-config.local.yaml',\n );\n }\n return value ?? '';\n};\n\nexport const getTokenEndpoint = (\n logErr: logFn,\n logWarning: logFn,\n config: Config,\n) => {\n if (!config) {\n logErr('No backstage config found when getting token endpoint.');\n return '';\n }\n const value = config.getOptionalString(\n 'glooPlatformPortal.backend.tokenEndpoint',\n );\n if (!value) {\n logWarning(\n 'No glooPlatformPortal.backend.tokenEndpoint found in app-config.local.yaml',\n );\n }\n return value ?? '';\n};\n","import fetch from 'node-fetch';\nimport { AccessTokensResponse } from './api-types';\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 (const property in requestJSON) {\n if (!requestJSON.hasOwnProperty(property)) continue;\n const encodedKey = encodeURIComponent(property);\n const 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' | 'client_credentials',\n tokenEndpoint: string,\n clientId: string,\n clientSecret: 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 //\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\n//\n// From: https://backstage.io/docs/features/software-catalog/descriptor-format\n//\nconst sanitizeRegex = {\n // Each tag must be sequences of [a-z0-9:+#] separated by -, at most 63 characters in total\n tag: /[a-z]|[0-9]|\\:|\\+|\\#|\\-/,\n // Strings of length at least 1, and at most 63\n // Must consist of sequences of [a-z0-9A-Z] possibly separated by one of [-_.].\n name: /[a-z]|[0-9]|[A-Z]|\\-|\\_|\\./,\n // Namespaces must be sequences of [a-zA-Z0-9], possibly separated by -, at most 63 characters in total.\n namespace: /[a-z]|[0-9]|[A-Z]|\\-/,\n};\n\n/**\n * Sanitizes a string before adding it to a backstage entity.\n */\nexport const sanitizeStringForEntity = (\n propertyType: keyof typeof sanitizeRegex,\n propertyValue: string,\n) => {\n return propertyValue\n .split('')\n .map(ch => (!sanitizeRegex[propertyType].test(ch) ? '-' : ch))\n .reduce(\n (prev, cur) =>\n // Don't go over 63 characters.\n prev.length >= 62\n ? prev\n : // Don't repeat \"-\"\n prev.at(-1) === '-' && cur === '-'\n ? prev\n : prev + cur,\n '',\n );\n};\n","import { LoggerService, SchedulerService } from '@backstage/backend-plugin-api';\nimport { Entity, EntityMeta } from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-node';\nimport fetch from 'node-fetch';\nimport { API, APIProduct, APISchema, AccessTokensResponse } from './api-types';\nimport {\n getClientId,\n getClientSecret,\n getPortalServerUrl,\n getTokenEndpoint,\n} from './configHelpers';\nimport {\n doAccessTokenRequest,\n parseJwt,\n sanitizeStringForEntity,\n} from './utility';\n\n/**\n * Provides API entities from the Gloo Platform Portal REST server.\n */\nexport class GlooPlatformPortalProvider implements EntityProvider {\n private connection?: EntityProviderConnection;\n private logger: LoggerService;\n private config: Config;\n private latestTokensResponse?: AccessTokensResponse;\n private debugLogging = false;\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-backend-provider`;\n async connect(connection: EntityProviderConnection): Promise<void> {\n this.connection = connection;\n }\n\n //\n // 1. Init class\n //\n constructor(\n logger: LoggerService,\n config: Config,\n scheduler: SchedulerService,\n ) {\n this.logger = logger;\n this.config = config;\n // Default extra debug-logging to false\n this.debugLogging = !!this.config?.getOptionalBoolean(\n 'glooPlatformPortal.backend.debugLogging',\n );\n this.log('Initializing GlooPlatformPortalProvider.');\n // Get the tokens, then schedule the task to update the catalog.\n this.startTokensRequests().then(() => this.startScheduler(scheduler));\n }\n\n //\n // 2. Get access_token\n //\n async startTokensRequests() {\n //\n // Make the initial request for the access_token.\n if (this.debugLogging) {\n this.log('Making the initial access_token request.');\n }\n if (!this.config) {\n this.error(\n 'Backstage config object not found when doing access token request.',\n );\n return;\n }\n const res = await doAccessTokenRequest(\n 'client_credentials',\n getTokenEndpoint(this.error, this.warn, this.config),\n getClientId(this.error, this.warn, this.config),\n getClientSecret(this.error, this.warn, this.config),\n );\n this.latestTokensResponse = res;\n if (!this.latestTokensResponse) {\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.bind(this), 5000);\n return;\n }\n if (this.debugLogging) {\n this.log('Got the access_token.');\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 if (this.debugLogging) {\n this.log('Setting a timeout to get the next access token.');\n }\n // Set the timeout to request new tokens.\n setTimeout(\n this.startTokensRequests.bind(this),\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 * 3. Schedule sync.\n *\n * This passes the user config into the\n * Backstage plugin task scheduler.\n * */\n async startScheduler(scheduler: SchedulerService) {\n if (this.debugLogging) {\n this.log('Scheduling backstage catalog sync.');\n }\n const frequencyConfig = this.config?.getOptionalConfig(\n 'glooPlatformPortal.backend.syncFrequency',\n );\n // Get frequency from the config.\n const frequency = {\n hours: frequencyConfig?.getOptionalNumber('hours') ?? 0,\n minutes: frequencyConfig?.getOptionalNumber('minutes') ?? 0,\n seconds: frequencyConfig?.getOptionalNumber('seconds') ?? 0,\n milliseconds: frequencyConfig?.getOptionalNumber('milliseconds') ?? 0,\n };\n if (Object.values(frequency).every(v => v === 0)) {\n // If there are no values for frequency, set a resonable default instead of 0.\n frequency.minutes = 5;\n if (this.debugLogging) {\n this.log(\n `No frequency value was set, so the default value of ${frequency.minutes} minutes will be used.`,\n );\n }\n }\n if (this.debugLogging) {\n this.log(`Frequency set to ${JSON.stringify(frequency)}.`);\n }\n // Get timeout from the config.\n const timeoutConfig = this.config?.getOptionalConfig(\n 'glooPlatformPortal.backend.syncTimeout',\n );\n const timeout = {\n hours: timeoutConfig?.getOptionalNumber('hours') ?? 0,\n minutes: timeoutConfig?.getOptionalNumber('minutes') ?? 0,\n seconds: timeoutConfig?.getOptionalNumber('seconds') ?? 0,\n milliseconds: timeoutConfig?.getOptionalNumber('milliseconds') ?? 0,\n };\n if (Object.values(timeout).every(v => v === 0)) {\n // If there are no values for timeout, set a resonable default instead of 0.\n timeout.seconds = 30;\n if (this.debugLogging) {\n this.log(\n `No timeout value was set. The default value of ${timeout.seconds} seconds will be used.`,\n );\n }\n }\n if (this.debugLogging) {\n this.log(`Timeout set to ${JSON.stringify(timeout)}.`);\n }\n // Start the sync task on the Backstage scheduler.\n await scheduler.scheduleTask({\n id: 'run_gloo_platform_portal_refresh',\n fn: async () => {\n await this.run();\n },\n frequency,\n timeout,\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 const 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(\n this.error,\n this.warn,\n this.config,\n );\n const apisEndpoint = `${portalServerUrl}/apis`;\n //\n // Make API request\n try {\n const fullRequestURI = apisEndpoint + '?includeSchema=true';\n if (this.debugLogging) {\n this.log(\n `Fetching APIs from ${fullRequestURI} with header: \"Authorization: Bearer ${this.latestTokensResponse.access_token}\"`,\n );\n }\n const res = await fetch(fullRequestURI, {\n headers: {\n Authorization: `Bearer ${this.latestTokensResponse.access_token}`,\n },\n });\n const resText = await res.text();\n if (this.debugLogging) {\n this.log('Performed fetch and recieved the response text: ' + resText);\n }\n let processedAPIs = JSON.parse(resText) as API[];\n if (this.debugLogging) {\n this.log('Parsed the text into JSON.');\n }\n\n //\n // The server returns the APIs grouped by APIProduct,\n // so we can convert it back to a list here.\n //\n if (!!processedAPIs?.length && 'apiVersions' in processedAPIs[0]) {\n const apiProducts = processedAPIs as unknown as APIProduct[];\n processedAPIs = apiProducts.reduce((accum, curProd) => {\n accum.push(\n ...curProd.apiVersions.reduce((accumVer, api) => {\n if (!!api.openapiSpecFetchErr) {\n this.warn(\n `Schema fetch error for ${api.apiId} : ${JSON.stringify(\n api.openapiSpecFetchErr,\n )}`,\n );\n }\n accumVer.push({\n apiId: api.apiId,\n apiProductDisplayName: curProd.apiProductDisplayName,\n apiProductId: curProd.apiProductId,\n apiVersion: api.apiVersion,\n contact: api.contact,\n customMetadata: api.customMetadata,\n description: api.description,\n license: api.license,\n termsOfService: api.termsOfService,\n title: api.title,\n usagePlans: api.usagePlans,\n openapiSpec: api.openapiSpec,\n openapiSpecFetchErr: api.openapiSpecFetchErr,\n });\n return accumVer;\n }, [] as API[]),\n );\n return accum;\n }, [] as API[]);\n }\n\n //\n // Convert the APIs to entities\n for (let i = 0; i < processedAPIs.length; i++) {\n const apiVersion = processedAPIs[i];\n let schema = apiVersion.openapiSpec;\n if (!schema && !apiVersion.openapiSpecFetchErr) {\n // If the schema was not attempted to be fetched with\n // the /apis call, we individually fetch it here.\n // This is for backwards compatibility only, for\n // when the schema was not in the /apis response.\n const schemaRes = await fetch(\n `${apisEndpoint}/${apiVersion.apiId}/schema`,\n {\n headers: {\n Authorization: `Bearer ${this.latestTokensResponse.access_token}`,\n },\n },\n );\n schema = (await schemaRes.json()) as APISchema;\n }\n entities.push({\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'API',\n metadata: {\n tags: [\n 'gloo-platform',\n ...(!!apiVersion.apiVersion\n ? [\n 'api-version-' +\n sanitizeStringForEntity('tag', apiVersion.apiVersion),\n ]\n : []),\n ],\n name: sanitizeStringForEntity('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': `url:${apisEndpoint}`,\n },\n } as EntityMeta,\n spec: {\n type: 'openapi',\n lifecycle: 'production',\n system: bsSystemName,\n owner: `user:${bsServiceAccountName}`,\n definition: JSON.stringify(schema),\n },\n });\n }\n if (this.debugLogging) {\n this.log(\n 'Transformed APIs into new entities: ' + JSON.stringify(entities),\n );\n }\n } catch (e) {\n this.error(\n `Could not get APIs from the portal server endpoint or their schemas or transform them into entities (${apisEndpoint}). Error: ${JSON.stringify(\n e,\n )}`,\n );\n }\n\n const locationKey = `gloo-platform-portal-provider`;\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': `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': `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': `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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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/provider/ConfigUtil.ts","../src/provider/utility.ts","../src/provider/EntityBuilder.ts","../src/provider/GlooPlatformPortalProvider.ts"],"sourcesContent":["import { Config } from '@backstage/config';\n\ntype logFn = (s: string) => any;\n\nexport class ConfigUtil {\n logErr: logFn;\n logWarning: logFn;\n config: Config;\n\n constructor(logErr: logFn, logWarning: logFn, config: Config) {\n this.logErr = logErr;\n this.logWarning = logWarning;\n this.config = config;\n }\n\n getPortalServerUrl() {\n if (!this.config) {\n this.logErr('No backstage config found when getting portal server url.');\n return '';\n }\n let value = this.config.getOptionalString(\n 'glooPlatformPortal.backend.portalServerUrl',\n );\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\n getClientSecret() {\n if (!this.config) {\n this.logErr('No backstage config found when getting client secret.');\n return '';\n }\n const value = this.config.getOptionalString(\n 'glooPlatformPortal.backend.clientSecret',\n );\n if (!value) {\n this.logWarning(\n 'No glooPlatformPortal.backend.clientSecret found in app-config.local.yaml',\n );\n }\n return value ?? '';\n }\n\n getClientId() {\n if (!this.config) {\n this.logErr('No backstage config found when getting client id.');\n return '';\n }\n const value = this.config.getOptionalString(\n 'glooPlatformPortal.backend.clientId',\n );\n if (!value) {\n this.logWarning(\n 'No glooPlatformPortal.backend.clientId found in app-config.local.yaml',\n );\n }\n return value ?? '';\n }\n\n getTokenEndpoint() {\n if (!this.config) {\n this.logErr('No backstage config found when getting token endpoint.');\n return '';\n }\n const value = this.config.getOptionalString(\n 'glooPlatformPortal.backend.tokenEndpoint',\n );\n if (!value) {\n this.logWarning(\n 'No glooPlatformPortal.backend.tokenEndpoint found in app-config.local.yaml',\n );\n }\n return value ?? '';\n }\n}\n","import fetch from 'node-fetch';\nimport { AccessTokensResponse } from './api-types';\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 (const property in requestJSON) {\n if (!requestJSON.hasOwnProperty(property)) continue;\n const encodedKey = encodeURIComponent(property);\n const 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' | 'client_credentials',\n tokenEndpoint: string,\n clientId: string,\n clientSecret: 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 //\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\n//\n// From: https://backstage.io/docs/features/software-catalog/descriptor-format\n//\nconst sanitizeRegex = {\n // Each tag must be sequences of [a-z0-9:+#] separated by -, at most 63 characters in total\n tag: /[a-z]|[0-9]|\\:|\\+|\\#|\\-/,\n // Strings of length at least 1, and at most 63\n // Must consist of sequences of [a-z0-9A-Z] possibly separated by one of [-_.].\n name: /[a-z]|[0-9]|[A-Z]|\\-|\\_|\\./,\n // Namespaces must be sequences of [a-zA-Z0-9], possibly separated by -, at most 63 characters in total.\n namespace: /[a-z]|[0-9]|[A-Z]|\\-/,\n};\n\n/**\n * Sanitizes a string before adding it to a backstage entity.\n */\nexport const sanitizeStringForEntity = (\n propertyType: keyof typeof sanitizeRegex,\n propertyValue: string,\n) => {\n return propertyValue\n .split('')\n .map(ch => (!sanitizeRegex[propertyType].test(ch) ? '-' : ch))\n .reduce(\n (prev, cur) =>\n // Don't go over 63 characters.\n prev.length >= 62\n ? prev\n : // Don't repeat \"-\"\n prev.at(-1) === '-' && cur === '-'\n ? prev\n : prev + cur,\n '',\n );\n};\n","import { Entity, EntityMeta } from '@backstage/catalog-model';\nimport { EntityProviderMutation } from '@backstage/plugin-catalog-node';\nimport { APISchema } from './api-types';\nimport { sanitizeStringForEntity } from './utility';\n\nexport class EntityBuilder {\n // Backstage catalog metadata.\n private bsGroupName = 'solo-io-service-accounts';\n private bsServiceAccountName = 'gloo-platform-portal-service-account';\n private bsSystemName = 'gloo-platform-portal-apis';\n\n private apisEndpoint = '';\n private portalServerUrl = '';\n\n onApisEndpointChange = (apisEndpoint: string) =>\n (this.apisEndpoint = apisEndpoint);\n onPortalServerUrlChange = (portalServerUrl: string) =>\n (this.portalServerUrl = portalServerUrl);\n\n /**\n * A helper function to return a Backstage catalog entity for an API.\n */\n buildApiVersionEntity(\n apiId: string,\n apiVersion: string | undefined,\n apiDescription: string,\n schema: APISchema | string | undefined,\n ): Entity {\n const newEntity: Entity = {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'API',\n metadata: {\n tags: [\n 'gloo-platform',\n ...(!!apiVersion\n ? ['api-version-' + sanitizeStringForEntity('tag', apiVersion)]\n : []),\n ],\n name: sanitizeStringForEntity('name', apiId),\n title: apiId,\n description: apiDescription,\n annotations: {\n 'backstage.io/managed-by-location': `url:${this.apisEndpoint}`,\n 'backstage.io/managed-by-origin-location': `url:${this.apisEndpoint}`,\n },\n } as EntityMeta,\n spec: {\n type: 'openapi',\n lifecycle: 'production',\n system: this.bsSystemName,\n owner: `user:${this.bsServiceAccountName}`,\n definition: JSON.stringify(schema),\n },\n };\n return newEntity;\n }\n\n /**\n * A helper function to return a Backstage catalog EntityProviderMutation object for the GlooPlatformPortalProvider plugin.\n * The returned object includes entities that will be added to the catalog.\n */\n buildEntityProviderMutation(entities: Entity[]) {\n const locationKey = `gloo-platform-portal-provider`;\n const mutationObj: EntityProviderMutation = {\n type: 'full',\n entities: [\n {\n locationKey,\n entity: {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'Group',\n metadata: {\n name: this.bsGroupName,\n annotations: {\n 'backstage.io/managed-by-location': `url:${this.portalServerUrl}`,\n 'backstage.io/managed-by-origin-location': `url:${this.portalServerUrl}`,\n },\n },\n spec: {\n type: 'service-account-group',\n children: [],\n members: [this.bsServiceAccountName],\n },\n },\n },\n {\n locationKey,\n entity: {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'User',\n metadata: {\n name: this.bsServiceAccountName,\n annotations: {\n 'backstage.io/managed-by-location': `url:${this.portalServerUrl}`,\n 'backstage.io/managed-by-origin-location': `url:${this.portalServerUrl}`,\n },\n },\n spec: {\n displayName: 'Solo.io Service Account',\n email: '',\n picture: '',\n memberOf: [this.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:' + this.apisEndpoint,\n // 'backstage.io/managed-by-origin-location':\n // 'url:' + this.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: this.bsSystemName,\n title: 'Gloo Platform Portal APIs',\n annotations: {\n 'backstage.io/managed-by-location': `url:${this.portalServerUrl}`,\n 'backstage.io/managed-by-origin-location': `url:${this.portalServerUrl}`,\n },\n } as EntityMeta,\n spec: {\n owner: `user:${this.bsServiceAccountName}`,\n // domain: 'api-product',\n },\n },\n },\n ...entities.map(entity => ({ locationKey, entity })),\n ],\n };\n return mutationObj;\n }\n}\n","import { LoggerService, SchedulerService } from '@backstage/backend-plugin-api';\nimport { Entity } from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-node';\nimport fetch from 'node-fetch';\nimport { ConfigUtil } from './ConfigUtil';\nimport { EntityBuilder } from './EntityBuilder';\nimport {\n API,\n APIProduct,\n APISchema,\n AccessTokensResponse,\n ApiProductSummary,\n ApiVersion,\n ApiVersionExtended,\n} from './api-types';\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 connection?: EntityProviderConnection;\n private logger: LoggerService;\n private config: Config;\n private latestTokensResponse?: AccessTokensResponse;\n private debugLogging = false;\n private isInitialApisRequest = true;\n\n // Helper classes\n private configUtil: ConfigUtil;\n private entityBuilder: EntityBuilder;\n\n /**\n * Defaults to gloo-mesh-gateway backend.\n * This is updated to gloo-gateway if that's what the backend response type uses.\n */\n private _portalServerType: 'gloo-mesh-gateway' | 'gloo-gateway' =\n 'gloo-mesh-gateway';\n private get portalServerType() {\n return this._portalServerType;\n }\n private _portalServerUrl = '';\n private get portalServerUrl() {\n return this._portalServerUrl;\n }\n private _apisEndpoint = '';\n private get apisEndpoint() {\n return this._apisEndpoint;\n }\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-backend-provider`;\n async connect(connection: EntityProviderConnection): Promise<void> {\n this.connection = connection;\n }\n\n updatePortalServerUrl() {\n this._portalServerUrl = this.configUtil.getPortalServerUrl();\n this.entityBuilder.onPortalServerUrlChange(this.portalServerUrl);\n }\n\n updateApisEndpoint() {\n let apisPath =\n this.portalServerType === 'gloo-gateway' ? '/api-products' : '/apis';\n this._apisEndpoint = `${this.portalServerUrl}${apisPath}`;\n this.entityBuilder.onApisEndpointChange(this.apisEndpoint);\n }\n\n updatePortalServerType(newType: typeof this.portalServerType) {\n this._portalServerType = newType;\n // When the portal server type changes, the apis endpoint may be updated.\n this.updateApisEndpoint();\n }\n\n //\n // 1. Init class\n //\n constructor(\n logger: LoggerService,\n config: Config,\n scheduler: SchedulerService,\n ) {\n this.logger = logger;\n this.config = config;\n this.configUtil = new ConfigUtil(this.error, this.warn, this.config);\n this.entityBuilder = new EntityBuilder();\n // Default extra debug-logging to false\n this.debugLogging = !!this.config?.getOptionalBoolean(\n 'glooPlatformPortal.backend.debugLogging',\n );\n this.log('Initializing GlooPlatformPortalProvider.');\n // Get the tokens, then schedule the task to update the catalog.\n this.startTokensRequests().then(() => this.startScheduler(scheduler));\n }\n\n //\n // 2. Get access_token\n //\n async startTokensRequests() {\n //\n // Make the initial request for the access_token.\n if (this.debugLogging) {\n this.log('Making the initial access_token request.');\n }\n if (!this.config) {\n this.error(\n 'Backstage config object not found when doing access token request.',\n );\n return;\n }\n const res = await doAccessTokenRequest(\n 'client_credentials',\n this.configUtil.getTokenEndpoint(),\n this.configUtil.getClientId(),\n this.configUtil.getClientSecret(),\n );\n this.latestTokensResponse = res;\n if (!this.latestTokensResponse) {\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.bind(this), 5000);\n return;\n }\n if (this.debugLogging) {\n this.log('Got the access_token.');\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 if (this.debugLogging) {\n this.log('Setting a timeout to get the next access token.');\n }\n // Set the timeout to request new tokens.\n setTimeout(\n this.startTokensRequests.bind(this),\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 * 3. Schedule sync.\n *\n * This passes the user config into the\n * Backstage plugin task scheduler.\n * */\n async startScheduler(scheduler: SchedulerService) {\n if (this.debugLogging) {\n this.log('Scheduling backstage catalog sync.');\n }\n const frequencyConfig = this.config?.getOptionalConfig(\n 'glooPlatformPortal.backend.syncFrequency',\n );\n // Get frequency from the config.\n const frequency = {\n hours: frequencyConfig?.getOptionalNumber('hours') ?? 0,\n minutes: frequencyConfig?.getOptionalNumber('minutes') ?? 0,\n seconds: frequencyConfig?.getOptionalNumber('seconds') ?? 0,\n milliseconds: frequencyConfig?.getOptionalNumber('milliseconds') ?? 0,\n };\n if (Object.values(frequency).every(v => v === 0)) {\n // If there are no values for frequency, set a resonable default instead of 0.\n frequency.minutes = 5;\n if (this.debugLogging) {\n this.log(\n `No frequency value was set, so the default value of ${frequency.minutes} minutes will be used.`,\n );\n }\n }\n if (this.debugLogging) {\n this.log(`Frequency set to ${JSON.stringify(frequency)}.`);\n }\n // Get timeout from the config.\n const timeoutConfig = this.config?.getOptionalConfig(\n 'glooPlatformPortal.backend.syncTimeout',\n );\n const timeout = {\n hours: timeoutConfig?.getOptionalNumber('hours') ?? 0,\n minutes: timeoutConfig?.getOptionalNumber('minutes') ?? 0,\n seconds: timeoutConfig?.getOptionalNumber('seconds') ?? 0,\n milliseconds: timeoutConfig?.getOptionalNumber('milliseconds') ?? 0,\n };\n if (Object.values(timeout).every(v => v === 0)) {\n // If there are no values for timeout, set a resonable default instead of 0.\n timeout.seconds = 30;\n if (this.debugLogging) {\n this.log(\n `No timeout value was set. The default value of ${timeout.seconds} seconds will be used.`,\n );\n }\n }\n if (this.debugLogging) {\n this.log(`Timeout set to ${JSON.stringify(timeout)}.`);\n }\n // Start the sync task on the Backstage scheduler.\n await scheduler.scheduleTask({\n id: 'run_gloo_platform_portal_refresh',\n fn: async () => {\n await this.run();\n },\n frequency,\n timeout,\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 const entities: Entity[] = [];\n this.updatePortalServerUrl();\n this.updateApisEndpoint();\n\n // Make API request\n try {\n let processedAPIs = await this.fetchAPIs();\n\n //\n // Some Gloo Mesh Gateway portal servers returned the APIs grouped by APIProduct,\n // so we can convert it back to a list here.\n //\n if (\n this.portalServerType === 'gloo-mesh-gateway' &&\n !!processedAPIs?.length &&\n 'apiVersions' in processedAPIs[0]\n ) {\n const apiProducts = processedAPIs as unknown as APIProduct[];\n processedAPIs = apiProducts.reduce((accum, curProd) => {\n accum.push(\n ...curProd.apiVersions.reduce((accumVer, api) => {\n if (!!api.openapiSpecFetchErr) {\n this.warn(\n `Schema fetch error for ${api.apiId} : ${JSON.stringify(\n api.openapiSpecFetchErr,\n )}`,\n );\n }\n accumVer.push({\n apiId: api.apiId,\n apiProductDisplayName: curProd.apiProductDisplayName,\n apiProductId: curProd.apiProductId,\n apiVersion: api.apiVersion,\n contact: api.contact,\n customMetadata: api.customMetadata,\n description: api.description,\n license: api.license,\n termsOfService: api.termsOfService,\n title: api.title,\n usagePlans: api.usagePlans,\n openapiSpec: api.openapiSpec,\n openapiSpecFetchErr: api.openapiSpecFetchErr,\n });\n return accumVer;\n }, [] as API[]),\n );\n return accum;\n }, [] as API[]);\n }\n\n // Convert the APIs to entities\n for (let i = 0; i < processedAPIs.length; i++) {\n const api = processedAPIs[i];\n if ('id' in api) {\n //\n // For \"gloo-gateway\"\n //\n this.updatePortalServerType('gloo-gateway');\n entities.push(await this.getGlooGatewayApiEntity(api));\n } else if ('apiProductId' in api) {\n //\n // For \"gloo-mesh-gateway\"\n //\n this.updatePortalServerType('gloo-mesh-gateway');\n entities.push(await this.getGlooMeshGatewayApiEntity(api));\n }\n }\n if (this.debugLogging) {\n this.log(\n 'Transformed APIs into new entities: ' + JSON.stringify(entities),\n );\n }\n } catch (e) {\n this.error(\n `Could not get APIs from the portal server endpoint or their schemas or transform them into entities (${\n this.apisEndpoint\n }). Error: ${JSON.stringify(e)}`,\n );\n }\n\n await this.connection.applyMutation(\n this.entityBuilder.buildEntityProviderMutation(entities),\n );\n }\n\n /**\n * Returns the Backstage catalog entity for the \"gloo-gateway\" Portal Server API response.\n */\n async getGlooGatewayApiEntity(api: ApiVersionExtended): Promise<Entity> {\n return this.entityBuilder.buildApiVersionEntity(\n api.id,\n api.name,\n api.apiProductDescription,\n api.apiSpec,\n );\n }\n\n /**\n * Returns the Backstage catalog entity for the \"gloo-mesh-gateway\" Portal Server API response.\n */\n async getGlooMeshGatewayApiEntity(api: API): Promise<Entity> {\n if (!this.connection || !this.latestTokensResponse || !this.apisEndpoint) {\n throw new Error('Unable to getGlooMeshGatewayApiEntity');\n }\n let schema = api.openapiSpec;\n if (!schema && !api.openapiSpecFetchErr) {\n // If the schema was not attempted to be fetched with\n // the /apis call, we individually fetch it here.\n // This is for backwards compatibility only, for\n // when the schema was not in the /apis response.\n const schemaRes = await fetch(\n `${this.apisEndpoint}/${api.apiId}/schema`,\n {\n headers: {\n Authorization: `Bearer ${this.latestTokensResponse.access_token}`,\n },\n },\n );\n schema = (await schemaRes.json()) as APISchema;\n }\n return this.entityBuilder.buildApiVersionEntity(\n api.apiId,\n api.apiVersion,\n api.description,\n schema,\n );\n }\n\n /**\n * A helper function for getting the API's from the Portal Server.\n * This abstracts away the \"gloo-gateway\"/\"gloo-mesh-gateway\" portal server details.\n */\n async fetchAPIs() {\n if (!this.connection || !this.latestTokensResponse || !this.apisEndpoint) {\n throw new Error('Unable to fetch APIs');\n }\n let processedAPIs: (API | ApiVersionExtended)[] = [];\n let res: any;\n let resText: string = '';\n const headers: fetch.HeaderInit = {\n Authorization: `Bearer ${this.latestTokensResponse.access_token}`,\n };\n\n if (this.debugLogging) {\n // We don't identify the portal server type until after the first request,\n // so wait to show it in order to reduce any confusion.\n const portalServerInfo = this.isInitialApisRequest\n ? ''\n : ` (identified as ${this.portalServerType})`;\n this.log(\n `Fetching APIs from ${this.apisEndpoint}${portalServerInfo} with header: \"Authorization: Bearer ${this.latestTokensResponse.access_token}\"`,\n );\n }\n this.isInitialApisRequest = false;\n\n //\n // For \"gloo-mesh-gateway\"\n //\n if (this.portalServerType === 'gloo-mesh-gateway') {\n const fullRequestURI = this.apisEndpoint + '?includeSchema=true';\n try {\n // Make the request.\n res = await fetch(fullRequestURI, { headers });\n resText = await res.text();\n if (this.debugLogging) {\n this.log(\n 'Performed fetch and recieved the response text: ' + resText,\n );\n }\n processedAPIs = JSON.parse(resText) as API[];\n if (this.debugLogging) {\n this.log('Parsed the text into JSON.');\n }\n\n // Check if this is actually \"gloo-gateway\".\n if (processedAPIs.length > 0 && 'versionsCount' in processedAPIs[0]) {\n this.updatePortalServerType('gloo-gateway');\n }\n } catch (e) {\n // If this doesn't work, change it to \"gloo-gateway\".\n this.updatePortalServerType('gloo-gateway');\n }\n }\n\n //\n // For \"gloo-gateway\"\n //\n if (this.portalServerType === 'gloo-gateway') {\n // Make the request.\n res = await fetch(this.apisEndpoint, { headers });\n resText = await res.text();\n if (this.debugLogging) {\n this.log('Performed fetch and recieved the response text: ' + resText);\n }\n const parsedResponse = JSON.parse(resText) as unknown[];\n\n // Check if this is actually \"gloo-mesh-gateway\".\n if (\n parsedResponse.length > 0 &&\n 'apiProductDisplayName' in (parsedResponse as API[])[0]\n ) {\n this.updatePortalServerType('gloo-mesh-gateway');\n return parsedResponse as API[];\n }\n\n // Fetch the information for each version.\n const summaries = parsedResponse as ApiProductSummary[];\n if (this.debugLogging) {\n this.log('Parsed the ApiProductSummary text into JSON.');\n }\n // Reset the processedAPIs so we can add each version to it.\n processedAPIs = [];\n // We have to do a separate request for each ApiProduct in order to get their versions.\n for (let i = 0; i < summaries.length; i++) {\n const apiProductSummary = summaries[i];\n const getVersionsUrl = `${this.portalServerUrl}/apis/${apiProductSummary.id}/versions`;\n if (this.debugLogging) {\n this.log(\n `Fetching API versions from ${getVersionsUrl} (identified as ${this.portalServerType}).`,\n );\n }\n const versionsRes = await fetch(getVersionsUrl, { headers });\n const resText = await versionsRes.text();\n if (this.debugLogging) {\n this.log(\n `Fetched ${getVersionsUrl} (identified as ${this.portalServerType}) and recieved the response text: ${resText}`,\n );\n }\n const versions = JSON.parse(resText) as ApiVersion[];\n if (this.debugLogging) {\n this.log('Parsed the ApiVersion text into JSON.');\n }\n if (!!versions?.length) {\n // Add each API product's version to the processedAPIs.\n processedAPIs.push(\n ...versions.map(v => ({\n ...v,\n apiProductDescription: apiProductSummary.description,\n })),\n );\n }\n }\n }\n\n return processedAPIs;\n }\n}\n"],"names":["__publicField","fetch","resText"],"mappings":";;;;;;;;;;;;;;AAIO,MAAM,UAAW,CAAA;AAAA,EAKtB,WAAA,CAAY,MAAe,EAAA,UAAA,EAAmB,MAAgB,EAAA;AAJ9D,IAAAA,eAAA,CAAA,IAAA,EAAA,QAAA,CAAA,CAAA;AACA,IAAAA,eAAA,CAAA,IAAA,EAAA,YAAA,CAAA,CAAA;AACA,IAAAA,eAAA,CAAA,IAAA,EAAA,QAAA,CAAA,CAAA;AAGE,IAAA,IAAA,CAAK,MAAS,GAAA,MAAA,CAAA;AACd,IAAA,IAAA,CAAK,UAAa,GAAA,UAAA,CAAA;AAClB,IAAA,IAAA,CAAK,MAAS,GAAA,MAAA,CAAA;AAAA,GAChB;AAAA,EAEA,kBAAqB,GAAA;AACnB,IAAI,IAAA,CAAC,KAAK,MAAQ,EAAA;AAChB,MAAA,IAAA,CAAK,OAAO,2DAA2D,CAAA,CAAA;AACvE,MAAO,OAAA,EAAA,CAAA;AAAA,KACT;AACA,IAAI,IAAA,KAAA,GAAQ,KAAK,MAAO,CAAA,iBAAA;AAAA,MACtB,4CAAA;AAAA,KACF,CAAA;AAEA,IAAA,IAAI,CAAC,CAAC,KAAA,IAAS,KAAM,CAAA,EAAA,CAAG,EAAE,CAAM,KAAA,GAAA;AAC9B,MAAA,KAAA,GAAQ,KAAM,CAAA,SAAA,CAAU,CAAG,EAAA,KAAA,CAAM,SAAS,CAAC,CAAA,CAAA;AAC7C,IAAA,OAAO,KAAS,IAAA,IAAA,GAAA,KAAA,GAAA,2BAAA,CAAA;AAAA,GAClB;AAAA,EAEA,eAAkB,GAAA;AAChB,IAAI,IAAA,CAAC,KAAK,MAAQ,EAAA;AAChB,MAAA,IAAA,CAAK,OAAO,uDAAuD,CAAA,CAAA;AACnE,MAAO,OAAA,EAAA,CAAA;AAAA,KACT;AACA,IAAM,MAAA,KAAA,GAAQ,KAAK,MAAO,CAAA,iBAAA;AAAA,MACxB,yCAAA;AAAA,KACF,CAAA;AACA,IAAA,IAAI,CAAC,KAAO,EAAA;AACV,MAAK,IAAA,CAAA,UAAA;AAAA,QACH,2EAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IAAA,OAAO,KAAS,IAAA,IAAA,GAAA,KAAA,GAAA,EAAA,CAAA;AAAA,GAClB;AAAA,EAEA,WAAc,GAAA;AACZ,IAAI,IAAA,CAAC,KAAK,MAAQ,EAAA;AAChB,MAAA,IAAA,CAAK,OAAO,mDAAmD,CAAA,CAAA;AAC/D,MAAO,OAAA,EAAA,CAAA;AAAA,KACT;AACA,IAAM,MAAA,KAAA,GAAQ,KAAK,MAAO,CAAA,iBAAA;AAAA,MACxB,qCAAA;AAAA,KACF,CAAA;AACA,IAAA,IAAI,CAAC,KAAO,EAAA;AACV,MAAK,IAAA,CAAA,UAAA;AAAA,QACH,uEAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IAAA,OAAO,KAAS,IAAA,IAAA,GAAA,KAAA,GAAA,EAAA,CAAA;AAAA,GAClB;AAAA,EAEA,gBAAmB,GAAA;AACjB,IAAI,IAAA,CAAC,KAAK,MAAQ,EAAA;AAChB,MAAA,IAAA,CAAK,OAAO,wDAAwD,CAAA,CAAA;AACpE,MAAO,OAAA,EAAA,CAAA;AAAA,KACT;AACA,IAAM,MAAA,KAAA,GAAQ,KAAK,MAAO,CAAA,iBAAA;AAAA,MACxB,0CAAA;AAAA,KACF,CAAA;AACA,IAAA,IAAI,CAAC,KAAO,EAAA;AACV,MAAK,IAAA,CAAA,UAAA;AAAA,QACH,4EAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IAAA,OAAO,KAAS,IAAA,IAAA,GAAA,KAAA,GAAA,EAAA,CAAA;AAAA,GAClB;AACF;;ACzEO,SAAS,SAAS,KAAe,EAAA;AACtC,EAAA,MAAM,SAAY,GAAA,KAAA,CAAM,KAAM,CAAA,GAAG,EAAE,CAAC,CAAA,CAAA;AACpC,EAAM,MAAA,MAAA,GAAS,UAAU,OAAQ,CAAA,IAAA,EAAM,GAAG,CAAE,CAAA,OAAA,CAAQ,MAAM,GAAG,CAAA,CAAA;AAC7D,EAAA,MAAM,WAAc,GAAA,kBAAA;AAAA,IAClB,IAAA,CAAK,MAAM,CAAA,CACR,KAAM,CAAA,EAAE,EACR,GAAI,CAAA,CAAA,CAAA,KAAK,CAAI,CAAA,EAAA,CAAA,EAAA,EAAK,CAAE,CAAA,UAAA,CAAW,CAAC,CAAE,CAAA,QAAA,CAAS,EAAE,CAAC,CAAG,CAAA,CAAA,KAAA,CAAM,EAAE,CAAC,CAAA,CAAE,CAC5D,CAAA,IAAA,CAAK,EAAE,CAAA;AAAA,GACZ,CAAA;AACA,EAAO,OAAA,IAAA,CAAK,MAAM,WAAW,CAAA,CAAA;AAC/B,CAAA;AAEO,SAAS,8BACd,WACA,EAAA;AACA,EAAA,MAAM,iBAAiB,EAAC,CAAA;AACxB,EAAA,KAAA,MAAW,YAAY,WAAa,EAAA;AAClC,IAAI,IAAA,CAAC,WAAY,CAAA,cAAA,CAAe,QAAQ,CAAA;AAAG,MAAA,SAAA;AAC3C,IAAM,MAAA,UAAA,GAAa,mBAAmB,QAAQ,CAAA,CAAA;AAC9C,IAAA,MAAM,YAAe,GAAA,kBAAA;AAAA,MACnB,YAAY,QAAoC,CAAA;AAAA,KAClD,CAAA;AACA,IAAA,cAAA,CAAe,IAAK,CAAA,CAAA,EAAG,UAAU,CAAA,CAAA,EAAI,YAAY,CAAE,CAAA,CAAA,CAAA;AAAA,GACrD;AACA,EAAM,MAAA,cAAA,GAAiB,cAAe,CAAA,IAAA,CAAK,GAAG,CAAA,CAAA;AAC9C,EAAO,OAAA,cAAA,CAAA;AACT,CAAA;AAEA,eAAsB,oBACpB,CAAA,SAAA,EACA,aACA,EAAA,QAAA,EACA,cACA,YACA,EAAA;AACA,EAAA,MAAM,WAAW,EAAC,CAAA;AAIlB,EAAA,QAAA,CAAS,UAAa,GAAA,SAAA,CAAA;AACtB,EAAA,QAAA,CAAS,SAAY,GAAA,QAAA,CAAA;AACrB,EAAA,QAAA,CAAS,aAAgB,GAAA,YAAA,CAAA;AAUzB,EAAM,MAAA,MAAA,GAAS,MAAMC,sBAAA,CAAM,aAAe,EAAA;AAAA,IACxC,OAAS,EAAA;AAAA,MACP,cAAgB,EAAA,iDAAA;AAAA,KAClB;AAAA,IACA,MAAQ,EAAA,MAAA;AAAA,IACR,IAAA,EAAM,8BAA8B,QAAQ,CAAA;AAAA,GAC7C,CAAA,CAAA;AACD,EAAI,IAAA,OAAA,CAAA;AACJ,EAAI,IAAA;AACF,IAAU,OAAA,GAAA,MAAM,OAAO,IAAK,EAAA,CAAA;AAAA,GACtB,CAAA,MAAA;AACN,IAAM,MAAA,IAAI,MAAM,+BAA+B,CAAA,CAAA;AAAA,GACjD;AACA,EAAI,IAAA,CAAC,CAAC,OAAA,CAAQ,iBAAmB,EAAA;AAC/B,IAAM,MAAA,IAAI,KAAM,CAAA,OAAA,CAAQ,iBAAiB,CAAA,CAAA;AAAA,GAC3C;AACA,EAAI,IAAA,CAAC,CAAC,OAAA,CAAQ,KAAO,EAAA;AACnB,IAAM,MAAA,IAAI,KAAM,CAAA,OAAA,CAAQ,KAAK,CAAA,CAAA;AAAA,GAC/B;AAIA,EAAI,IAAA,CAAC,QAAQ,YAAc,EAAA;AACzB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,kEAAA;AAAA,KACF,CAAA;AAAA,GACF;AACA,EAAO,OAAA,OAAA,CAAA;AACT,CAAA;AAKA,MAAM,aAAgB,GAAA;AAAA;AAAA,EAEpB,GAAK,EAAA,yBAAA;AAAA;AAAA;AAAA,EAGL,IAAM,EAAA,4BAAA;AAAA;AAAA,EAEN,SAAW,EAAA,sBAAA;AACb,CAAA,CAAA;AAKa,MAAA,uBAAA,GAA0B,CACrC,YAAA,EACA,aACG,KAAA;AACH,EAAA,OAAO,aACJ,CAAA,KAAA,CAAM,EAAE,CAAA,CACR,IAAI,CAAO,EAAA,KAAA,CAAC,aAAc,CAAA,YAAY,EAAE,IAAK,CAAA,EAAE,CAAI,GAAA,GAAA,GAAM,EAAG,CAC5D,CAAA,MAAA;AAAA,IACC,CAAC,IAAM,EAAA,GAAA;AAAA;AAAA,MAEL,IAAA,CAAK,UAAU,EACX,GAAA,IAAA;AAAA;AAAA,QAEF,IAAA,CAAK,GAAG,CAAE,CAAA,CAAA,KAAM,OAAO,GAAQ,KAAA,GAAA,GAC7B,OACA,IAAO,GAAA,GAAA;AAAA,OAAA;AAAA,KAAA;AAAA,IACb,EAAA;AAAA,GACF,CAAA;AACJ,CAAA;;;;;;;;ACjHO,MAAM,aAAc,CAAA;AAAA,EAApB,WAAA,GAAA;AAEL;AAAA,IAAAD,eAAA,CAAA,IAAA,EAAQ,aAAc,EAAA,0BAAA,CAAA,CAAA;AACtB,IAAAA,eAAA,CAAA,IAAA,EAAQ,sBAAuB,EAAA,sCAAA,CAAA,CAAA;AAC/B,IAAAA,eAAA,CAAA,IAAA,EAAQ,cAAe,EAAA,2BAAA,CAAA,CAAA;AAEvB,IAAAA,eAAA,CAAA,IAAA,EAAQ,cAAe,EAAA,EAAA,CAAA,CAAA;AACvB,IAAAA,eAAA,CAAA,IAAA,EAAQ,iBAAkB,EAAA,EAAA,CAAA,CAAA;AAE1B,IAAuBA,eAAA,CAAA,IAAA,EAAA,sBAAA,EAAA,CAAC,YACrB,KAAA,IAAA,CAAK,YAAe,GAAA,YAAA,CAAA,CAAA;AACvB,IAA0BA,eAAA,CAAA,IAAA,EAAA,yBAAA,EAAA,CAAC,eACxB,KAAA,IAAA,CAAK,eAAkB,GAAA,eAAA,CAAA,CAAA;AAAA,GAAA;AAAA;AAAA;AAAA;AAAA,EAK1B,qBACE,CAAA,KAAA,EACA,UACA,EAAA,cAAA,EACA,MACQ,EAAA;AACR,IAAA,MAAM,SAAoB,GAAA;AAAA,MACxB,UAAY,EAAA,uBAAA;AAAA,MACZ,IAAM,EAAA,KAAA;AAAA,MACN,QAAU,EAAA;AAAA,QACR,IAAM,EAAA;AAAA,UACJ,eAAA;AAAA,UACA,GAAI,CAAC,CAAC,UACF,GAAA,CAAC,cAAiB,GAAA,uBAAA,CAAwB,KAAO,EAAA,UAAU,CAAC,CAAA,GAC5D,EAAC;AAAA,SACP;AAAA,QACA,IAAA,EAAM,uBAAwB,CAAA,MAAA,EAAQ,KAAK,CAAA;AAAA,QAC3C,KAAO,EAAA,KAAA;AAAA,QACP,WAAa,EAAA,cAAA;AAAA,QACb,WAAa,EAAA;AAAA,UACX,kCAAA,EAAoC,CAAO,IAAA,EAAA,IAAA,CAAK,YAAY,CAAA,CAAA;AAAA,UAC5D,yCAAA,EAA2C,CAAO,IAAA,EAAA,IAAA,CAAK,YAAY,CAAA,CAAA;AAAA,SACrE;AAAA,OACF;AAAA,MACA,IAAM,EAAA;AAAA,QACJ,IAAM,EAAA,SAAA;AAAA,QACN,SAAW,EAAA,YAAA;AAAA,QACX,QAAQ,IAAK,CAAA,YAAA;AAAA,QACb,KAAA,EAAO,CAAQ,KAAA,EAAA,IAAA,CAAK,oBAAoB,CAAA,CAAA;AAAA,QACxC,UAAA,EAAY,IAAK,CAAA,SAAA,CAAU,MAAM,CAAA;AAAA,OACnC;AAAA,KACF,CAAA;AACA,IAAO,OAAA,SAAA,CAAA;AAAA,GACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,4BAA4B,QAAoB,EAAA;AAC9C,IAAA,MAAM,WAAc,GAAA,CAAA,6BAAA,CAAA,CAAA;AACpB,IAAA,MAAM,WAAsC,GAAA;AAAA,MAC1C,IAAM,EAAA,MAAA;AAAA,MACN,QAAU,EAAA;AAAA,QACR;AAAA,UACE,WAAA;AAAA,UACA,MAAQ,EAAA;AAAA,YACN,UAAY,EAAA,uBAAA;AAAA,YACZ,IAAM,EAAA,OAAA;AAAA,YACN,QAAU,EAAA;AAAA,cACR,MAAM,IAAK,CAAA,WAAA;AAAA,cACX,WAAa,EAAA;AAAA,gBACX,kCAAA,EAAoC,CAAO,IAAA,EAAA,IAAA,CAAK,eAAe,CAAA,CAAA;AAAA,gBAC/D,yCAAA,EAA2C,CAAO,IAAA,EAAA,IAAA,CAAK,eAAe,CAAA,CAAA;AAAA,eACxE;AAAA,aACF;AAAA,YACA,IAAM,EAAA;AAAA,cACJ,IAAM,EAAA,uBAAA;AAAA,cACN,UAAU,EAAC;AAAA,cACX,OAAA,EAAS,CAAC,IAAA,CAAK,oBAAoB,CAAA;AAAA,aACrC;AAAA,WACF;AAAA,SACF;AAAA,QACA;AAAA,UACE,WAAA;AAAA,UACA,MAAQ,EAAA;AAAA,YACN,UAAY,EAAA,uBAAA;AAAA,YACZ,IAAM,EAAA,MAAA;AAAA,YACN,QAAU,EAAA;AAAA,cACR,MAAM,IAAK,CAAA,oBAAA;AAAA,cACX,WAAa,EAAA;AAAA,gBACX,kCAAA,EAAoC,CAAO,IAAA,EAAA,IAAA,CAAK,eAAe,CAAA,CAAA;AAAA,gBAC/D,yCAAA,EAA2C,CAAO,IAAA,EAAA,IAAA,CAAK,eAAe,CAAA,CAAA;AAAA,eACxE;AAAA,aACF;AAAA,YACA,IAAM,EAAA;AAAA,cACJ,WAAa,EAAA,yBAAA;AAAA,cACb,KAAO,EAAA,EAAA;AAAA,cACP,OAAS,EAAA,EAAA;AAAA,cACT,QAAA,EAAU,CAAC,IAAA,CAAK,WAAW,CAAA;AAAA,aAC7B;AAAA,WACF;AAAA,SACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAqBA;AAAA,UACE,WAAA;AAAA,UACA,MAAQ,EAAA;AAAA,YACN,UAAY,EAAA,uBAAA;AAAA,YACZ,IAAM,EAAA,QAAA;AAAA,YACN,QAAU,EAAA;AAAA,cACR,IAAA,EAAM,CAAC,eAAe,CAAA;AAAA,cACtB,MAAM,IAAK,CAAA,YAAA;AAAA,cACX,KAAO,EAAA,2BAAA;AAAA,cACP,WAAa,EAAA;AAAA,gBACX,kCAAA,EAAoC,CAAO,IAAA,EAAA,IAAA,CAAK,eAAe,CAAA,CAAA;AAAA,gBAC/D,yCAAA,EAA2C,CAAO,IAAA,EAAA,IAAA,CAAK,eAAe,CAAA,CAAA;AAAA,eACxE;AAAA,aACF;AAAA,YACA,IAAM,EAAA;AAAA,cACJ,KAAA,EAAO,CAAQ,KAAA,EAAA,IAAA,CAAK,oBAAoB,CAAA,CAAA;AAAA;AAAA,aAE1C;AAAA,WACF;AAAA,SACF;AAAA,QACA,GAAG,QAAS,CAAA,GAAA,CAAI,aAAW,EAAE,WAAA,EAAa,QAAS,CAAA,CAAA;AAAA,OACrD;AAAA,KACF,CAAA;AACA,IAAO,OAAA,WAAA,CAAA;AAAA,GACT;AACF;;;;;;;;AC9HO,MAAM,0BAAqD,CAAA;AAAA;AAAA;AAAA;AAAA,EA2DhE,WAAA,CACE,MACA,EAAA,MAAA,EACA,SACA,EAAA;AA9DF,IAAQ,aAAA,CAAA,IAAA,EAAA,YAAA,CAAA,CAAA;AACR,IAAQ,aAAA,CAAA,IAAA,EAAA,QAAA,CAAA,CAAA;AACR,IAAQ,aAAA,CAAA,IAAA,EAAA,QAAA,CAAA,CAAA;AACR,IAAQ,aAAA,CAAA,IAAA,EAAA,sBAAA,CAAA,CAAA;AACR,IAAA,aAAA,CAAA,IAAA,EAAQ,cAAe,EAAA,KAAA,CAAA,CAAA;AACvB,IAAA,aAAA,CAAA,IAAA,EAAQ,sBAAuB,EAAA,IAAA,CAAA,CAAA;AAG/B;AAAA,IAAQ,aAAA,CAAA,IAAA,EAAA,YAAA,CAAA,CAAA;AACR,IAAQ,aAAA,CAAA,IAAA,EAAA,eAAA,CAAA,CAAA;AAMR;AAAA;AAAA;AAAA;AAAA,IAAA,aAAA,CAAA,IAAA,EAAQ,mBACN,EAAA,mBAAA,CAAA,CAAA;AAIF,IAAA,aAAA,CAAA,IAAA,EAAQ,kBAAmB,EAAA,EAAA,CAAA,CAAA;AAI3B,IAAA,aAAA,CAAA,IAAA,EAAQ,eAAgB,EAAA,EAAA,CAAA,CAAA;AAKxB,IAAA,aAAA,CAAA,IAAA,EAAA,KAAA,EAAM,CAAC,CAAW,KAAA;AAtDpB,MAAA,IAAA,EAAA,CAAA;AAsDuB,MAAA,OAAA,CAAA,EAAA,GAAA,IAAA,CAAK,MAAL,KAAA,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAa,IAAK,CAAA,CAAA,sBAAA,EAAyB,CAAC,CAAA,CAAA,CAAA,CAAA;AAAA,KAAA,CAAA,CAAA;AACjE,IAAA,aAAA,CAAA,IAAA,EAAA,MAAA,EAAO,CAAC,CAAW,KAAA;AAvDrB,MAAA,IAAA,EAAA,CAAA;AAuDwB,MAAA,OAAA,CAAA,EAAA,GAAA,IAAA,CAAK,MAAL,KAAA,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAa,IAAK,CAAA,CAAA,sBAAA,EAAyB,CAAC,CAAA,CAAA,CAAA,CAAA;AAAA,KAAA,CAAA,CAAA;AAClE,IAAA,aAAA,CAAA,IAAA,EAAA,OAAA,EAAQ,CAAC,CAAW,KAAA;AAxDtB,MAAA,IAAA,EAAA,CAAA;AAwDyB,MAAA,OAAA,CAAA,EAAA,GAAA,IAAA,CAAK,MAAL,KAAA,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAa,KAAM,CAAA,CAAA,sBAAA,EAAyB,CAAC,CAAA,CAAA,CAAA,CAAA;AAAA,KAAA,CAAA,CAAA;AACpE,IAAA,aAAA,CAAA,IAAA,EAAA,iBAAA,EAAkB,MAAM,CAAA,qCAAA,CAAA,CAAA,CAAA;AAzD1B,IAAA,IAAA,EAAA,CAAA;AAwFI,IAAA,IAAA,CAAK,MAAS,GAAA,MAAA,CAAA;AACd,IAAA,IAAA,CAAK,MAAS,GAAA,MAAA,CAAA;AACd,IAAK,IAAA,CAAA,UAAA,GAAa,IAAI,UAAW,CAAA,IAAA,CAAK,OAAO,IAAK,CAAA,IAAA,EAAM,KAAK,MAAM,CAAA,CAAA;AACnE,IAAK,IAAA,CAAA,aAAA,GAAgB,IAAI,aAAc,EAAA,CAAA;AAEvC,IAAA,IAAA,CAAK,YAAe,GAAA,CAAC,EAAC,CAAA,EAAA,GAAA,IAAA,CAAK,WAAL,IAAa,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,kBAAA;AAAA,MACjC,yCAAA;AAAA,KAAA,CAAA,CAAA;AAEF,IAAA,IAAA,CAAK,IAAI,0CAA0C,CAAA,CAAA;AAEnD,IAAA,IAAA,CAAK,qBAAsB,CAAA,IAAA,CAAK,MAAM,IAAK,CAAA,cAAA,CAAe,SAAS,CAAC,CAAA,CAAA;AAAA,GACtE;AAAA,EAzDA,IAAY,gBAAmB,GAAA;AAC7B,IAAA,OAAO,IAAK,CAAA,iBAAA,CAAA;AAAA,GACd;AAAA,EAEA,IAAY,eAAkB,GAAA;AAC5B,IAAA,OAAO,IAAK,CAAA,gBAAA,CAAA;AAAA,GACd;AAAA,EAEA,IAAY,YAAe,GAAA;AACzB,IAAA,OAAO,IAAK,CAAA,aAAA,CAAA;AAAA,GACd;AAAA,EAMA,MAAM,QAAQ,UAAqD,EAAA;AACjE,IAAA,IAAA,CAAK,UAAa,GAAA,UAAA,CAAA;AAAA,GACpB;AAAA,EAEA,qBAAwB,GAAA;AACtB,IAAK,IAAA,CAAA,gBAAA,GAAmB,IAAK,CAAA,UAAA,CAAW,kBAAmB,EAAA,CAAA;AAC3D,IAAK,IAAA,CAAA,aAAA,CAAc,uBAAwB,CAAA,IAAA,CAAK,eAAe,CAAA,CAAA;AAAA,GACjE;AAAA,EAEA,kBAAqB,GAAA;AACnB,IAAA,IAAI,QACF,GAAA,IAAA,CAAK,gBAAqB,KAAA,cAAA,GAAiB,eAAkB,GAAA,OAAA,CAAA;AAC/D,IAAA,IAAA,CAAK,aAAgB,GAAA,CAAA,EAAG,IAAK,CAAA,eAAe,GAAG,QAAQ,CAAA,CAAA,CAAA;AACvD,IAAK,IAAA,CAAA,aAAA,CAAc,oBAAqB,CAAA,IAAA,CAAK,YAAY,CAAA,CAAA;AAAA,GAC3D;AAAA,EAEA,uBAAuB,OAAuC,EAAA;AAC5D,IAAA,IAAA,CAAK,iBAAoB,GAAA,OAAA,CAAA;AAEzB,IAAA,IAAA,CAAK,kBAAmB,EAAA,CAAA;AAAA,GAC1B;AAAA;AAAA;AAAA;AAAA,EA0BA,MAAM,mBAAsB,GAAA;AAG1B,IAAA,IAAI,KAAK,YAAc,EAAA;AACrB,MAAA,IAAA,CAAK,IAAI,0CAA0C,CAAA,CAAA;AAAA,KACrD;AACA,IAAI,IAAA,CAAC,KAAK,MAAQ,EAAA;AAChB,MAAK,IAAA,CAAA,KAAA;AAAA,QACH,oEAAA;AAAA,OACF,CAAA;AACA,MAAA,OAAA;AAAA,KACF;AACA,IAAA,MAAM,MAAM,MAAM,oBAAA;AAAA,MAChB,oBAAA;AAAA,MACA,IAAA,CAAK,WAAW,gBAAiB,EAAA;AAAA,MACjC,IAAA,CAAK,WAAW,WAAY,EAAA;AAAA,MAC5B,IAAA,CAAK,WAAW,eAAgB,EAAA;AAAA,KAClC,CAAA;AACA,IAAA,IAAA,CAAK,oBAAuB,GAAA,GAAA,CAAA;AAC5B,IAAI,IAAA,CAAC,KAAK,oBAAsB,EAAA;AAG9B,MAAA,IAAA,CAAK,KAAK,yDAAyD,CAAA,CAAA;AACnE,MAAA,UAAA,CAAW,IAAK,CAAA,mBAAA,CAAoB,IAAK,CAAA,IAAI,GAAG,GAAI,CAAA,CAAA;AACpD,MAAA,OAAA;AAAA,KACF;AACA,IAAA,IAAI,KAAK,YAAc,EAAA;AACrB,MAAA,IAAA,CAAK,IAAI,uBAAuB,CAAA,CAAA;AAAA,KAClC;AAGA,IAAA,MAAM,WAAc,GAAA,QAAA,CAAS,IAAK,CAAA,oBAAA,CAAqB,YAAY,CAAA,CAAA;AACnE,IAAI,IAAA,CAAC,YAAY,GAAK,EAAA;AACpB,MAAA,IAAA,CAAK,KAAK,kDAAkD,CAAA,CAAA;AAAA,KAC9D;AACA,IAAM,MAAA,OAAA,uBAAc,IAAK,EAAA,CAAA;AACzB,IAAA,MAAM,WAAc,GAAA,IAAI,IAAK,CAAA,WAAA,CAAY,MAAM,GAAI,CAAA,CAAA;AACnD,IAAA,MAAM,kBAAqB,GAAA,WAAA,CAAY,OAAQ,EAAA,GAAI,QAAQ,OAAQ,EAAA,CAAA;AACnE,IAAA,IAAI,sBAAsB,CAAG,EAAA;AAC3B,MAAA,IAAA,CAAK,KAAK,0BAA0B,CAAA,CAAA;AACpC,MAAA,IAAA,CAAK,oBAAuB,GAAA,KAAA,CAAA,CAAA;AAC5B,MAAA,OAAA;AAAA,KACF;AACA,IAAA,IAAI,KAAK,YAAc,EAAA;AACrB,MAAA,IAAA,CAAK,IAAI,iDAAiD,CAAA,CAAA;AAAA,KAC5D;AAEA,IAAA,UAAA;AAAA,MACE,IAAA,CAAK,mBAAoB,CAAA,IAAA,CAAK,IAAI,CAAA;AAAA;AAAA;AAAA,MAGlC,IAAK,CAAA,GAAA,CAAI,GAAM,EAAA,kBAAA,GAAqB,GAAI,CAAA;AAAA,KAC1C,CAAA;AAAA,GACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,eAAe,SAA6B,EAAA;AAtKpD,IAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,CAAA;AAuKI,IAAA,IAAI,KAAK,YAAc,EAAA;AACrB,MAAA,IAAA,CAAK,IAAI,oCAAoC,CAAA,CAAA;AAAA,KAC/C;AACA,IAAM,MAAA,eAAA,GAAA,CAAkB,EAAK,GAAA,IAAA,CAAA,MAAA,KAAL,IAAa,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,iBAAA;AAAA,MACnC,0CAAA;AAAA,KAAA,CAAA;AAGF,IAAA,MAAM,SAAY,GAAA;AAAA,MAChB,KAAO,EAAA,CAAA,EAAA,GAAA,eAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,eAAA,CAAiB,iBAAkB,CAAA,OAAA,CAAA,KAAnC,IAA+C,GAAA,EAAA,GAAA,CAAA;AAAA,MACtD,OAAS,EAAA,CAAA,EAAA,GAAA,eAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,eAAA,CAAiB,iBAAkB,CAAA,SAAA,CAAA,KAAnC,IAAiD,GAAA,EAAA,GAAA,CAAA;AAAA,MAC1D,OAAS,EAAA,CAAA,EAAA,GAAA,eAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,eAAA,CAAiB,iBAAkB,CAAA,SAAA,CAAA,KAAnC,IAAiD,GAAA,EAAA,GAAA,CAAA;AAAA,MAC1D,YAAc,EAAA,CAAA,EAAA,GAAA,eAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,eAAA,CAAiB,iBAAkB,CAAA,cAAA,CAAA,KAAnC,IAAsD,GAAA,EAAA,GAAA,CAAA;AAAA,KACtE,CAAA;AACA,IAAI,IAAA,MAAA,CAAO,OAAO,SAAS,CAAA,CAAE,MAAM,CAAK,CAAA,KAAA,CAAA,KAAM,CAAC,CAAG,EAAA;AAEhD,MAAA,SAAA,CAAU,OAAU,GAAA,CAAA,CAAA;AACpB,MAAA,IAAI,KAAK,YAAc,EAAA;AACrB,QAAK,IAAA,CAAA,GAAA;AAAA,UACH,CAAA,oDAAA,EAAuD,UAAU,OAAO,CAAA,sBAAA,CAAA;AAAA,SAC1E,CAAA;AAAA,OACF;AAAA,KACF;AACA,IAAA,IAAI,KAAK,YAAc,EAAA;AACrB,MAAA,IAAA,CAAK,IAAI,CAAoB,iBAAA,EAAA,IAAA,CAAK,SAAU,CAAA,SAAS,CAAC,CAAG,CAAA,CAAA,CAAA,CAAA;AAAA,KAC3D;AAEA,IAAM,MAAA,aAAA,GAAA,CAAgB,EAAK,GAAA,IAAA,CAAA,MAAA,KAAL,IAAa,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,iBAAA;AAAA,MACjC,wCAAA;AAAA,KAAA,CAAA;AAEF,IAAA,MAAM,OAAU,GAAA;AAAA,MACd,KAAO,EAAA,CAAA,EAAA,GAAA,aAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,aAAA,CAAe,iBAAkB,CAAA,OAAA,CAAA,KAAjC,IAA6C,GAAA,EAAA,GAAA,CAAA;AAAA,MACpD,OAAS,EAAA,CAAA,EAAA,GAAA,aAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,aAAA,CAAe,iBAAkB,CAAA,SAAA,CAAA,KAAjC,IAA+C,GAAA,EAAA,GAAA,CAAA;AAAA,MACxD,OAAS,EAAA,CAAA,EAAA,GAAA,aAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,aAAA,CAAe,iBAAkB,CAAA,SAAA,CAAA,KAAjC,IAA+C,GAAA,EAAA,GAAA,CAAA;AAAA,MACxD,YAAc,EAAA,CAAA,EAAA,GAAA,aAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,aAAA,CAAe,iBAAkB,CAAA,cAAA,CAAA,KAAjC,IAAoD,GAAA,EAAA,GAAA,CAAA;AAAA,KACpE,CAAA;AACA,IAAI,IAAA,MAAA,CAAO,OAAO,OAAO,CAAA,CAAE,MAAM,CAAK,CAAA,KAAA,CAAA,KAAM,CAAC,CAAG,EAAA;AAE9C,MAAA,OAAA,CAAQ,OAAU,GAAA,EAAA,CAAA;AAClB,MAAA,IAAI,KAAK,YAAc,EAAA;AACrB,QAAK,IAAA,CAAA,GAAA;AAAA,UACH,CAAA,+CAAA,EAAkD,QAAQ,OAAO,CAAA,sBAAA,CAAA;AAAA,SACnE,CAAA;AAAA,OACF;AAAA,KACF;AACA,IAAA,IAAI,KAAK,YAAc,EAAA;AACrB,MAAA,IAAA,CAAK,IAAI,CAAkB,eAAA,EAAA,IAAA,CAAK,SAAU,CAAA,OAAO,CAAC,CAAG,CAAA,CAAA,CAAA,CAAA;AAAA,KACvD;AAEA,IAAA,MAAM,UAAU,YAAa,CAAA;AAAA,MAC3B,EAAI,EAAA,kCAAA;AAAA,MACJ,IAAI,YAAY;AACd,QAAA,MAAM,KAAK,GAAI,EAAA,CAAA;AAAA,OACjB;AAAA,MACA,SAAA;AAAA,MACA,OAAA;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,GAAqB,GAAA;AACzB,IAAA,IAAI,CAAC,IAAA,CAAK,UAAc,IAAA,CAAC,KAAK,oBAAsB,EAAA;AAClD,MAAM,MAAA,IAAI,MAAM,iBAAiB,CAAA,CAAA;AAAA,KACnC;AACA,IAAA,MAAM,WAAqB,EAAC,CAAA;AAC5B,IAAA,IAAA,CAAK,qBAAsB,EAAA,CAAA;AAC3B,IAAA,IAAA,CAAK,kBAAmB,EAAA,CAAA;AAGxB,IAAI,IAAA;AACF,MAAI,IAAA,aAAA,GAAgB,MAAM,IAAA,CAAK,SAAU,EAAA,CAAA;AAMzC,MACE,IAAA,IAAA,CAAK,gBAAqB,KAAA,mBAAA,IAC1B,CAAC,EAAC,+CAAe,MACjB,CAAA,IAAA,aAAA,IAAiB,aAAc,CAAA,CAAC,CAChC,EAAA;AACA,QAAA,MAAM,WAAc,GAAA,aAAA,CAAA;AACpB,QAAA,aAAA,GAAgB,WAAY,CAAA,MAAA,CAAO,CAAC,KAAA,EAAO,OAAY,KAAA;AACrD,UAAM,KAAA,CAAA,IAAA;AAAA,YACJ,GAAG,OAAQ,CAAA,WAAA,CAAY,MAAO,CAAA,CAAC,UAAU,GAAQ,KAAA;AAC/C,cAAI,IAAA,CAAC,CAAC,GAAA,CAAI,mBAAqB,EAAA;AAC7B,gBAAK,IAAA,CAAA,IAAA;AAAA,kBACH,CAA0B,uBAAA,EAAA,GAAA,CAAI,KAAK,CAAA,GAAA,EAAM,IAAK,CAAA,SAAA;AAAA,oBAC5C,GAAI,CAAA,mBAAA;AAAA,mBACL,CAAA,CAAA;AAAA,iBACH,CAAA;AAAA,eACF;AACA,cAAA,QAAA,CAAS,IAAK,CAAA;AAAA,gBACZ,OAAO,GAAI,CAAA,KAAA;AAAA,gBACX,uBAAuB,OAAQ,CAAA,qBAAA;AAAA,gBAC/B,cAAc,OAAQ,CAAA,YAAA;AAAA,gBACtB,YAAY,GAAI,CAAA,UAAA;AAAA,gBAChB,SAAS,GAAI,CAAA,OAAA;AAAA,gBACb,gBAAgB,GAAI,CAAA,cAAA;AAAA,gBACpB,aAAa,GAAI,CAAA,WAAA;AAAA,gBACjB,SAAS,GAAI,CAAA,OAAA;AAAA,gBACb,gBAAgB,GAAI,CAAA,cAAA;AAAA,gBACpB,OAAO,GAAI,CAAA,KAAA;AAAA,gBACX,YAAY,GAAI,CAAA,UAAA;AAAA,gBAChB,aAAa,GAAI,CAAA,WAAA;AAAA,gBACjB,qBAAqB,GAAI,CAAA,mBAAA;AAAA,eAC1B,CAAA,CAAA;AACD,cAAO,OAAA,QAAA,CAAA;AAAA,aACT,EAAG,EAAW,CAAA;AAAA,WAChB,CAAA;AACA,UAAO,OAAA,KAAA,CAAA;AAAA,SACT,EAAG,EAAW,CAAA,CAAA;AAAA,OAChB;AAGA,MAAA,KAAA,IAAS,CAAI,GAAA,CAAA,EAAG,CAAI,GAAA,aAAA,CAAc,QAAQ,CAAK,EAAA,EAAA;AAC7C,QAAM,MAAA,GAAA,GAAM,cAAc,CAAC,CAAA,CAAA;AAC3B,QAAA,IAAI,QAAQ,GAAK,EAAA;AAIf,UAAA,IAAA,CAAK,uBAAuB,cAAc,CAAA,CAAA;AAC1C,UAAA,QAAA,CAAS,IAAK,CAAA,MAAM,IAAK,CAAA,uBAAA,CAAwB,GAAG,CAAC,CAAA,CAAA;AAAA,SACvD,MAAA,IAAW,kBAAkB,GAAK,EAAA;AAIhC,UAAA,IAAA,CAAK,uBAAuB,mBAAmB,CAAA,CAAA;AAC/C,UAAA,QAAA,CAAS,IAAK,CAAA,MAAM,IAAK,CAAA,2BAAA,CAA4B,GAAG,CAAC,CAAA,CAAA;AAAA,SAC3D;AAAA,OACF;AACA,MAAA,IAAI,KAAK,YAAc,EAAA;AACrB,QAAK,IAAA,CAAA,GAAA;AAAA,UACH,sCAAA,GAAyC,IAAK,CAAA,SAAA,CAAU,QAAQ,CAAA;AAAA,SAClE,CAAA;AAAA,OACF;AAAA,aACO,CAAG,EAAA;AACV,MAAK,IAAA,CAAA,KAAA;AAAA,QACH,wGACE,IAAK,CAAA,YACP,aAAa,IAAK,CAAA,SAAA,CAAU,CAAC,CAAC,CAAA,CAAA;AAAA,OAChC,CAAA;AAAA,KACF;AAEA,IAAA,MAAM,KAAK,UAAW,CAAA,aAAA;AAAA,MACpB,IAAA,CAAK,aAAc,CAAA,2BAAA,CAA4B,QAAQ,CAAA;AAAA,KACzD,CAAA;AAAA,GACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,wBAAwB,GAA0C,EAAA;AACtE,IAAA,OAAO,KAAK,aAAc,CAAA,qBAAA;AAAA,MACxB,GAAI,CAAA,EAAA;AAAA,MACJ,GAAI,CAAA,IAAA;AAAA,MACJ,GAAI,CAAA,qBAAA;AAAA,MACJ,GAAI,CAAA,OAAA;AAAA,KACN,CAAA;AAAA,GACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,4BAA4B,GAA2B,EAAA;AAC3D,IAAI,IAAA,CAAC,KAAK,UAAc,IAAA,CAAC,KAAK,oBAAwB,IAAA,CAAC,KAAK,YAAc,EAAA;AACxE,MAAM,MAAA,IAAI,MAAM,uCAAuC,CAAA,CAAA;AAAA,KACzD;AACA,IAAA,IAAI,SAAS,GAAI,CAAA,WAAA,CAAA;AACjB,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,GAAA,CAAI,mBAAqB,EAAA;AAKvC,MAAA,MAAM,YAAY,MAAMC,sBAAA;AAAA,QACtB,CAAG,EAAA,IAAA,CAAK,YAAY,CAAA,CAAA,EAAI,IAAI,KAAK,CAAA,OAAA,CAAA;AAAA,QACjC;AAAA,UACE,OAAS,EAAA;AAAA,YACP,aAAe,EAAA,CAAA,OAAA,EAAU,IAAK,CAAA,oBAAA,CAAqB,YAAY,CAAA,CAAA;AAAA,WACjE;AAAA,SACF;AAAA,OACF,CAAA;AACA,MAAU,MAAA,GAAA,MAAM,UAAU,IAAK,EAAA,CAAA;AAAA,KACjC;AACA,IAAA,OAAO,KAAK,aAAc,CAAA,qBAAA;AAAA,MACxB,GAAI,CAAA,KAAA;AAAA,MACJ,GAAI,CAAA,UAAA;AAAA,MACJ,GAAI,CAAA,WAAA;AAAA,MACJ,MAAA;AAAA,KACF,CAAA;AAAA,GACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,SAAY,GAAA;AAChB,IAAI,IAAA,CAAC,KAAK,UAAc,IAAA,CAAC,KAAK,oBAAwB,IAAA,CAAC,KAAK,YAAc,EAAA;AACxE,MAAM,MAAA,IAAI,MAAM,sBAAsB,CAAA,CAAA;AAAA,KACxC;AACA,IAAA,IAAI,gBAA8C,EAAC,CAAA;AACnD,IAAI,IAAA,GAAA,CAAA;AACJ,IAAA,IAAI,OAAkB,GAAA,EAAA,CAAA;AACtB,IAAA,MAAM,OAA4B,GAAA;AAAA,MAChC,aAAe,EAAA,CAAA,OAAA,EAAU,IAAK,CAAA,oBAAA,CAAqB,YAAY,CAAA,CAAA;AAAA,KACjE,CAAA;AAEA,IAAA,IAAI,KAAK,YAAc,EAAA;AAGrB,MAAA,MAAM,mBAAmB,IAAK,CAAA,oBAAA,GAC1B,EACA,GAAA,CAAA,gBAAA,EAAmB,KAAK,gBAAgB,CAAA,CAAA,CAAA,CAAA;AAC5C,MAAK,IAAA,CAAA,GAAA;AAAA,QACH,CAAA,mBAAA,EAAsB,KAAK,YAAY,CAAA,EAAG,gBAAgB,CAAwC,qCAAA,EAAA,IAAA,CAAK,qBAAqB,YAAY,CAAA,CAAA,CAAA;AAAA,OAC1I,CAAA;AAAA,KACF;AACA,IAAA,IAAA,CAAK,oBAAuB,GAAA,KAAA,CAAA;AAK5B,IAAI,IAAA,IAAA,CAAK,qBAAqB,mBAAqB,EAAA;AACjD,MAAM,MAAA,cAAA,GAAiB,KAAK,YAAe,GAAA,qBAAA,CAAA;AAC3C,MAAI,IAAA;AAEF,QAAA,GAAA,GAAM,MAAMA,sBAAA,CAAM,cAAgB,EAAA,EAAE,SAAS,CAAA,CAAA;AAC7C,QAAU,OAAA,GAAA,MAAM,IAAI,IAAK,EAAA,CAAA;AACzB,QAAA,IAAI,KAAK,YAAc,EAAA;AACrB,UAAK,IAAA,CAAA,GAAA;AAAA,YACH,kDAAqD,GAAA,OAAA;AAAA,WACvD,CAAA;AAAA,SACF;AACA,QAAgB,aAAA,GAAA,IAAA,CAAK,MAAM,OAAO,CAAA,CAAA;AAClC,QAAA,IAAI,KAAK,YAAc,EAAA;AACrB,UAAA,IAAA,CAAK,IAAI,4BAA4B,CAAA,CAAA;AAAA,SACvC;AAGA,QAAA,IAAI,cAAc,MAAS,GAAA,CAAA,IAAK,eAAmB,IAAA,aAAA,CAAc,CAAC,CAAG,EAAA;AACnE,UAAA,IAAA,CAAK,uBAAuB,cAAc,CAAA,CAAA;AAAA,SAC5C;AAAA,eACO,CAAG,EAAA;AAEV,QAAA,IAAA,CAAK,uBAAuB,cAAc,CAAA,CAAA;AAAA,OAC5C;AAAA,KACF;AAKA,IAAI,IAAA,IAAA,CAAK,qBAAqB,cAAgB,EAAA;AAE5C,MAAA,GAAA,GAAM,MAAMA,sBAAM,CAAA,IAAA,CAAK,YAAc,EAAA,EAAE,SAAS,CAAA,CAAA;AAChD,MAAU,OAAA,GAAA,MAAM,IAAI,IAAK,EAAA,CAAA;AACzB,MAAA,IAAI,KAAK,YAAc,EAAA;AACrB,QAAK,IAAA,CAAA,GAAA,CAAI,qDAAqD,OAAO,CAAA,CAAA;AAAA,OACvE;AACA,MAAM,MAAA,cAAA,GAAiB,IAAK,CAAA,KAAA,CAAM,OAAO,CAAA,CAAA;AAGzC,MAAA,IACE,eAAe,MAAS,GAAA,CAAA,IACxB,uBAA4B,IAAA,cAAA,CAAyB,CAAC,CACtD,EAAA;AACA,QAAA,IAAA,CAAK,uBAAuB,mBAAmB,CAAA,CAAA;AAC/C,QAAO,OAAA,cAAA,CAAA;AAAA,OACT;AAGA,MAAA,MAAM,SAAY,GAAA,cAAA,CAAA;AAClB,MAAA,IAAI,KAAK,YAAc,EAAA;AACrB,QAAA,IAAA,CAAK,IAAI,8CAA8C,CAAA,CAAA;AAAA,OACzD;AAEA,MAAA,aAAA,GAAgB,EAAC,CAAA;AAEjB,MAAA,KAAA,IAAS,CAAI,GAAA,CAAA,EAAG,CAAI,GAAA,SAAA,CAAU,QAAQ,CAAK,EAAA,EAAA;AACzC,QAAM,MAAA,iBAAA,GAAoB,UAAU,CAAC,CAAA,CAAA;AACrC,QAAA,MAAM,iBAAiB,CAAG,EAAA,IAAA,CAAK,eAAe,CAAA,MAAA,EAAS,kBAAkB,EAAE,CAAA,SAAA,CAAA,CAAA;AAC3E,QAAA,IAAI,KAAK,YAAc,EAAA;AACrB,UAAK,IAAA,CAAA,GAAA;AAAA,YACH,CAA8B,2BAAA,EAAA,cAAc,CAAmB,gBAAA,EAAA,IAAA,CAAK,gBAAgB,CAAA,EAAA,CAAA;AAAA,WACtF,CAAA;AAAA,SACF;AACA,QAAA,MAAM,cAAc,MAAMA,sBAAA,CAAM,cAAgB,EAAA,EAAE,SAAS,CAAA,CAAA;AAC3D,QAAMC,MAAAA,QAAAA,GAAU,MAAM,WAAA,CAAY,IAAK,EAAA,CAAA;AACvC,QAAA,IAAI,KAAK,YAAc,EAAA;AACrB,UAAK,IAAA,CAAA,GAAA;AAAA,YACH,WAAW,cAAc,CAAA,gBAAA,EAAmB,IAAK,CAAA,gBAAgB,qCAAqCA,QAAO,CAAA,CAAA;AAAA,WAC/G,CAAA;AAAA,SACF;AACA,QAAM,MAAA,QAAA,GAAW,IAAK,CAAA,KAAA,CAAMA,QAAO,CAAA,CAAA;AACnC,QAAA,IAAI,KAAK,YAAc,EAAA;AACrB,UAAA,IAAA,CAAK,IAAI,uCAAuC,CAAA,CAAA;AAAA,SAClD;AACA,QAAI,IAAA,CAAC,EAAC,QAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,QAAA,CAAU,MAAQ,CAAA,EAAA;AAEtB,UAAc,aAAA,CAAA,IAAA;AAAA,YACZ,GAAG,QAAS,CAAA,GAAA,CAAI,CAAM,CAAA,MAAA;AAAA,cACpB,GAAG,CAAA;AAAA,cACH,uBAAuB,iBAAkB,CAAA,WAAA;AAAA,aACzC,CAAA,CAAA;AAAA,WACJ,CAAA;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAEA,IAAO,OAAA,aAAA,CAAA;AAAA,GACT;AACF;;;;"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,73 @@
1
1
  import { LoggerService, SchedulerService } from '@backstage/backend-plugin-api';
2
+ import { Entity } from '@backstage/catalog-model';
2
3
  import { Config } from '@backstage/config';
3
4
  import { EntityProvider, EntityProviderConnection } from '@backstage/plugin-catalog-node';
4
5
 
6
+ type API = {
7
+ apiProductId?: string;
8
+ apiProductDisplayName?: string;
9
+ apiVersion?: string;
10
+ apiId: string;
11
+ contact: string;
12
+ customMetadata: Record<string, string> | undefined;
13
+ description: string;
14
+ license: string;
15
+ termsOfService: string;
16
+ title: string;
17
+ usagePlans: string[];
18
+ openapiSpec?: APISchema;
19
+ openapiSpecFetchErr?: string | null;
20
+ };
21
+ type ApiVersion = {
22
+ apiSpec?: string | APISchema;
23
+ createdAt: string;
24
+ documentation: string;
25
+ id: string;
26
+ name: string;
27
+ publicVisible?: boolean;
28
+ status: string;
29
+ title: string;
30
+ updatedAt: string;
31
+ };
32
+ type ApiVersionExtended = ApiVersion & {
33
+ apiProductDescription: string;
34
+ };
35
+ type SchemaPropertyType = 'string' | 'integer' | 'array' | 'object';
36
+ type APISchema = {
37
+ components?: {
38
+ schemas: {
39
+ Author?: {
40
+ properties: {
41
+ [key: string]: SchemaPropertyType;
42
+ };
43
+ type: 'object';
44
+ };
45
+ Module?: {
46
+ properties: {
47
+ [key: string]: SchemaPropertyType;
48
+ };
49
+ type: 'object';
50
+ };
51
+ Track?: {
52
+ properties: {
53
+ [key: string]: SchemaPropertyType;
54
+ };
55
+ type: 'object';
56
+ };
57
+ };
58
+ };
59
+ info: {
60
+ title: string;
61
+ version: string;
62
+ };
63
+ paths: {
64
+ [key: string]: unknown;
65
+ };
66
+ servers?: {
67
+ url: string;
68
+ }[];
69
+ };
70
+
5
71
  /**
6
72
  * Provides API entities from the Gloo Platform Portal REST server.
7
73
  */
@@ -11,11 +77,27 @@ declare class GlooPlatformPortalProvider implements EntityProvider {
11
77
  private config;
12
78
  private latestTokensResponse?;
13
79
  private debugLogging;
80
+ private isInitialApisRequest;
81
+ private configUtil;
82
+ private entityBuilder;
83
+ /**
84
+ * Defaults to gloo-mesh-gateway backend.
85
+ * This is updated to gloo-gateway if that's what the backend response type uses.
86
+ */
87
+ private _portalServerType;
88
+ private get portalServerType();
89
+ private _portalServerUrl;
90
+ private get portalServerUrl();
91
+ private _apisEndpoint;
92
+ private get apisEndpoint();
14
93
  log: (s: string) => void;
15
94
  warn: (s: string) => void;
16
95
  error: (s: string) => void;
17
96
  getProviderName: () => string;
18
97
  connect(connection: EntityProviderConnection): Promise<void>;
98
+ updatePortalServerUrl(): void;
99
+ updateApisEndpoint(): void;
100
+ updatePortalServerType(newType: typeof this.portalServerType): void;
19
101
  constructor(logger: LoggerService, config: Config, scheduler: SchedulerService);
20
102
  startTokensRequests(): Promise<void>;
21
103
  /**
@@ -34,6 +116,19 @@ declare class GlooPlatformPortalProvider implements EntityProvider {
34
116
  * and transforms the response into Backstage API entities.
35
117
  */
36
118
  run(): Promise<void>;
119
+ /**
120
+ * Returns the Backstage catalog entity for the "gloo-gateway" Portal Server API response.
121
+ */
122
+ getGlooGatewayApiEntity(api: ApiVersionExtended): Promise<Entity>;
123
+ /**
124
+ * Returns the Backstage catalog entity for the "gloo-mesh-gateway" Portal Server API response.
125
+ */
126
+ getGlooMeshGatewayApiEntity(api: API): Promise<Entity>;
127
+ /**
128
+ * A helper function for getting the API's from the Portal Server.
129
+ * This abstracts away the "gloo-gateway"/"gloo-mesh-gateway" portal server details.
130
+ */
131
+ fetchAPIs(): Promise<(API | ApiVersionExtended)[]>;
37
132
  }
38
133
 
39
134
  export { GlooPlatformPortalProvider };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@solo.io/platform-portal-backstage-plugin-backend",
3
3
  "description": "A Backstage backend plugin that synchronizes Gloo Platform Portal APIs with the Backstage catalog.",
4
- "version": "0.0.30",
4
+ "version": "0.0.31",
5
5
  "main": "dist/index.cjs.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "Apache-2.0",