@backstage-community/plugin-3scale-backend 3.0.0 → 3.0.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## @janus-idp/backstage-plugin-3scale-backend [1.8.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-3scale-backend@1.7.1...@janus-idp/backstage-plugin-3scale-backend@1.8.0) (2024-07-25)
2
2
 
3
+ ## 3.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - c143a75: Backstage version bump to v1.32.2
8
+
3
9
  ## 3.0.0
4
10
 
5
11
  ### Major Changes
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ function listServices(baseUrl, access_token, page, size) {
4
+ return fetch(
5
+ `${baseUrl}/admin/api/services.json?access_token=${access_token}&page=${page}&size=${size}`
6
+ ).then((response) => {
7
+ if (!response.ok) {
8
+ throw new Error(response.statusText);
9
+ }
10
+ return response.json();
11
+ });
12
+ }
13
+ function listApiDocs(baseUrl, access_token) {
14
+ return fetch(
15
+ `${baseUrl}/admin/api/active_docs.json?access_token=${access_token}`
16
+ ).then((response) => {
17
+ if (!response.ok) {
18
+ throw new Error(response.statusText);
19
+ }
20
+ return response.json();
21
+ });
22
+ }
23
+ function getProxyConfig(baseUrl, access_token, service_id) {
24
+ return fetch(
25
+ `${baseUrl}/admin/api/services/${service_id}/proxy.json?access_token=${access_token}`
26
+ ).then((response) => {
27
+ if (!response.ok) {
28
+ throw new Error(response.statusText);
29
+ }
30
+ return response.json();
31
+ });
32
+ }
33
+
34
+ exports.getProxyConfig = getProxyConfig;
35
+ exports.listApiDocs = listApiDocs;
36
+ exports.listServices = listServices;
37
+ //# sourceMappingURL=ThreeScaleAPIConnector.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThreeScaleAPIConnector.cjs.js","sources":["../../src/clients/ThreeScaleAPIConnector.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { APIDocs, Proxy, Services } from './types';\n\nexport function listServices(\n baseUrl: string,\n access_token: string,\n page: number,\n size: number,\n): Promise<Services> {\n return fetch(\n `${baseUrl}/admin/api/services.json?access_token=${access_token}&page=${page}&size=${size}`,\n ).then(response => {\n if (!response.ok) {\n throw new Error(response.statusText);\n }\n return response.json() as Promise<Services>;\n });\n}\n\nexport function listApiDocs(\n baseUrl: string,\n access_token: string,\n): Promise<APIDocs> {\n return fetch(\n `${baseUrl}/admin/api/active_docs.json?access_token=${access_token}`,\n ).then(response => {\n if (!response.ok) {\n throw new Error(response.statusText);\n }\n return response.json() as Promise<APIDocs>;\n });\n}\n\nexport function getProxyConfig(\n baseUrl: string,\n access_token: string,\n service_id: number,\n): Promise<Proxy> {\n return fetch(\n `${baseUrl}/admin/api/services/${service_id}/proxy.json?access_token=${access_token}`,\n ).then(response => {\n if (!response.ok) {\n throw new Error(response.statusText);\n }\n return response.json() as Promise<Proxy>;\n });\n}\n"],"names":[],"mappings":";;AAiBO,SAAS,YACd,CAAA,OAAA,EACA,YACA,EAAA,IAAA,EACA,IACmB,EAAA;AACnB,EAAO,OAAA,KAAA;AAAA,IACL,GAAG,OAAO,CAAA,sCAAA,EAAyC,YAAY,CAAS,MAAA,EAAA,IAAI,SAAS,IAAI,CAAA,CAAA;AAAA,GAC3F,CAAE,KAAK,CAAY,QAAA,KAAA;AACjB,IAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AAChB,MAAM,MAAA,IAAI,KAAM,CAAA,QAAA,CAAS,UAAU,CAAA,CAAA;AAAA,KACrC;AACA,IAAA,OAAO,SAAS,IAAK,EAAA,CAAA;AAAA,GACtB,CAAA,CAAA;AACH,CAAA;AAEgB,SAAA,WAAA,CACd,SACA,YACkB,EAAA;AAClB,EAAO,OAAA,KAAA;AAAA,IACL,CAAA,EAAG,OAAO,CAAA,yCAAA,EAA4C,YAAY,CAAA,CAAA;AAAA,GACpE,CAAE,KAAK,CAAY,QAAA,KAAA;AACjB,IAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AAChB,MAAM,MAAA,IAAI,KAAM,CAAA,QAAA,CAAS,UAAU,CAAA,CAAA;AAAA,KACrC;AACA,IAAA,OAAO,SAAS,IAAK,EAAA,CAAA;AAAA,GACtB,CAAA,CAAA;AACH,CAAA;AAEgB,SAAA,cAAA,CACd,OACA,EAAA,YAAA,EACA,UACgB,EAAA;AAChB,EAAO,OAAA,KAAA;AAAA,IACL,CAAG,EAAA,OAAO,CAAuB,oBAAA,EAAA,UAAU,4BAA4B,YAAY,CAAA,CAAA;AAAA,GACrF,CAAE,KAAK,CAAY,QAAA,KAAA;AACjB,IAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AAChB,MAAM,MAAA,IAAI,KAAM,CAAA,QAAA,CAAS,UAAU,CAAA,CAAA;AAAA,KACrC;AACA,IAAA,OAAO,SAAS,IAAK,EAAA,CAAA;AAAA,GACtB,CAAA,CAAA;AACH;;;;;;"}
package/dist/index.cjs.js CHANGED
@@ -2,397 +2,15 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var backendPluginApi = require('@backstage/backend-plugin-api');
6
- var alpha = require('@backstage/plugin-catalog-node/alpha');
7
- var catalogModel = require('@backstage/catalog-model');
8
- var errors = require('@backstage/errors');
9
- var openapiMerge = require('openapi-merge');
10
- var Swagger2OpenAPI = require('swagger2openapi');
11
- var SwaggerConverter = require('swagger-converter');
5
+ var ThreeScaleAPIConnector = require('./clients/ThreeScaleAPIConnector.cjs.js');
6
+ var module$1 = require('./module.cjs.js');
7
+ var ThreeScaleApiEntityProvider = require('./providers/ThreeScaleApiEntityProvider.cjs.js');
12
8
 
13
- function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
14
9
 
15
- var Swagger2OpenAPI__default = /*#__PURE__*/_interopDefaultCompat(Swagger2OpenAPI);
16
- var SwaggerConverter__default = /*#__PURE__*/_interopDefaultCompat(SwaggerConverter);
17
10
 
18
- function listServices(baseUrl, access_token, page, size) {
19
- return fetch(
20
- `${baseUrl}/admin/api/services.json?access_token=${access_token}&page=${page}&size=${size}`
21
- ).then((response) => {
22
- if (!response.ok) {
23
- throw new Error(response.statusText);
24
- }
25
- return response.json();
26
- });
27
- }
28
- function listApiDocs(baseUrl, access_token) {
29
- return fetch(
30
- `${baseUrl}/admin/api/active_docs.json?access_token=${access_token}`
31
- ).then((response) => {
32
- if (!response.ok) {
33
- throw new Error(response.statusText);
34
- }
35
- return response.json();
36
- });
37
- }
38
- function getProxyConfig(baseUrl, access_token, service_id) {
39
- return fetch(
40
- `${baseUrl}/admin/api/services/${service_id}/proxy.json?access_token=${access_token}`
41
- ).then((response) => {
42
- if (!response.ok) {
43
- throw new Error(response.statusText);
44
- }
45
- return response.json();
46
- });
47
- }
48
-
49
- function readThreeScaleApiEntityConfigs(config) {
50
- const providerConfigs = config.getOptionalConfig(
51
- "catalog.providers.threeScaleApiEntity"
52
- );
53
- if (!providerConfigs) {
54
- return [];
55
- }
56
- return providerConfigs.keys().map(
57
- (id) => readThreeScaleApiEntityConfig(id, providerConfigs.getConfig(id))
58
- );
59
- }
60
- function readThreeScaleApiEntityConfig(id, config) {
61
- const baseUrl = config.getString("baseUrl");
62
- const accessToken = config.getString("accessToken");
63
- const systemLabel = config.getOptionalString("systemLabel");
64
- const ownerLabel = config.getOptionalString("ownerLabel");
65
- const addLabels = config.getOptionalBoolean("addLabels") || true;
66
- const schedule = config.has("schedule") ? backendPluginApi.readSchedulerServiceTaskScheduleDefinitionFromConfig(
67
- config.getConfig("schedule")
68
- ) : void 0;
69
- return {
70
- id,
71
- baseUrl,
72
- accessToken,
73
- systemLabel,
74
- ownerLabel,
75
- addLabels,
76
- schedule
77
- };
78
- }
79
-
80
- function isNonEmptyArray(arr) {
81
- return arr.length > 0;
82
- }
83
-
84
- function isSwagger1_2(apiDoc) {
85
- return apiDoc.swaggerVersion && apiDoc.swaggerVersion === "1.2";
86
- }
87
- function isSwagger2_0(apiDoc) {
88
- return apiDoc.swagger && apiDoc.swagger === "2.0";
89
- }
90
- function isOpenAPI3_0(apiDoc) {
91
- return apiDoc.openapi;
92
- }
93
- class OpenAPIMergerAndConverter {
94
- async mergeOpenAPI3Docs(docs) {
95
- const mergeInput = docs.map((doc) => {
96
- return { oas: doc };
97
- });
98
- const result = await openapiMerge.merge(mergeInput);
99
- if (openapiMerge.isErrorResult(result)) {
100
- throw new Error(result.message);
101
- }
102
- return result.output;
103
- }
104
- // Convert api doc to format openAPI 3. Do nothing with doc if it has format openAPI 3.0.
105
- // 3scale supports API docs in formats:
106
- // - swagger 1.2
107
- // - swagger 2.0
108
- // - openAPI 3.0
109
- async convertAPIDocToOpenAPI3(apiDoc) {
110
- if (isOpenAPI3_0(apiDoc)) {
111
- return apiDoc;
112
- }
113
- if (isSwagger1_2(apiDoc)) {
114
- const swagger2_0Doc = await this.convertSwagger1_2To2_0(apiDoc);
115
- return await this.convertSwagger2_0ToOpenAPI3_0(swagger2_0Doc);
116
- }
117
- if (isSwagger2_0(apiDoc)) {
118
- return await this.convertSwagger2_0ToOpenAPI3_0(apiDoc);
119
- }
120
- throw new Error(
121
- `Unsupported API document. Plugin supports Swagger 1.2, 2.0, 3.0(Open API 3.0)`
122
- );
123
- }
124
- async convertSwagger1_2To2_0(swaggerDoc) {
125
- try {
126
- const result = SwaggerConverter__default.default.convert(swaggerDoc, {});
127
- return result;
128
- } catch (error) {
129
- console.error("Error converting Swagger 1.2 to Swagger 2.0:", error);
130
- throw error;
131
- }
132
- }
133
- async convertSwagger2_0ToOpenAPI3_0(swaggerDoc) {
134
- try {
135
- const result = await Swagger2OpenAPI__default.default.convertObj(swaggerDoc, {
136
- patch: true,
137
- // patch: true helps to fix minor issues
138
- warnOnly: true
139
- // Do not throw on non-patchable errors
140
- });
141
- return result.openapi;
142
- } catch (error) {
143
- console.error("Error converting Swagger 2.0 to OpenAPI 3.0:", error);
144
- throw error;
145
- }
146
- }
147
- }
148
-
149
- class ThreeScaleApiEntityProvider {
150
- static SERVICES_FETCH_SIZE = 500;
151
- env;
152
- baseUrl;
153
- accessToken;
154
- logger;
155
- scheduleFn;
156
- openApiMerger;
157
- connection;
158
- static fromConfig(deps, options) {
159
- const providerConfigs = readThreeScaleApiEntityConfigs(deps.config);
160
- if (!options.schedule && !options.scheduler) {
161
- throw new Error("Either schedule or scheduler must be provided.");
162
- }
163
- return providerConfigs.map((providerConfig) => {
164
- if (!options.schedule && !providerConfig.schedule) {
165
- throw new errors.InputError(
166
- `No schedule provided via config for ThreeScaleApiEntityProvider:${providerConfig.id}.`
167
- );
168
- }
169
- let taskRunner;
170
- if (options.scheduler && providerConfig.schedule) {
171
- taskRunner = options.scheduler.createScheduledTaskRunner(
172
- providerConfig.schedule
173
- );
174
- } else if (options.schedule) {
175
- taskRunner = options.schedule;
176
- } else {
177
- throw new Error("Neither schedule nor scheduler is provided.");
178
- }
179
- return new ThreeScaleApiEntityProvider(
180
- providerConfig,
181
- deps.logger,
182
- taskRunner
183
- );
184
- });
185
- }
186
- constructor(config, logger, taskRunner) {
187
- this.env = config.id;
188
- this.baseUrl = config.baseUrl;
189
- this.accessToken = config.accessToken;
190
- this.logger = logger.child({
191
- target: this.getProviderName()
192
- });
193
- this.scheduleFn = this.createScheduleFn(taskRunner);
194
- this.openApiMerger = new OpenAPIMergerAndConverter();
195
- }
196
- createScheduleFn(taskRunner) {
197
- return async () => {
198
- const taskId = `${this.getProviderName()}:run`;
199
- return taskRunner.run({
200
- id: taskId,
201
- fn: async () => {
202
- try {
203
- await this.run();
204
- } catch (error) {
205
- if (errors.isError(error)) {
206
- this.logger.error(
207
- `Error while syncing 3scale API from ${this.baseUrl}`,
208
- {
209
- // Default Error properties:
210
- name: error.name,
211
- message: error.message,
212
- stack: error.stack,
213
- // Additional status code if available:
214
- status: error.response?.status
215
- }
216
- );
217
- }
218
- }
219
- }
220
- });
221
- };
222
- }
223
- getProviderName() {
224
- return `ThreeScaleApiEntityProvider:${this.env}`;
225
- }
226
- async connect(connection) {
227
- this.connection = connection;
228
- await this.scheduleFn();
229
- }
230
- async run() {
231
- if (!this.connection) {
232
- throw new errors.NotFoundError("Not initialized");
233
- }
234
- this.logger.info(`Discovering ApiEntities from 3scale ${this.baseUrl}`);
235
- const entities = [];
236
- let page = 0;
237
- let services;
238
- let apiDocs;
239
- let fetchServices = true;
240
- while (fetchServices) {
241
- services = await listServices(
242
- this.baseUrl,
243
- this.accessToken,
244
- page,
245
- ThreeScaleApiEntityProvider.SERVICES_FETCH_SIZE
246
- );
247
- apiDocs = await listApiDocs(this.baseUrl, this.accessToken);
248
- for (const element of services.services) {
249
- const service = element;
250
- this.logger.debug(`Find service ${service.service.name}`);
251
- const docs = apiDocs.api_docs.filter(
252
- (obj) => obj.api_doc.service_id === service.service.id
253
- );
254
- const proxy = await getProxyConfig(
255
- this.baseUrl,
256
- this.accessToken,
257
- service.service.id
258
- );
259
- if (isNonEmptyArray(docs)) {
260
- this.logger.info(JSON.stringify(docs));
261
- const apiEntity = await this.buildApiEntityFromService(
262
- service,
263
- docs,
264
- proxy
265
- );
266
- entities.push(apiEntity);
267
- this.logger.debug(`Discovered ApiEntity ${service.service.name}`);
268
- }
269
- }
270
- if (services.services.length < ThreeScaleApiEntityProvider.SERVICES_FETCH_SIZE) {
271
- fetchServices = false;
272
- }
273
- page++;
274
- }
275
- this.logger.info(`Applying the mutation with ${entities.length} entities`);
276
- await this.connection.applyMutation({
277
- type: "full",
278
- entities: entities.map((entity) => ({
279
- entity,
280
- locationKey: this.getProviderName()
281
- }))
282
- });
283
- }
284
- async buildApiEntityFromService(service, apiDocs, proxy) {
285
- const location = `url:${this.baseUrl}/apiconfig/services/${service.service.id}`;
286
- const serviceDescription = service.service.description || "";
287
- let entityDescription;
288
- const docs = apiDocs.map((doc) => JSON.parse(doc.api_doc.body));
289
- let swaggerDocJSON;
290
- if (docs.length > 1) {
291
- let mergedDescription = `[Merged ${docs.length} API docs]`;
292
- let mergedTitle = mergedDescription;
293
- const convertedDocs = [];
294
- for (const doc of docs) {
295
- const convertedDoc = await this.openApiMerger.convertAPIDocToOpenAPI3(
296
- doc
297
- );
298
- convertedDocs.push(convertedDoc);
299
- mergedDescription = getDocInfo(convertedDoc)?.description ? `${mergedDescription} ${getDocInfo(convertedDoc)?.description}` : mergedDescription;
300
- mergedTitle = getDocInfo(convertedDoc)?.title ? `${mergedTitle} ${getDocInfo(convertedDoc)?.title}` : mergedTitle;
301
- }
302
- if (isNonEmptyArray(convertedDocs)) {
303
- swaggerDocJSON = await this.openApiMerger.mergeOpenAPI3Docs(
304
- convertedDocs
305
- );
306
- swaggerDocJSON.info.description = mergedDescription;
307
- swaggerDocJSON.info.title = mergedTitle;
308
- entityDescription = mergedDescription;
309
- }
310
- }
311
- if (docs.length === 1) {
312
- swaggerDocJSON = docs[0];
313
- const spec = JSON.parse(apiDocs[0].api_doc.body);
314
- if (isSwagger1_2(spec)) {
315
- swaggerDocJSON = await this.openApiMerger.convertSwagger1_2To2_0(spec);
316
- }
317
- entityDescription = getDocInfo(spec)?.description;
318
- }
319
- return {
320
- kind: "API",
321
- apiVersion: "backstage.io/v1alpha1",
322
- metadata: {
323
- annotations: {
324
- [catalogModel.ANNOTATION_LOCATION]: location,
325
- [catalogModel.ANNOTATION_ORIGIN_LOCATION]: location
326
- },
327
- // TODO: add tenant name
328
- name: `${service.service.system_name}`,
329
- description: entityDescription || serviceDescription,
330
- // TODO: add labels
331
- // labels: this.getApiEntityLabels(service),
332
- links: [
333
- {
334
- url: `${this.baseUrl}/apiconfig/services/${service.service.id}`,
335
- title: "3scale Overview"
336
- },
337
- {
338
- url: `${proxy.proxy.sandbox_endpoint}`,
339
- title: "Staging Apicast Endpoint"
340
- },
341
- {
342
- url: `${proxy.proxy.endpoint}`,
343
- title: "Production Apicast Endpoint"
344
- }
345
- ]
346
- },
347
- spec: {
348
- type: "openapi",
349
- lifecycle: this.env,
350
- system: "3scale",
351
- owner: "3scale",
352
- definition: JSON.stringify(swaggerDocJSON, null, 2)
353
- }
354
- };
355
- }
356
- }
357
- function getDocInfo(spec) {
358
- if (isSwagger2_0(spec) || isOpenAPI3_0(spec)) {
359
- return spec.info;
360
- }
361
- return void 0;
362
- }
363
-
364
- const catalogModule3ScaleEntityProvider = backendPluginApi.createBackendModule({
365
- moduleId: "catalog-backend-module-3scale",
366
- pluginId: "catalog",
367
- register(env) {
368
- env.registerInit({
369
- deps: {
370
- catalog: alpha.catalogProcessingExtensionPoint,
371
- config: backendPluginApi.coreServices.rootConfig,
372
- logger: backendPluginApi.coreServices.logger,
373
- scheduler: backendPluginApi.coreServices.scheduler
374
- },
375
- async init({ catalog, config, logger, scheduler }) {
376
- catalog.addEntityProvider(
377
- ThreeScaleApiEntityProvider.fromConfig(
378
- { config, logger },
379
- {
380
- scheduler,
381
- schedule: scheduler.createScheduledTaskRunner({
382
- frequency: { minutes: 30 },
383
- timeout: { minutes: 3 }
384
- })
385
- }
386
- )
387
- );
388
- }
389
- });
390
- }
391
- });
392
-
393
- exports.ThreeScaleApiEntityProvider = ThreeScaleApiEntityProvider;
394
- exports.default = catalogModule3ScaleEntityProvider;
395
- exports.getProxyConfig = getProxyConfig;
396
- exports.listApiDocs = listApiDocs;
397
- exports.listServices = listServices;
11
+ exports.getProxyConfig = ThreeScaleAPIConnector.getProxyConfig;
12
+ exports.listApiDocs = ThreeScaleAPIConnector.listApiDocs;
13
+ exports.listServices = ThreeScaleAPIConnector.listServices;
14
+ exports.default = module$1.catalogModule3ScaleEntityProvider;
15
+ exports.ThreeScaleApiEntityProvider = ThreeScaleApiEntityProvider.ThreeScaleApiEntityProvider;
398
16
  //# sourceMappingURL=index.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/clients/ThreeScaleAPIConnector.ts","../src/providers/config.ts","../src/providers/types.ts","../src/providers/open-api-merger-converter.ts","../src/providers/ThreeScaleApiEntityProvider.ts","../src/module.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { APIDocs, Proxy, Services } from './types';\n\nexport function listServices(\n baseUrl: string,\n access_token: string,\n page: number,\n size: number,\n): Promise<Services> {\n return fetch(\n `${baseUrl}/admin/api/services.json?access_token=${access_token}&page=${page}&size=${size}`,\n ).then(response => {\n if (!response.ok) {\n throw new Error(response.statusText);\n }\n return response.json() as Promise<Services>;\n });\n}\n\nexport function listApiDocs(\n baseUrl: string,\n access_token: string,\n): Promise<APIDocs> {\n return fetch(\n `${baseUrl}/admin/api/active_docs.json?access_token=${access_token}`,\n ).then(response => {\n if (!response.ok) {\n throw new Error(response.statusText);\n }\n return response.json() as Promise<APIDocs>;\n });\n}\n\nexport function getProxyConfig(\n baseUrl: string,\n access_token: string,\n service_id: number,\n): Promise<Proxy> {\n return fetch(\n `${baseUrl}/admin/api/services/${service_id}/proxy.json?access_token=${access_token}`,\n ).then(response => {\n if (!response.ok) {\n throw new Error(response.statusText);\n }\n return response.json() as Promise<Proxy>;\n });\n}\n","/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { readSchedulerServiceTaskScheduleDefinitionFromConfig } from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\n\nimport { ThreeScaleConfig } from './types';\n\nexport function readThreeScaleApiEntityConfigs(\n config: Config,\n): ThreeScaleConfig[] {\n const providerConfigs = config.getOptionalConfig(\n 'catalog.providers.threeScaleApiEntity',\n );\n if (!providerConfigs) {\n return [];\n }\n return providerConfigs\n .keys()\n .map(id =>\n readThreeScaleApiEntityConfig(id, providerConfigs.getConfig(id)),\n );\n}\n\nfunction readThreeScaleApiEntityConfig(\n id: string,\n config: Config,\n): ThreeScaleConfig {\n const baseUrl = config.getString('baseUrl');\n const accessToken = config.getString('accessToken');\n const systemLabel = config.getOptionalString('systemLabel');\n const ownerLabel = config.getOptionalString('ownerLabel');\n const addLabels = config.getOptionalBoolean('addLabels') || true;\n\n const schedule = config.has('schedule')\n ? readSchedulerServiceTaskScheduleDefinitionFromConfig(\n config.getConfig('schedule'),\n )\n : undefined;\n\n return {\n id,\n baseUrl,\n accessToken,\n systemLabel,\n ownerLabel,\n addLabels,\n schedule,\n };\n}\n","/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { SchedulerServiceTaskScheduleDefinition } from '@backstage/backend-plugin-api';\n\nexport type ThreeScaleConfig = {\n id: string;\n baseUrl: string;\n accessToken: string;\n systemLabel?: string;\n ownerLabel?: string;\n addLabels?: boolean;\n schedule?: SchedulerServiceTaskScheduleDefinition;\n};\n\nexport type NonEmptyArray<T> = [T, ...T[]];\n\nexport function isNonEmptyArray<T>(arr: T[]): arr is NonEmptyArray<T> {\n return arr.length > 0;\n}\n","/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { merge, isErrorResult, MergeInput } from 'openapi-merge';\nimport { Swagger } from 'atlassian-openapi';\nimport Swagger2OpenAPI from 'swagger2openapi';\n// @ts-ignore\nimport SwaggerConverter from 'swagger-converter';\nimport { NonEmptyArray } from './types';\n\nexport function isSwagger1_2(apiDoc: any): boolean {\n return apiDoc.swaggerVersion && apiDoc.swaggerVersion === '1.2';\n}\n\nexport function isSwagger2_0(apiDoc: any): boolean {\n return apiDoc.swagger && apiDoc.swagger === '2.0';\n}\n\nexport function isOpenAPI3_0(apiDoc: any): boolean {\n return apiDoc.openapi;\n}\n\nexport class OpenAPIMergerAndConverter {\n async mergeOpenAPI3Docs(\n docs: NonEmptyArray<Swagger.SwaggerV3>,\n ): Promise<Swagger.SwaggerV3> {\n const mergeInput: MergeInput = docs.map(doc => {\n return { oas: doc };\n });\n\n const result = await merge(mergeInput);\n if (isErrorResult(result)) {\n throw new Error(result.message);\n }\n return result.output;\n }\n\n // Convert api doc to format openAPI 3. Do nothing with doc if it has format openAPI 3.0.\n // 3scale supports API docs in formats:\n // - swagger 1.2\n // - swagger 2.0\n // - openAPI 3.0\n async convertAPIDocToOpenAPI3(apiDoc: any): Promise<Swagger.SwaggerV3> {\n if (isOpenAPI3_0(apiDoc)) {\n return apiDoc;\n }\n if (isSwagger1_2(apiDoc)) {\n // Unfortunately there is no library in the JavaScript world, which can convert both swagger 1.2 and 2.0 to openAPI 3.0.\n // That's why, for swagger 1.2 we are using convertation to swagger 2.0. And then swagger 2.0 will be converted to openAPI 3.0.\n const swagger2_0Doc = await this.convertSwagger1_2To2_0(apiDoc);\n return await this.convertSwagger2_0ToOpenAPI3_0(swagger2_0Doc);\n }\n if (isSwagger2_0(apiDoc)) {\n return await this.convertSwagger2_0ToOpenAPI3_0(apiDoc);\n }\n\n throw new Error(\n `Unsupported API document. Plugin supports Swagger 1.2, 2.0, 3.0(Open API 3.0)`,\n );\n }\n\n async convertSwagger1_2To2_0(swaggerDoc: any): Promise<any> {\n try {\n const result = SwaggerConverter.convert(swaggerDoc, {});\n return result;\n } catch (error) {\n console.error('Error converting Swagger 1.2 to Swagger 2.0:', error);\n throw error;\n }\n }\n\n private async convertSwagger2_0ToOpenAPI3_0(swaggerDoc: any): Promise<any> {\n try {\n const result = await Swagger2OpenAPI.convertObj(swaggerDoc, {\n patch: true, // patch: true helps to fix minor issues\n warnOnly: true, // Do not throw on non-patchable errors\n });\n return result.openapi;\n } catch (error) {\n console.error('Error converting Swagger 2.0 to OpenAPI 3.0:', error);\n throw error;\n }\n }\n}\n","/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n SchedulerServiceTaskRunner,\n SchedulerService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport {\n ANNOTATION_LOCATION,\n ANNOTATION_ORIGIN_LOCATION,\n ApiEntity,\n Entity,\n} from '@backstage/catalog-model';\n\nimport type { Config } from '@backstage/config';\nimport { InputError, isError, NotFoundError } from '@backstage/errors';\n\nimport {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-node';\n\nimport {\n getProxyConfig,\n listApiDocs,\n listServices,\n} from '../clients/ThreeScaleAPIConnector';\nimport type {\n APIDocElement,\n APIDocs,\n Proxy,\n ServiceElement,\n Services,\n} from '../clients/types';\nimport { readThreeScaleApiEntityConfigs } from './config';\nimport { isNonEmptyArray, NonEmptyArray, ThreeScaleConfig } from './types';\nimport {\n isOpenAPI3_0,\n isSwagger1_2,\n isSwagger2_0,\n OpenAPIMergerAndConverter,\n} from './open-api-merger-converter';\nimport { Swagger } from 'atlassian-openapi';\n\nexport class ThreeScaleApiEntityProvider implements EntityProvider {\n private static SERVICES_FETCH_SIZE: number = 500;\n private readonly env: string;\n private readonly baseUrl: string;\n private readonly accessToken: string;\n private readonly logger: LoggerService;\n private readonly scheduleFn: () => Promise<void>;\n private readonly openApiMerger: OpenAPIMergerAndConverter;\n private connection?: EntityProviderConnection;\n\n static fromConfig(\n deps: {\n config: Config;\n logger: LoggerService;\n },\n\n options: {\n schedule: SchedulerServiceTaskRunner;\n scheduler: SchedulerService;\n },\n ): ThreeScaleApiEntityProvider[] {\n const providerConfigs = readThreeScaleApiEntityConfigs(deps.config);\n\n if (!options.schedule && !options.scheduler) {\n throw new Error('Either schedule or scheduler must be provided.');\n }\n\n return providerConfigs.map(providerConfig => {\n if (!options.schedule && !providerConfig.schedule) {\n throw new InputError(\n `No schedule provided via config for ThreeScaleApiEntityProvider:${providerConfig.id}.`,\n );\n }\n\n let taskRunner;\n\n if (options.scheduler && providerConfig.schedule) {\n // Create a scheduled task runner using the provided scheduler and schedule configuration\n taskRunner = options.scheduler.createScheduledTaskRunner(\n providerConfig.schedule,\n );\n } else if (options.schedule) {\n // Use the provided schedule directly\n taskRunner = options.schedule;\n } else {\n // Handle the case where both options.schedule and options.scheduler are missing\n throw new Error('Neither schedule nor scheduler is provided.');\n }\n\n return new ThreeScaleApiEntityProvider(\n providerConfig,\n deps.logger,\n taskRunner,\n );\n });\n }\n\n private constructor(\n config: ThreeScaleConfig,\n logger: LoggerService,\n taskRunner: SchedulerServiceTaskRunner,\n ) {\n this.env = config.id;\n this.baseUrl = config.baseUrl;\n this.accessToken = config.accessToken;\n this.logger = logger.child({\n target: this.getProviderName(),\n });\n\n this.scheduleFn = this.createScheduleFn(taskRunner);\n this.openApiMerger = new OpenAPIMergerAndConverter();\n }\n\n private createScheduleFn(\n taskRunner: SchedulerServiceTaskRunner,\n ): () => Promise<void> {\n return async () => {\n const taskId = `${this.getProviderName()}:run`;\n return taskRunner.run({\n id: taskId,\n fn: async () => {\n try {\n await this.run();\n } catch (error: any) {\n if (isError(error)) {\n // Ensure that we don't log any sensitive internal data:\n this.logger.error(\n `Error while syncing 3scale API from ${this.baseUrl}`,\n {\n // Default Error properties:\n name: error.name,\n message: error.message,\n stack: error.stack,\n // Additional status code if available:\n status: (error.response as { status?: string })?.status,\n },\n );\n }\n }\n },\n });\n };\n }\n\n getProviderName(): string {\n return `ThreeScaleApiEntityProvider:${this.env}`;\n }\n\n async connect(connection: EntityProviderConnection): Promise<void> {\n this.connection = connection;\n await this.scheduleFn();\n }\n\n async run(): Promise<void> {\n if (!this.connection) {\n throw new NotFoundError('Not initialized');\n }\n\n this.logger.info(`Discovering ApiEntities from 3scale ${this.baseUrl}`);\n\n const entities: Entity[] = [];\n\n let page: number = 0;\n let services: Services;\n let apiDocs: APIDocs;\n let fetchServices: boolean = true;\n while (fetchServices) {\n services = await listServices(\n this.baseUrl,\n this.accessToken,\n page,\n ThreeScaleApiEntityProvider.SERVICES_FETCH_SIZE,\n );\n apiDocs = await listApiDocs(this.baseUrl, this.accessToken);\n for (const element of services.services) {\n const service = element;\n this.logger.debug(`Find service ${service.service.name}`);\n\n const docs = apiDocs.api_docs.filter(\n obj => obj.api_doc.service_id === service.service.id,\n );\n const proxy = await getProxyConfig(\n this.baseUrl,\n this.accessToken,\n service.service.id,\n );\n if (isNonEmptyArray(docs)) {\n this.logger.info(JSON.stringify(docs));\n const apiEntity: ApiEntity = await this.buildApiEntityFromService(\n service,\n docs,\n proxy,\n );\n entities.push(apiEntity);\n this.logger.debug(`Discovered ApiEntity ${service.service.name}`);\n }\n }\n\n if (\n services.services.length <\n ThreeScaleApiEntityProvider.SERVICES_FETCH_SIZE\n ) {\n fetchServices = false;\n }\n page++;\n }\n\n this.logger.info(`Applying the mutation with ${entities.length} entities`);\n\n await this.connection.applyMutation({\n type: 'full',\n entities: entities.map(entity => ({\n entity,\n locationKey: this.getProviderName(),\n })),\n });\n }\n\n private async buildApiEntityFromService(\n service: ServiceElement,\n apiDocs: NonEmptyArray<APIDocElement>,\n proxy: Proxy,\n ): Promise<ApiEntity> {\n const location = `url:${this.baseUrl}/apiconfig/services/${service.service.id}`;\n const serviceDescription = service.service.description || '';\n let entityDescription: string | undefined;\n\n const docs = apiDocs.map(doc => JSON.parse(doc.api_doc.body));\n\n let swaggerDocJSON;\n if (docs.length > 1) {\n // convert all docs to openapi 3.0 and merge them\n let mergedDescription = `[Merged ${docs.length} API docs]`;\n let mergedTitle = mergedDescription;\n const convertedDocs: Swagger.SwaggerV3[] = [];\n for (const doc of docs) {\n const convertedDoc = await this.openApiMerger.convertAPIDocToOpenAPI3(\n doc,\n );\n convertedDocs.push(convertedDoc);\n mergedDescription = getDocInfo(convertedDoc)?.description\n ? `${mergedDescription} ${getDocInfo(convertedDoc)?.description}`\n : mergedDescription;\n mergedTitle = getDocInfo(convertedDoc)?.title\n ? `${mergedTitle} ${getDocInfo(convertedDoc)?.title}`\n : mergedTitle;\n }\n if (isNonEmptyArray(convertedDocs)) {\n swaggerDocJSON = await this.openApiMerger.mergeOpenAPI3Docs(\n convertedDocs,\n );\n swaggerDocJSON.info.description = mergedDescription;\n swaggerDocJSON.info.title = mergedTitle;\n entityDescription = mergedDescription;\n }\n }\n\n if (docs.length === 1) {\n swaggerDocJSON = docs[0];\n\n const spec = JSON.parse(apiDocs[0].api_doc.body);\n if (isSwagger1_2(spec)) {\n // Backstage UI can render only openapi 3.0 or swagger 2.0. That's why we need to convert swagger 1.2 to swagger 2.0.\n swaggerDocJSON = await this.openApiMerger.convertSwagger1_2To2_0(spec);\n }\n entityDescription = getDocInfo(spec)?.description;\n }\n\n return {\n kind: 'API',\n apiVersion: 'backstage.io/v1alpha1',\n metadata: {\n annotations: {\n [ANNOTATION_LOCATION]: location,\n [ANNOTATION_ORIGIN_LOCATION]: location,\n },\n // TODO: add tenant name\n name: `${service.service.system_name}`,\n description: entityDescription || serviceDescription,\n // TODO: add labels\n // labels: this.getApiEntityLabels(service),\n links: [\n {\n url: `${this.baseUrl}/apiconfig/services/${service.service.id}`,\n title: '3scale Overview',\n },\n {\n url: `${proxy.proxy.sandbox_endpoint}`,\n title: 'Staging Apicast Endpoint',\n },\n {\n url: `${proxy.proxy.endpoint}`,\n title: 'Production Apicast Endpoint',\n },\n ],\n },\n spec: {\n type: 'openapi',\n lifecycle: this.env,\n system: '3scale',\n owner: '3scale',\n definition: JSON.stringify(swaggerDocJSON, null, 2),\n },\n };\n }\n}\n\nfunction getDocInfo(\n spec: any,\n): { description: string; title: string } | undefined {\n if (isSwagger2_0(spec) || isOpenAPI3_0(spec)) {\n return spec.info;\n }\n\n // swagger 1.2 spec doc defined by single file doesn't have description field\n return undefined;\n}\n","/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';\n\nimport { ThreeScaleApiEntityProvider } from './providers';\n\nexport const catalogModule3ScaleEntityProvider = createBackendModule({\n moduleId: 'catalog-backend-module-3scale',\n pluginId: 'catalog',\n register(env) {\n env.registerInit({\n deps: {\n catalog: catalogProcessingExtensionPoint,\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n scheduler: coreServices.scheduler,\n },\n async init({ catalog, config, logger, scheduler }) {\n catalog.addEntityProvider(\n ThreeScaleApiEntityProvider.fromConfig(\n { config, logger },\n {\n scheduler: scheduler,\n schedule: scheduler.createScheduledTaskRunner({\n frequency: { minutes: 30 },\n timeout: { minutes: 3 },\n }),\n },\n ),\n );\n },\n });\n },\n});\n"],"names":["readSchedulerServiceTaskScheduleDefinitionFromConfig","merge","isErrorResult","SwaggerConverter","Swagger2OpenAPI","InputError","isError","NotFoundError","ANNOTATION_LOCATION","ANNOTATION_ORIGIN_LOCATION","createBackendModule","catalogProcessingExtensionPoint","coreServices"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;"}
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ var backendPluginApi = require('@backstage/backend-plugin-api');
4
+ var alpha = require('@backstage/plugin-catalog-node/alpha');
5
+ var ThreeScaleApiEntityProvider = require('./providers/ThreeScaleApiEntityProvider.cjs.js');
6
+
7
+ const catalogModule3ScaleEntityProvider = backendPluginApi.createBackendModule({
8
+ moduleId: "catalog-backend-module-3scale",
9
+ pluginId: "catalog",
10
+ register(env) {
11
+ env.registerInit({
12
+ deps: {
13
+ catalog: alpha.catalogProcessingExtensionPoint,
14
+ config: backendPluginApi.coreServices.rootConfig,
15
+ logger: backendPluginApi.coreServices.logger,
16
+ scheduler: backendPluginApi.coreServices.scheduler
17
+ },
18
+ async init({ catalog, config, logger, scheduler }) {
19
+ catalog.addEntityProvider(
20
+ ThreeScaleApiEntityProvider.ThreeScaleApiEntityProvider.fromConfig(
21
+ { config, logger },
22
+ {
23
+ scheduler,
24
+ schedule: scheduler.createScheduledTaskRunner({
25
+ frequency: { minutes: 30 },
26
+ timeout: { minutes: 3 }
27
+ })
28
+ }
29
+ )
30
+ );
31
+ }
32
+ });
33
+ }
34
+ });
35
+
36
+ exports.catalogModule3ScaleEntityProvider = catalogModule3ScaleEntityProvider;
37
+ //# sourceMappingURL=module.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.cjs.js","sources":["../src/module.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';\n\nimport { ThreeScaleApiEntityProvider } from './providers';\n\nexport const catalogModule3ScaleEntityProvider = createBackendModule({\n moduleId: 'catalog-backend-module-3scale',\n pluginId: 'catalog',\n register(env) {\n env.registerInit({\n deps: {\n catalog: catalogProcessingExtensionPoint,\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n scheduler: coreServices.scheduler,\n },\n async init({ catalog, config, logger, scheduler }) {\n catalog.addEntityProvider(\n ThreeScaleApiEntityProvider.fromConfig(\n { config, logger },\n {\n scheduler: scheduler,\n schedule: scheduler.createScheduledTaskRunner({\n frequency: { minutes: 30 },\n timeout: { minutes: 3 },\n }),\n },\n ),\n );\n },\n });\n },\n});\n"],"names":["createBackendModule","catalogProcessingExtensionPoint","coreServices","ThreeScaleApiEntityProvider"],"mappings":";;;;;;AAwBO,MAAM,oCAAoCA,oCAAoB,CAAA;AAAA,EACnE,QAAU,EAAA,+BAAA;AAAA,EACV,QAAU,EAAA,SAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,OAAS,EAAAC,qCAAA;AAAA,QACT,QAAQC,6BAAa,CAAA,UAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,MAAA;AAAA,QACrB,WAAWA,6BAAa,CAAA,SAAA;AAAA,OAC1B;AAAA,MACA,MAAM,IAAK,CAAA,EAAE,SAAS,MAAQ,EAAA,MAAA,EAAQ,WAAa,EAAA;AACjD,QAAQ,OAAA,CAAA,iBAAA;AAAA,UACNC,uDAA4B,CAAA,UAAA;AAAA,YAC1B,EAAE,QAAQ,MAAO,EAAA;AAAA,YACjB;AAAA,cACE,SAAA;AAAA,cACA,QAAA,EAAU,UAAU,yBAA0B,CAAA;AAAA,gBAC5C,SAAA,EAAW,EAAE,OAAA,EAAS,EAAG,EAAA;AAAA,gBACzB,OAAA,EAAS,EAAE,OAAA,EAAS,CAAE,EAAA;AAAA,eACvB,CAAA;AAAA,aACH;AAAA,WACF;AAAA,SACF,CAAA;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AACF,CAAC;;;;"}
@@ -0,0 +1,226 @@
1
+ 'use strict';
2
+
3
+ var catalogModel = require('@backstage/catalog-model');
4
+ var errors = require('@backstage/errors');
5
+ var ThreeScaleAPIConnector = require('../clients/ThreeScaleAPIConnector.cjs.js');
6
+ var config = require('./config.cjs.js');
7
+ var types = require('./types.cjs.js');
8
+ var openApiMergerConverter = require('./open-api-merger-converter.cjs.js');
9
+
10
+ class ThreeScaleApiEntityProvider {
11
+ static SERVICES_FETCH_SIZE = 500;
12
+ env;
13
+ baseUrl;
14
+ accessToken;
15
+ logger;
16
+ scheduleFn;
17
+ openApiMerger;
18
+ connection;
19
+ static fromConfig(deps, options) {
20
+ const providerConfigs = config.readThreeScaleApiEntityConfigs(deps.config);
21
+ if (!options.schedule && !options.scheduler) {
22
+ throw new Error("Either schedule or scheduler must be provided.");
23
+ }
24
+ return providerConfigs.map((providerConfig) => {
25
+ if (!options.schedule && !providerConfig.schedule) {
26
+ throw new errors.InputError(
27
+ `No schedule provided via config for ThreeScaleApiEntityProvider:${providerConfig.id}.`
28
+ );
29
+ }
30
+ let taskRunner;
31
+ if (options.scheduler && providerConfig.schedule) {
32
+ taskRunner = options.scheduler.createScheduledTaskRunner(
33
+ providerConfig.schedule
34
+ );
35
+ } else if (options.schedule) {
36
+ taskRunner = options.schedule;
37
+ } else {
38
+ throw new Error("Neither schedule nor scheduler is provided.");
39
+ }
40
+ return new ThreeScaleApiEntityProvider(
41
+ providerConfig,
42
+ deps.logger,
43
+ taskRunner
44
+ );
45
+ });
46
+ }
47
+ constructor(config, logger, taskRunner) {
48
+ this.env = config.id;
49
+ this.baseUrl = config.baseUrl;
50
+ this.accessToken = config.accessToken;
51
+ this.logger = logger.child({
52
+ target: this.getProviderName()
53
+ });
54
+ this.scheduleFn = this.createScheduleFn(taskRunner);
55
+ this.openApiMerger = new openApiMergerConverter.OpenAPIMergerAndConverter();
56
+ }
57
+ createScheduleFn(taskRunner) {
58
+ return async () => {
59
+ const taskId = `${this.getProviderName()}:run`;
60
+ return taskRunner.run({
61
+ id: taskId,
62
+ fn: async () => {
63
+ try {
64
+ await this.run();
65
+ } catch (error) {
66
+ if (errors.isError(error)) {
67
+ this.logger.error(
68
+ `Error while syncing 3scale API from ${this.baseUrl}`,
69
+ {
70
+ // Default Error properties:
71
+ name: error.name,
72
+ message: error.message,
73
+ stack: error.stack,
74
+ // Additional status code if available:
75
+ status: error.response?.status
76
+ }
77
+ );
78
+ }
79
+ }
80
+ }
81
+ });
82
+ };
83
+ }
84
+ getProviderName() {
85
+ return `ThreeScaleApiEntityProvider:${this.env}`;
86
+ }
87
+ async connect(connection) {
88
+ this.connection = connection;
89
+ await this.scheduleFn();
90
+ }
91
+ async run() {
92
+ if (!this.connection) {
93
+ throw new errors.NotFoundError("Not initialized");
94
+ }
95
+ this.logger.info(`Discovering ApiEntities from 3scale ${this.baseUrl}`);
96
+ const entities = [];
97
+ let page = 0;
98
+ let services;
99
+ let apiDocs;
100
+ let fetchServices = true;
101
+ while (fetchServices) {
102
+ services = await ThreeScaleAPIConnector.listServices(
103
+ this.baseUrl,
104
+ this.accessToken,
105
+ page,
106
+ ThreeScaleApiEntityProvider.SERVICES_FETCH_SIZE
107
+ );
108
+ apiDocs = await ThreeScaleAPIConnector.listApiDocs(this.baseUrl, this.accessToken);
109
+ for (const element of services.services) {
110
+ const service = element;
111
+ this.logger.debug(`Find service ${service.service.name}`);
112
+ const docs = apiDocs.api_docs.filter(
113
+ (obj) => obj.api_doc.service_id === service.service.id
114
+ );
115
+ const proxy = await ThreeScaleAPIConnector.getProxyConfig(
116
+ this.baseUrl,
117
+ this.accessToken,
118
+ service.service.id
119
+ );
120
+ if (types.isNonEmptyArray(docs)) {
121
+ this.logger.info(JSON.stringify(docs));
122
+ const apiEntity = await this.buildApiEntityFromService(
123
+ service,
124
+ docs,
125
+ proxy
126
+ );
127
+ entities.push(apiEntity);
128
+ this.logger.debug(`Discovered ApiEntity ${service.service.name}`);
129
+ }
130
+ }
131
+ if (services.services.length < ThreeScaleApiEntityProvider.SERVICES_FETCH_SIZE) {
132
+ fetchServices = false;
133
+ }
134
+ page++;
135
+ }
136
+ this.logger.info(`Applying the mutation with ${entities.length} entities`);
137
+ await this.connection.applyMutation({
138
+ type: "full",
139
+ entities: entities.map((entity) => ({
140
+ entity,
141
+ locationKey: this.getProviderName()
142
+ }))
143
+ });
144
+ }
145
+ async buildApiEntityFromService(service, apiDocs, proxy) {
146
+ const location = `url:${this.baseUrl}/apiconfig/services/${service.service.id}`;
147
+ const serviceDescription = service.service.description || "";
148
+ let entityDescription;
149
+ const docs = apiDocs.map((doc) => JSON.parse(doc.api_doc.body));
150
+ let swaggerDocJSON;
151
+ if (docs.length > 1) {
152
+ let mergedDescription = `[Merged ${docs.length} API docs]`;
153
+ let mergedTitle = mergedDescription;
154
+ const convertedDocs = [];
155
+ for (const doc of docs) {
156
+ const convertedDoc = await this.openApiMerger.convertAPIDocToOpenAPI3(
157
+ doc
158
+ );
159
+ convertedDocs.push(convertedDoc);
160
+ mergedDescription = getDocInfo(convertedDoc)?.description ? `${mergedDescription} ${getDocInfo(convertedDoc)?.description}` : mergedDescription;
161
+ mergedTitle = getDocInfo(convertedDoc)?.title ? `${mergedTitle} ${getDocInfo(convertedDoc)?.title}` : mergedTitle;
162
+ }
163
+ if (types.isNonEmptyArray(convertedDocs)) {
164
+ swaggerDocJSON = await this.openApiMerger.mergeOpenAPI3Docs(
165
+ convertedDocs
166
+ );
167
+ swaggerDocJSON.info.description = mergedDescription;
168
+ swaggerDocJSON.info.title = mergedTitle;
169
+ entityDescription = mergedDescription;
170
+ }
171
+ }
172
+ if (docs.length === 1) {
173
+ swaggerDocJSON = docs[0];
174
+ const spec = JSON.parse(apiDocs[0].api_doc.body);
175
+ if (openApiMergerConverter.isSwagger1_2(spec)) {
176
+ swaggerDocJSON = await this.openApiMerger.convertSwagger1_2To2_0(spec);
177
+ }
178
+ entityDescription = getDocInfo(spec)?.description;
179
+ }
180
+ return {
181
+ kind: "API",
182
+ apiVersion: "backstage.io/v1alpha1",
183
+ metadata: {
184
+ annotations: {
185
+ [catalogModel.ANNOTATION_LOCATION]: location,
186
+ [catalogModel.ANNOTATION_ORIGIN_LOCATION]: location
187
+ },
188
+ // TODO: add tenant name
189
+ name: `${service.service.system_name}`,
190
+ description: entityDescription || serviceDescription,
191
+ // TODO: add labels
192
+ // labels: this.getApiEntityLabels(service),
193
+ links: [
194
+ {
195
+ url: `${this.baseUrl}/apiconfig/services/${service.service.id}`,
196
+ title: "3scale Overview"
197
+ },
198
+ {
199
+ url: `${proxy.proxy.sandbox_endpoint}`,
200
+ title: "Staging Apicast Endpoint"
201
+ },
202
+ {
203
+ url: `${proxy.proxy.endpoint}`,
204
+ title: "Production Apicast Endpoint"
205
+ }
206
+ ]
207
+ },
208
+ spec: {
209
+ type: "openapi",
210
+ lifecycle: this.env,
211
+ system: "3scale",
212
+ owner: "3scale",
213
+ definition: JSON.stringify(swaggerDocJSON, null, 2)
214
+ }
215
+ };
216
+ }
217
+ }
218
+ function getDocInfo(spec) {
219
+ if (openApiMergerConverter.isSwagger2_0(spec) || openApiMergerConverter.isOpenAPI3_0(spec)) {
220
+ return spec.info;
221
+ }
222
+ return void 0;
223
+ }
224
+
225
+ exports.ThreeScaleApiEntityProvider = ThreeScaleApiEntityProvider;
226
+ //# sourceMappingURL=ThreeScaleApiEntityProvider.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThreeScaleApiEntityProvider.cjs.js","sources":["../../src/providers/ThreeScaleApiEntityProvider.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n SchedulerServiceTaskRunner,\n SchedulerService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport {\n ANNOTATION_LOCATION,\n ANNOTATION_ORIGIN_LOCATION,\n ApiEntity,\n Entity,\n} from '@backstage/catalog-model';\n\nimport type { Config } from '@backstage/config';\nimport { InputError, isError, NotFoundError } from '@backstage/errors';\n\nimport {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-node';\n\nimport {\n getProxyConfig,\n listApiDocs,\n listServices,\n} from '../clients/ThreeScaleAPIConnector';\nimport type {\n APIDocElement,\n APIDocs,\n Proxy,\n ServiceElement,\n Services,\n} from '../clients/types';\nimport { readThreeScaleApiEntityConfigs } from './config';\nimport { isNonEmptyArray, NonEmptyArray, ThreeScaleConfig } from './types';\nimport {\n isOpenAPI3_0,\n isSwagger1_2,\n isSwagger2_0,\n OpenAPIMergerAndConverter,\n} from './open-api-merger-converter';\nimport { Swagger } from 'atlassian-openapi';\n\nexport class ThreeScaleApiEntityProvider implements EntityProvider {\n private static SERVICES_FETCH_SIZE: number = 500;\n private readonly env: string;\n private readonly baseUrl: string;\n private readonly accessToken: string;\n private readonly logger: LoggerService;\n private readonly scheduleFn: () => Promise<void>;\n private readonly openApiMerger: OpenAPIMergerAndConverter;\n private connection?: EntityProviderConnection;\n\n static fromConfig(\n deps: {\n config: Config;\n logger: LoggerService;\n },\n\n options: {\n schedule: SchedulerServiceTaskRunner;\n scheduler: SchedulerService;\n },\n ): ThreeScaleApiEntityProvider[] {\n const providerConfigs = readThreeScaleApiEntityConfigs(deps.config);\n\n if (!options.schedule && !options.scheduler) {\n throw new Error('Either schedule or scheduler must be provided.');\n }\n\n return providerConfigs.map(providerConfig => {\n if (!options.schedule && !providerConfig.schedule) {\n throw new InputError(\n `No schedule provided via config for ThreeScaleApiEntityProvider:${providerConfig.id}.`,\n );\n }\n\n let taskRunner;\n\n if (options.scheduler && providerConfig.schedule) {\n // Create a scheduled task runner using the provided scheduler and schedule configuration\n taskRunner = options.scheduler.createScheduledTaskRunner(\n providerConfig.schedule,\n );\n } else if (options.schedule) {\n // Use the provided schedule directly\n taskRunner = options.schedule;\n } else {\n // Handle the case where both options.schedule and options.scheduler are missing\n throw new Error('Neither schedule nor scheduler is provided.');\n }\n\n return new ThreeScaleApiEntityProvider(\n providerConfig,\n deps.logger,\n taskRunner,\n );\n });\n }\n\n private constructor(\n config: ThreeScaleConfig,\n logger: LoggerService,\n taskRunner: SchedulerServiceTaskRunner,\n ) {\n this.env = config.id;\n this.baseUrl = config.baseUrl;\n this.accessToken = config.accessToken;\n this.logger = logger.child({\n target: this.getProviderName(),\n });\n\n this.scheduleFn = this.createScheduleFn(taskRunner);\n this.openApiMerger = new OpenAPIMergerAndConverter();\n }\n\n private createScheduleFn(\n taskRunner: SchedulerServiceTaskRunner,\n ): () => Promise<void> {\n return async () => {\n const taskId = `${this.getProviderName()}:run`;\n return taskRunner.run({\n id: taskId,\n fn: async () => {\n try {\n await this.run();\n } catch (error: any) {\n if (isError(error)) {\n // Ensure that we don't log any sensitive internal data:\n this.logger.error(\n `Error while syncing 3scale API from ${this.baseUrl}`,\n {\n // Default Error properties:\n name: error.name,\n message: error.message,\n stack: error.stack,\n // Additional status code if available:\n status: (error.response as { status?: string })?.status,\n },\n );\n }\n }\n },\n });\n };\n }\n\n getProviderName(): string {\n return `ThreeScaleApiEntityProvider:${this.env}`;\n }\n\n async connect(connection: EntityProviderConnection): Promise<void> {\n this.connection = connection;\n await this.scheduleFn();\n }\n\n async run(): Promise<void> {\n if (!this.connection) {\n throw new NotFoundError('Not initialized');\n }\n\n this.logger.info(`Discovering ApiEntities from 3scale ${this.baseUrl}`);\n\n const entities: Entity[] = [];\n\n let page: number = 0;\n let services: Services;\n let apiDocs: APIDocs;\n let fetchServices: boolean = true;\n while (fetchServices) {\n services = await listServices(\n this.baseUrl,\n this.accessToken,\n page,\n ThreeScaleApiEntityProvider.SERVICES_FETCH_SIZE,\n );\n apiDocs = await listApiDocs(this.baseUrl, this.accessToken);\n for (const element of services.services) {\n const service = element;\n this.logger.debug(`Find service ${service.service.name}`);\n\n const docs = apiDocs.api_docs.filter(\n obj => obj.api_doc.service_id === service.service.id,\n );\n const proxy = await getProxyConfig(\n this.baseUrl,\n this.accessToken,\n service.service.id,\n );\n if (isNonEmptyArray(docs)) {\n this.logger.info(JSON.stringify(docs));\n const apiEntity: ApiEntity = await this.buildApiEntityFromService(\n service,\n docs,\n proxy,\n );\n entities.push(apiEntity);\n this.logger.debug(`Discovered ApiEntity ${service.service.name}`);\n }\n }\n\n if (\n services.services.length <\n ThreeScaleApiEntityProvider.SERVICES_FETCH_SIZE\n ) {\n fetchServices = false;\n }\n page++;\n }\n\n this.logger.info(`Applying the mutation with ${entities.length} entities`);\n\n await this.connection.applyMutation({\n type: 'full',\n entities: entities.map(entity => ({\n entity,\n locationKey: this.getProviderName(),\n })),\n });\n }\n\n private async buildApiEntityFromService(\n service: ServiceElement,\n apiDocs: NonEmptyArray<APIDocElement>,\n proxy: Proxy,\n ): Promise<ApiEntity> {\n const location = `url:${this.baseUrl}/apiconfig/services/${service.service.id}`;\n const serviceDescription = service.service.description || '';\n let entityDescription: string | undefined;\n\n const docs = apiDocs.map(doc => JSON.parse(doc.api_doc.body));\n\n let swaggerDocJSON;\n if (docs.length > 1) {\n // convert all docs to openapi 3.0 and merge them\n let mergedDescription = `[Merged ${docs.length} API docs]`;\n let mergedTitle = mergedDescription;\n const convertedDocs: Swagger.SwaggerV3[] = [];\n for (const doc of docs) {\n const convertedDoc = await this.openApiMerger.convertAPIDocToOpenAPI3(\n doc,\n );\n convertedDocs.push(convertedDoc);\n mergedDescription = getDocInfo(convertedDoc)?.description\n ? `${mergedDescription} ${getDocInfo(convertedDoc)?.description}`\n : mergedDescription;\n mergedTitle = getDocInfo(convertedDoc)?.title\n ? `${mergedTitle} ${getDocInfo(convertedDoc)?.title}`\n : mergedTitle;\n }\n if (isNonEmptyArray(convertedDocs)) {\n swaggerDocJSON = await this.openApiMerger.mergeOpenAPI3Docs(\n convertedDocs,\n );\n swaggerDocJSON.info.description = mergedDescription;\n swaggerDocJSON.info.title = mergedTitle;\n entityDescription = mergedDescription;\n }\n }\n\n if (docs.length === 1) {\n swaggerDocJSON = docs[0];\n\n const spec = JSON.parse(apiDocs[0].api_doc.body);\n if (isSwagger1_2(spec)) {\n // Backstage UI can render only openapi 3.0 or swagger 2.0. That's why we need to convert swagger 1.2 to swagger 2.0.\n swaggerDocJSON = await this.openApiMerger.convertSwagger1_2To2_0(spec);\n }\n entityDescription = getDocInfo(spec)?.description;\n }\n\n return {\n kind: 'API',\n apiVersion: 'backstage.io/v1alpha1',\n metadata: {\n annotations: {\n [ANNOTATION_LOCATION]: location,\n [ANNOTATION_ORIGIN_LOCATION]: location,\n },\n // TODO: add tenant name\n name: `${service.service.system_name}`,\n description: entityDescription || serviceDescription,\n // TODO: add labels\n // labels: this.getApiEntityLabels(service),\n links: [\n {\n url: `${this.baseUrl}/apiconfig/services/${service.service.id}`,\n title: '3scale Overview',\n },\n {\n url: `${proxy.proxy.sandbox_endpoint}`,\n title: 'Staging Apicast Endpoint',\n },\n {\n url: `${proxy.proxy.endpoint}`,\n title: 'Production Apicast Endpoint',\n },\n ],\n },\n spec: {\n type: 'openapi',\n lifecycle: this.env,\n system: '3scale',\n owner: '3scale',\n definition: JSON.stringify(swaggerDocJSON, null, 2),\n },\n };\n }\n}\n\nfunction getDocInfo(\n spec: any,\n): { description: string; title: string } | undefined {\n if (isSwagger2_0(spec) || isOpenAPI3_0(spec)) {\n return spec.info;\n }\n\n // swagger 1.2 spec doc defined by single file doesn't have description field\n return undefined;\n}\n"],"names":["readThreeScaleApiEntityConfigs","InputError","OpenAPIMergerAndConverter","isError","NotFoundError","listServices","listApiDocs","getProxyConfig","isNonEmptyArray","isSwagger1_2","ANNOTATION_LOCATION","ANNOTATION_ORIGIN_LOCATION","isSwagger2_0","isOpenAPI3_0"],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ var backendPluginApi = require('@backstage/backend-plugin-api');
4
+
5
+ function readThreeScaleApiEntityConfigs(config) {
6
+ const providerConfigs = config.getOptionalConfig(
7
+ "catalog.providers.threeScaleApiEntity"
8
+ );
9
+ if (!providerConfigs) {
10
+ return [];
11
+ }
12
+ return providerConfigs.keys().map(
13
+ (id) => readThreeScaleApiEntityConfig(id, providerConfigs.getConfig(id))
14
+ );
15
+ }
16
+ function readThreeScaleApiEntityConfig(id, config) {
17
+ const baseUrl = config.getString("baseUrl");
18
+ const accessToken = config.getString("accessToken");
19
+ const systemLabel = config.getOptionalString("systemLabel");
20
+ const ownerLabel = config.getOptionalString("ownerLabel");
21
+ const addLabels = config.getOptionalBoolean("addLabels") || true;
22
+ const schedule = config.has("schedule") ? backendPluginApi.readSchedulerServiceTaskScheduleDefinitionFromConfig(
23
+ config.getConfig("schedule")
24
+ ) : void 0;
25
+ return {
26
+ id,
27
+ baseUrl,
28
+ accessToken,
29
+ systemLabel,
30
+ ownerLabel,
31
+ addLabels,
32
+ schedule
33
+ };
34
+ }
35
+
36
+ exports.readThreeScaleApiEntityConfigs = readThreeScaleApiEntityConfigs;
37
+ //# sourceMappingURL=config.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.cjs.js","sources":["../../src/providers/config.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { readSchedulerServiceTaskScheduleDefinitionFromConfig } from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\n\nimport { ThreeScaleConfig } from './types';\n\nexport function readThreeScaleApiEntityConfigs(\n config: Config,\n): ThreeScaleConfig[] {\n const providerConfigs = config.getOptionalConfig(\n 'catalog.providers.threeScaleApiEntity',\n );\n if (!providerConfigs) {\n return [];\n }\n return providerConfigs\n .keys()\n .map(id =>\n readThreeScaleApiEntityConfig(id, providerConfigs.getConfig(id)),\n );\n}\n\nfunction readThreeScaleApiEntityConfig(\n id: string,\n config: Config,\n): ThreeScaleConfig {\n const baseUrl = config.getString('baseUrl');\n const accessToken = config.getString('accessToken');\n const systemLabel = config.getOptionalString('systemLabel');\n const ownerLabel = config.getOptionalString('ownerLabel');\n const addLabels = config.getOptionalBoolean('addLabels') || true;\n\n const schedule = config.has('schedule')\n ? readSchedulerServiceTaskScheduleDefinitionFromConfig(\n config.getConfig('schedule'),\n )\n : undefined;\n\n return {\n id,\n baseUrl,\n accessToken,\n systemLabel,\n ownerLabel,\n addLabels,\n schedule,\n };\n}\n"],"names":["readSchedulerServiceTaskScheduleDefinitionFromConfig"],"mappings":";;;;AAoBO,SAAS,+BACd,MACoB,EAAA;AACpB,EAAA,MAAM,kBAAkB,MAAO,CAAA,iBAAA;AAAA,IAC7B,uCAAA;AAAA,GACF,CAAA;AACA,EAAA,IAAI,CAAC,eAAiB,EAAA;AACpB,IAAA,OAAO,EAAC,CAAA;AAAA,GACV;AACA,EAAO,OAAA,eAAA,CACJ,MACA,CAAA,GAAA;AAAA,IAAI,QACH,6BAA8B,CAAA,EAAA,EAAI,eAAgB,CAAA,SAAA,CAAU,EAAE,CAAC,CAAA;AAAA,GACjE,CAAA;AACJ,CAAA;AAEA,SAAS,6BAAA,CACP,IACA,MACkB,EAAA;AAClB,EAAM,MAAA,OAAA,GAAU,MAAO,CAAA,SAAA,CAAU,SAAS,CAAA,CAAA;AAC1C,EAAM,MAAA,WAAA,GAAc,MAAO,CAAA,SAAA,CAAU,aAAa,CAAA,CAAA;AAClD,EAAM,MAAA,WAAA,GAAc,MAAO,CAAA,iBAAA,CAAkB,aAAa,CAAA,CAAA;AAC1D,EAAM,MAAA,UAAA,GAAa,MAAO,CAAA,iBAAA,CAAkB,YAAY,CAAA,CAAA;AACxD,EAAA,MAAM,SAAY,GAAA,MAAA,CAAO,kBAAmB,CAAA,WAAW,CAAK,IAAA,IAAA,CAAA;AAE5D,EAAA,MAAM,QAAW,GAAA,MAAA,CAAO,GAAI,CAAA,UAAU,CAClC,GAAAA,qEAAA;AAAA,IACE,MAAA,CAAO,UAAU,UAAU,CAAA;AAAA,GAE7B,GAAA,KAAA,CAAA,CAAA;AAEJ,EAAO,OAAA;AAAA,IACL,EAAA;AAAA,IACA,OAAA;AAAA,IACA,WAAA;AAAA,IACA,WAAA;AAAA,IACA,UAAA;AAAA,IACA,SAAA;AAAA,IACA,QAAA;AAAA,GACF,CAAA;AACF;;;;"}
@@ -0,0 +1,81 @@
1
+ 'use strict';
2
+
3
+ var openapiMerge = require('openapi-merge');
4
+ var Swagger2OpenAPI = require('swagger2openapi');
5
+ var SwaggerConverter = require('swagger-converter');
6
+
7
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
8
+
9
+ var Swagger2OpenAPI__default = /*#__PURE__*/_interopDefaultCompat(Swagger2OpenAPI);
10
+ var SwaggerConverter__default = /*#__PURE__*/_interopDefaultCompat(SwaggerConverter);
11
+
12
+ function isSwagger1_2(apiDoc) {
13
+ return apiDoc.swaggerVersion && apiDoc.swaggerVersion === "1.2";
14
+ }
15
+ function isSwagger2_0(apiDoc) {
16
+ return apiDoc.swagger && apiDoc.swagger === "2.0";
17
+ }
18
+ function isOpenAPI3_0(apiDoc) {
19
+ return apiDoc.openapi;
20
+ }
21
+ class OpenAPIMergerAndConverter {
22
+ async mergeOpenAPI3Docs(docs) {
23
+ const mergeInput = docs.map((doc) => {
24
+ return { oas: doc };
25
+ });
26
+ const result = await openapiMerge.merge(mergeInput);
27
+ if (openapiMerge.isErrorResult(result)) {
28
+ throw new Error(result.message);
29
+ }
30
+ return result.output;
31
+ }
32
+ // Convert api doc to format openAPI 3. Do nothing with doc if it has format openAPI 3.0.
33
+ // 3scale supports API docs in formats:
34
+ // - swagger 1.2
35
+ // - swagger 2.0
36
+ // - openAPI 3.0
37
+ async convertAPIDocToOpenAPI3(apiDoc) {
38
+ if (isOpenAPI3_0(apiDoc)) {
39
+ return apiDoc;
40
+ }
41
+ if (isSwagger1_2(apiDoc)) {
42
+ const swagger2_0Doc = await this.convertSwagger1_2To2_0(apiDoc);
43
+ return await this.convertSwagger2_0ToOpenAPI3_0(swagger2_0Doc);
44
+ }
45
+ if (isSwagger2_0(apiDoc)) {
46
+ return await this.convertSwagger2_0ToOpenAPI3_0(apiDoc);
47
+ }
48
+ throw new Error(
49
+ `Unsupported API document. Plugin supports Swagger 1.2, 2.0, 3.0(Open API 3.0)`
50
+ );
51
+ }
52
+ async convertSwagger1_2To2_0(swaggerDoc) {
53
+ try {
54
+ const result = SwaggerConverter__default.default.convert(swaggerDoc, {});
55
+ return result;
56
+ } catch (error) {
57
+ console.error("Error converting Swagger 1.2 to Swagger 2.0:", error);
58
+ throw error;
59
+ }
60
+ }
61
+ async convertSwagger2_0ToOpenAPI3_0(swaggerDoc) {
62
+ try {
63
+ const result = await Swagger2OpenAPI__default.default.convertObj(swaggerDoc, {
64
+ patch: true,
65
+ // patch: true helps to fix minor issues
66
+ warnOnly: true
67
+ // Do not throw on non-patchable errors
68
+ });
69
+ return result.openapi;
70
+ } catch (error) {
71
+ console.error("Error converting Swagger 2.0 to OpenAPI 3.0:", error);
72
+ throw error;
73
+ }
74
+ }
75
+ }
76
+
77
+ exports.OpenAPIMergerAndConverter = OpenAPIMergerAndConverter;
78
+ exports.isOpenAPI3_0 = isOpenAPI3_0;
79
+ exports.isSwagger1_2 = isSwagger1_2;
80
+ exports.isSwagger2_0 = isSwagger2_0;
81
+ //# sourceMappingURL=open-api-merger-converter.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-api-merger-converter.cjs.js","sources":["../../src/providers/open-api-merger-converter.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { merge, isErrorResult, MergeInput } from 'openapi-merge';\nimport { Swagger } from 'atlassian-openapi';\nimport Swagger2OpenAPI from 'swagger2openapi';\n// @ts-ignore\nimport SwaggerConverter from 'swagger-converter';\nimport { NonEmptyArray } from './types';\n\nexport function isSwagger1_2(apiDoc: any): boolean {\n return apiDoc.swaggerVersion && apiDoc.swaggerVersion === '1.2';\n}\n\nexport function isSwagger2_0(apiDoc: any): boolean {\n return apiDoc.swagger && apiDoc.swagger === '2.0';\n}\n\nexport function isOpenAPI3_0(apiDoc: any): boolean {\n return apiDoc.openapi;\n}\n\nexport class OpenAPIMergerAndConverter {\n async mergeOpenAPI3Docs(\n docs: NonEmptyArray<Swagger.SwaggerV3>,\n ): Promise<Swagger.SwaggerV3> {\n const mergeInput: MergeInput = docs.map(doc => {\n return { oas: doc };\n });\n\n const result = await merge(mergeInput);\n if (isErrorResult(result)) {\n throw new Error(result.message);\n }\n return result.output;\n }\n\n // Convert api doc to format openAPI 3. Do nothing with doc if it has format openAPI 3.0.\n // 3scale supports API docs in formats:\n // - swagger 1.2\n // - swagger 2.0\n // - openAPI 3.0\n async convertAPIDocToOpenAPI3(apiDoc: any): Promise<Swagger.SwaggerV3> {\n if (isOpenAPI3_0(apiDoc)) {\n return apiDoc;\n }\n if (isSwagger1_2(apiDoc)) {\n // Unfortunately there is no library in the JavaScript world, which can convert both swagger 1.2 and 2.0 to openAPI 3.0.\n // That's why, for swagger 1.2 we are using convertation to swagger 2.0. And then swagger 2.0 will be converted to openAPI 3.0.\n const swagger2_0Doc = await this.convertSwagger1_2To2_0(apiDoc);\n return await this.convertSwagger2_0ToOpenAPI3_0(swagger2_0Doc);\n }\n if (isSwagger2_0(apiDoc)) {\n return await this.convertSwagger2_0ToOpenAPI3_0(apiDoc);\n }\n\n throw new Error(\n `Unsupported API document. Plugin supports Swagger 1.2, 2.0, 3.0(Open API 3.0)`,\n );\n }\n\n async convertSwagger1_2To2_0(swaggerDoc: any): Promise<any> {\n try {\n const result = SwaggerConverter.convert(swaggerDoc, {});\n return result;\n } catch (error) {\n console.error('Error converting Swagger 1.2 to Swagger 2.0:', error);\n throw error;\n }\n }\n\n private async convertSwagger2_0ToOpenAPI3_0(swaggerDoc: any): Promise<any> {\n try {\n const result = await Swagger2OpenAPI.convertObj(swaggerDoc, {\n patch: true, // patch: true helps to fix minor issues\n warnOnly: true, // Do not throw on non-patchable errors\n });\n return result.openapi;\n } catch (error) {\n console.error('Error converting Swagger 2.0 to OpenAPI 3.0:', error);\n throw error;\n }\n }\n}\n"],"names":["merge","isErrorResult","SwaggerConverter","Swagger2OpenAPI"],"mappings":";;;;;;;;;;;AAuBO,SAAS,aAAa,MAAsB,EAAA;AACjD,EAAO,OAAA,MAAA,CAAO,cAAkB,IAAA,MAAA,CAAO,cAAmB,KAAA,KAAA,CAAA;AAC5D,CAAA;AAEO,SAAS,aAAa,MAAsB,EAAA;AACjD,EAAO,OAAA,MAAA,CAAO,OAAW,IAAA,MAAA,CAAO,OAAY,KAAA,KAAA,CAAA;AAC9C,CAAA;AAEO,SAAS,aAAa,MAAsB,EAAA;AACjD,EAAA,OAAO,MAAO,CAAA,OAAA,CAAA;AAChB,CAAA;AAEO,MAAM,yBAA0B,CAAA;AAAA,EACrC,MAAM,kBACJ,IAC4B,EAAA;AAC5B,IAAM,MAAA,UAAA,GAAyB,IAAK,CAAA,GAAA,CAAI,CAAO,GAAA,KAAA;AAC7C,MAAO,OAAA,EAAE,KAAK,GAAI,EAAA,CAAA;AAAA,KACnB,CAAA,CAAA;AAED,IAAM,MAAA,MAAA,GAAS,MAAMA,kBAAA,CAAM,UAAU,CAAA,CAAA;AACrC,IAAI,IAAAC,0BAAA,CAAc,MAAM,CAAG,EAAA;AACzB,MAAM,MAAA,IAAI,KAAM,CAAA,MAAA,CAAO,OAAO,CAAA,CAAA;AAAA,KAChC;AACA,IAAA,OAAO,MAAO,CAAA,MAAA,CAAA;AAAA,GAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,wBAAwB,MAAyC,EAAA;AACrE,IAAI,IAAA,YAAA,CAAa,MAAM,CAAG,EAAA;AACxB,MAAO,OAAA,MAAA,CAAA;AAAA,KACT;AACA,IAAI,IAAA,YAAA,CAAa,MAAM,CAAG,EAAA;AAGxB,MAAA,MAAM,aAAgB,GAAA,MAAM,IAAK,CAAA,sBAAA,CAAuB,MAAM,CAAA,CAAA;AAC9D,MAAO,OAAA,MAAM,IAAK,CAAA,6BAAA,CAA8B,aAAa,CAAA,CAAA;AAAA,KAC/D;AACA,IAAI,IAAA,YAAA,CAAa,MAAM,CAAG,EAAA;AACxB,MAAO,OAAA,MAAM,IAAK,CAAA,6BAAA,CAA8B,MAAM,CAAA,CAAA;AAAA,KACxD;AAEA,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,6EAAA,CAAA;AAAA,KACF,CAAA;AAAA,GACF;AAAA,EAEA,MAAM,uBAAuB,UAA+B,EAAA;AAC1D,IAAI,IAAA;AACF,MAAA,MAAM,MAAS,GAAAC,iCAAA,CAAiB,OAAQ,CAAA,UAAA,EAAY,EAAE,CAAA,CAAA;AACtD,MAAO,OAAA,MAAA,CAAA;AAAA,aACA,KAAO,EAAA;AACd,MAAQ,OAAA,CAAA,KAAA,CAAM,gDAAgD,KAAK,CAAA,CAAA;AACnE,MAAM,MAAA,KAAA,CAAA;AAAA,KACR;AAAA,GACF;AAAA,EAEA,MAAc,8BAA8B,UAA+B,EAAA;AACzE,IAAI,IAAA;AACF,MAAA,MAAM,MAAS,GAAA,MAAMC,gCAAgB,CAAA,UAAA,CAAW,UAAY,EAAA;AAAA,QAC1D,KAAO,EAAA,IAAA;AAAA;AAAA,QACP,QAAU,EAAA,IAAA;AAAA;AAAA,OACX,CAAA,CAAA;AACD,MAAA,OAAO,MAAO,CAAA,OAAA,CAAA;AAAA,aACP,KAAO,EAAA;AACd,MAAQ,OAAA,CAAA,KAAA,CAAM,gDAAgD,KAAK,CAAA,CAAA;AACnE,MAAM,MAAA,KAAA,CAAA;AAAA,KACR;AAAA,GACF;AACF;;;;;;;"}
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ function isNonEmptyArray(arr) {
4
+ return arr.length > 0;
5
+ }
6
+
7
+ exports.isNonEmptyArray = isNonEmptyArray;
8
+ //# sourceMappingURL=types.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.cjs.js","sources":["../../src/providers/types.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { SchedulerServiceTaskScheduleDefinition } from '@backstage/backend-plugin-api';\n\nexport type ThreeScaleConfig = {\n id: string;\n baseUrl: string;\n accessToken: string;\n systemLabel?: string;\n ownerLabel?: string;\n addLabels?: boolean;\n schedule?: SchedulerServiceTaskScheduleDefinition;\n};\n\nexport type NonEmptyArray<T> = [T, ...T[]];\n\nexport function isNonEmptyArray<T>(arr: T[]): arr is NonEmptyArray<T> {\n return arr.length > 0;\n}\n"],"names":[],"mappings":";;AA8BO,SAAS,gBAAmB,GAAmC,EAAA;AACpE,EAAA,OAAO,IAAI,MAAS,GAAA,CAAA,CAAA;AACtB;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage-community/plugin-3scale-backend",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "types": "./dist/index.d.ts",
@@ -15,6 +15,7 @@
15
15
  },
16
16
  "exports": {
17
17
  ".": {
18
+ "backstage": "@backstage/BackendFeature",
18
19
  "require": "./dist/index.cjs.js",
19
20
  "types": "./dist/index.d.ts",
20
21
  "default": "./dist/index.cjs.js"
@@ -33,21 +34,21 @@
33
34
  "test": "backstage-cli package test --passWithNoTests --coverage"
34
35
  },
35
36
  "dependencies": {
36
- "@backstage/backend-plugin-api": "^1.0.0",
37
+ "@backstage/backend-plugin-api": "^1.0.1",
37
38
  "@backstage/catalog-model": "^1.7.0",
38
39
  "@backstage/errors": "^1.2.4",
39
- "@backstage/plugin-catalog-node": "^1.13.0",
40
+ "@backstage/plugin-catalog-node": "^1.13.1",
40
41
  "atlassian-openapi": "^1.0.19",
41
42
  "openapi-merge": "^1.3.3",
42
43
  "swagger-converter": "2.1.0",
43
44
  "swagger2openapi": "^7.0.4"
44
45
  },
45
46
  "devDependencies": {
46
- "@backstage/backend-defaults": "^0.5.0",
47
- "@backstage/backend-test-utils": "0.4.4",
48
- "@backstage/cli": "^0.27.1",
47
+ "@backstage/backend-defaults": "^0.5.2",
48
+ "@backstage/backend-test-utils": "^1.0.2",
49
+ "@backstage/cli": "^0.28.0",
49
50
  "@backstage/config": "^1.2.0",
50
- "@backstage/plugin-catalog-backend": "^1.26.0",
51
+ "@backstage/plugin-catalog-backend": "^1.27.1",
51
52
  "@janus-idp/cli": "1.13.1",
52
53
  "@types/supertest": "6.0.2",
53
54
  "@types/swagger2openapi": "^7.0.4",