@backstage/repo-tools 0.5.0-next.0 → 0.5.0-next.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,29 @@
1
1
  # @backstage/repo-tools
2
2
 
3
+ ## 0.5.0-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - f909e9d: Includes templates in @backstage/repo-tools package and use them in the CLI
8
+ - da3c4db: Updates the `schema openapi generate-client` command to export all generated types from the generated directory.
9
+ - 7959f23: The `api-reports` command now checks for api report files that no longer apply.
10
+ If it finds such files, it's treated basically the same as report errors do, and
11
+ the check fails.
12
+
13
+ For example, if you had an `api-report-alpha.md` but then removed the alpha
14
+ export, the reports generator would now report that this file needs to be
15
+ deleted.
16
+
17
+ - f49e237: Fixed a bug where `schema openapi init` created an invalid test command.
18
+ - f91be2c: Updated dependency `@stoplight/types` to `^14.0.0`.
19
+ - 45bfb20: Execute `openapi-generator-cli` from `@backstage/repo-tools` directory to force it to use our openapitools.json config file.
20
+ - Updated dependencies
21
+ - @backstage/backend-common@0.20.0-next.2
22
+ - @backstage/catalog-model@1.4.3
23
+ - @backstage/cli-common@0.1.13
24
+ - @backstage/cli-node@0.2.0
25
+ - @backstage/errors@1.2.3
26
+
3
27
  ## 0.5.0-next.0
4
28
 
5
29
  ### Minor Changes
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var lodash = require('lodash');
3
4
  var path = require('path');
4
5
  var child_process = require('child_process');
5
6
  var fs = require('fs-extra');
@@ -255,7 +256,9 @@ async function runApiExtraction({
255
256
  };
256
257
  }
257
258
  const warnings = new Array();
258
- for (const { packageDir, name } of packageEntryPoints) {
259
+ for (const [packageDir, group] of Object.entries(
260
+ lodash.groupBy(packageEntryPoints, (ep) => ep.packageDir)
261
+ )) {
259
262
  console.log(`## Processing ${packageDir}`);
260
263
  const noBail = Array.isArray(allowWarnings) ? allowWarnings.some((aw) => aw === packageDir || minimatch__default["default"](packageDir, aw)) : allowWarnings;
261
264
  const projectFolder = paths.paths.resolveTargetRoot(packageDir);
@@ -263,165 +266,190 @@ async function runApiExtraction({
263
266
  "./dist-types",
264
267
  packageDir
265
268
  );
266
- const suffix = name === "index" ? "" : `-${name}`;
267
- const reportFileName = `api-report${suffix}.md`;
268
- const reportPath = path.resolve(projectFolder, reportFileName);
269
- const warningCountBefore = await countApiReportWarnings(reportPath);
270
- const extractorConfig = apiExtractor.ExtractorConfig.prepare({
271
- configObject: {
272
- mainEntryPointFilePath: path.resolve(packageFolder, `src/${name}.d.ts`),
273
- bundledPackages: [],
274
- compiler: {
275
- tsconfigFilePath
276
- },
277
- apiReport: {
278
- enabled: true,
279
- reportFileName,
280
- reportFolder: projectFolder,
281
- reportTempFolder: path.resolve(
282
- outputDir,
283
- `<unscopedPackageName>${suffix}`
284
- )
285
- },
286
- docModel: {
287
- // TODO(Rugvip): This skips docs for non-index entry points. We can try to work around it, but
288
- // most likely it makes sense to wait for API Extractor to natively support exports.
289
- enabled: name === "index",
290
- apiJsonFilePath: path.resolve(
291
- outputDir,
292
- `<unscopedPackageName>${suffix}.api.json`
293
- )
294
- },
295
- dtsRollup: {
296
- enabled: false
297
- },
298
- tsdocMetadata: {
299
- enabled: false
300
- },
301
- messages: {
302
- // Silence compiler warnings, as these will prevent the CI build to work
303
- compilerMessageReporting: {
304
- default: {
305
- logLevel: "none"
306
- // These contain absolute file paths, so can't be included in the report
307
- // addToApiReportFile: true,
308
- }
269
+ const names = group.map((ep) => ep.name);
270
+ const remainingReportFiles = new Set(
271
+ fs__default["default"].readdirSync(projectFolder).filter(
272
+ (filename) => filename.match(/^(.+)-api-report\.md$/) || filename.match(/^api-report(-.+)?\.md$/)
273
+ )
274
+ );
275
+ for (const name of names) {
276
+ const suffix = name === "index" ? "" : `-${name}`;
277
+ const reportFileName = `api-report${suffix}.md`;
278
+ const reportPath = path.resolve(projectFolder, reportFileName);
279
+ remainingReportFiles.delete(reportFileName);
280
+ const warningCountBefore = await countApiReportWarnings(reportPath);
281
+ const extractorConfig = apiExtractor.ExtractorConfig.prepare({
282
+ configObject: {
283
+ mainEntryPointFilePath: path.resolve(
284
+ packageFolder,
285
+ `src/${name}.d.ts`
286
+ ),
287
+ bundledPackages: [],
288
+ compiler: {
289
+ tsconfigFilePath
290
+ },
291
+ apiReport: {
292
+ enabled: true,
293
+ reportFileName,
294
+ reportFolder: projectFolder,
295
+ reportTempFolder: path.resolve(
296
+ outputDir,
297
+ `<unscopedPackageName>${suffix}`
298
+ )
299
+ },
300
+ docModel: {
301
+ // TODO(Rugvip): This skips docs for non-index entry points. We can try to work around it, but
302
+ // most likely it makes sense to wait for API Extractor to natively support exports.
303
+ enabled: name === "index",
304
+ apiJsonFilePath: path.resolve(
305
+ outputDir,
306
+ `<unscopedPackageName>${suffix}.api.json`
307
+ )
308
+ },
309
+ dtsRollup: {
310
+ enabled: false
309
311
  },
310
- extractorMessageReporting: {
311
- default: {
312
- logLevel: "warning",
313
- addToApiReportFile: true
312
+ tsdocMetadata: {
313
+ enabled: false
314
+ },
315
+ messages: {
316
+ // Silence compiler warnings, as these will prevent the CI build to work
317
+ compilerMessageReporting: {
318
+ default: {
319
+ logLevel: "none"
320
+ // These contain absolute file paths, so can't be included in the report
321
+ // addToApiReportFile: true,
322
+ }
323
+ },
324
+ extractorMessageReporting: {
325
+ default: {
326
+ logLevel: "warning",
327
+ addToApiReportFile: true
328
+ },
329
+ ...messagesConf
314
330
  },
315
- ...messagesConf
331
+ tsdocMessageReporting: {
332
+ default: {
333
+ logLevel: "warning",
334
+ addToApiReportFile: true
335
+ }
336
+ }
316
337
  },
317
- tsdocMessageReporting: {
318
- default: {
319
- logLevel: "warning",
320
- addToApiReportFile: true
338
+ newlineKind: "lf",
339
+ projectFolder
340
+ },
341
+ configObjectFullPath: projectFolder,
342
+ packageJsonFullPath: path.resolve(projectFolder, "package.json"),
343
+ tsdocConfigFile: await getTsDocConfig(),
344
+ ignoreMissingEntryPoint: true
345
+ });
346
+ extractorConfig.packageFolder = packageFolder;
347
+ if (!compilerState) {
348
+ compilerState = apiExtractor.CompilerState.create(extractorConfig, {
349
+ additionalEntryPoints: entryPoints
350
+ });
351
+ }
352
+ apiExtractor.Extractor._checkCompilerCompatibility = () => {
353
+ };
354
+ let shouldLogInstructions = false;
355
+ let conflictingFile = void 0;
356
+ const extractorResult = apiExtractor.Extractor.invoke(extractorConfig, {
357
+ localBuild: isLocalBuild,
358
+ showVerboseMessages: false,
359
+ showDiagnostics: false,
360
+ messageCallback(message) {
361
+ if (message.text.includes("The API report file is missing")) {
362
+ shouldLogInstructions = true;
363
+ }
364
+ if (message.text.includes(
365
+ "You have changed the public API signature for this project."
366
+ )) {
367
+ shouldLogInstructions = true;
368
+ const match = message.text.match(
369
+ /Please copy the file "(.*)" to "api-report\.md"/
370
+ );
371
+ if (match) {
372
+ conflictingFile = match[1];
321
373
  }
322
374
  }
323
375
  },
324
- newlineKind: "lf",
325
- projectFolder
326
- },
327
- configObjectFullPath: projectFolder,
328
- packageJsonFullPath: path.resolve(projectFolder, "package.json"),
329
- tsdocConfigFile: await getTsDocConfig(),
330
- ignoreMissingEntryPoint: true
331
- });
332
- extractorConfig.packageFolder = packageFolder;
333
- if (!compilerState) {
334
- compilerState = apiExtractor.CompilerState.create(extractorConfig, {
335
- additionalEntryPoints: entryPoints
376
+ compilerState
336
377
  });
337
- }
338
- apiExtractor.Extractor._checkCompilerCompatibility = () => {
339
- };
340
- let shouldLogInstructions = false;
341
- let conflictingFile = void 0;
342
- const extractorResult = apiExtractor.Extractor.invoke(extractorConfig, {
343
- localBuild: isLocalBuild,
344
- showVerboseMessages: false,
345
- showDiagnostics: false,
346
- messageCallback(message) {
347
- if (message.text.includes("The API report file is missing")) {
348
- shouldLogInstructions = true;
349
- }
350
- if (message.text.includes(
351
- "You have changed the public API signature for this project."
352
- )) {
353
- shouldLogInstructions = true;
354
- const match = message.text.match(
355
- /Please copy the file "(.*)" to "api-report\.md"/
378
+ if (validateReleaseTags && fs__default["default"].pathExistsSync(extractorConfig.reportFilePath)) {
379
+ if (["index", "alpha", "beta"].includes(name)) {
380
+ const report = await fs__default["default"].readFile(
381
+ extractorConfig.reportFilePath,
382
+ "utf8"
356
383
  );
357
- if (match) {
358
- conflictingFile = match[1];
384
+ const lines = report.split(/\r?\n/);
385
+ const expectedTag = name === "index" ? "public" : name;
386
+ for (let i = 0; i < lines.length; i += 1) {
387
+ const line = lines[i];
388
+ const match = line.match(/^\/\/ @(alpha|beta|public)/);
389
+ if (match && match[1] !== expectedTag) {
390
+ if (expectedTag !== "public" && match[1] === "public") {
391
+ continue;
392
+ }
393
+ throw new Error(
394
+ `Unexpected release tag ${match[1]} in ${extractorConfig.reportFilePath} at line ${i + 1}`
395
+ );
396
+ }
359
397
  }
360
398
  }
361
- },
362
- compilerState
363
- });
364
- if (validateReleaseTags && fs__default["default"].pathExistsSync(extractorConfig.reportFilePath)) {
365
- if (["index", "alpha", "beta"].includes(name)) {
366
- const report = await fs__default["default"].readFile(
367
- extractorConfig.reportFilePath,
368
- "utf8"
369
- );
370
- const lines = report.split(/\r?\n/);
371
- const expectedTag = name === "index" ? "public" : name;
372
- for (let i = 0; i < lines.length; i += 1) {
373
- const line = lines[i];
374
- const match = line.match(/^\/\/ @(alpha|beta|public)/);
375
- if (match && match[1] !== expectedTag) {
376
- if (expectedTag !== "public" && match[1] === "public") {
377
- continue;
378
- }
379
- throw new Error(
380
- `Unexpected release tag ${match[1]} in ${extractorConfig.reportFilePath} at line ${i + 1}`
399
+ }
400
+ if (!extractorResult.succeeded) {
401
+ if (shouldLogInstructions) {
402
+ logApiReportInstructions();
403
+ if (conflictingFile) {
404
+ console.log("");
405
+ console.log(
406
+ `The conflicting file is ${path.relative(
407
+ tmpDir,
408
+ conflictingFile
409
+ )}, with the following content:`
381
410
  );
411
+ console.log("");
412
+ const content = await fs__default["default"].readFile(conflictingFile, "utf8");
413
+ console.log(content);
414
+ logApiReportInstructions();
382
415
  }
383
416
  }
417
+ throw new Error(
418
+ `API Extractor completed with ${extractorResult.errorCount} errors and ${extractorResult.warningCount} warnings`
419
+ );
420
+ }
421
+ const warningCountAfter = await countApiReportWarnings(reportPath);
422
+ if (noBail) {
423
+ console.log(`Skipping warnings check for ${packageDir}`);
424
+ }
425
+ if (warningCountAfter > 0 && !noBail) {
426
+ throw new Error(
427
+ `The API Report for ${packageDir} is not allowed to have warnings`
428
+ );
429
+ }
430
+ if (warningCountAfter === 0 && allowWarningPkg.includes(packageDir)) {
431
+ console.log(
432
+ `No need to allow warnings for ${packageDir}, it does not have any`
433
+ );
434
+ }
435
+ if (warningCountAfter > warningCountBefore) {
436
+ warnings.push(
437
+ `The API Report for ${packageDir} introduces new warnings. Please fix these warnings in order to keep the API Reports tidy.`
438
+ );
384
439
  }
385
440
  }
386
- if (!extractorResult.succeeded) {
387
- if (shouldLogInstructions) {
388
- logApiReportInstructions();
389
- if (conflictingFile) {
390
- console.log("");
391
- console.log(
392
- `The conflicting file is ${path.relative(
393
- tmpDir,
394
- conflictingFile
395
- )}, with the following content:`
396
- );
397
- console.log("");
398
- const content = await fs__default["default"].readFile(conflictingFile, "utf8");
399
- console.log(content);
400
- logApiReportInstructions();
441
+ if (remainingReportFiles.size > 0) {
442
+ if (isLocalBuild) {
443
+ for (const f of remainingReportFiles) {
444
+ fs__default["default"].rmSync(path.resolve(projectFolder, f));
445
+ console.log(`Deleted deprecated API report ${f}`);
401
446
  }
447
+ } else {
448
+ const staleList = [...remainingReportFiles].map((f) => path.join(packageDir, f)).join(", ");
449
+ throw new Error(
450
+ `The API Report(s) ${staleList} are no longer relevant and should be deleted`
451
+ );
402
452
  }
403
- throw new Error(
404
- `API Extractor completed with ${extractorResult.errorCount} errors and ${extractorResult.warningCount} warnings`
405
- );
406
- }
407
- const warningCountAfter = await countApiReportWarnings(reportPath);
408
- if (noBail) {
409
- console.log(`Skipping warnings check for ${packageDir}`);
410
- }
411
- if (warningCountAfter > 0 && !noBail) {
412
- throw new Error(
413
- `The API Report for ${packageDir} is not allowed to have warnings`
414
- );
415
- }
416
- if (warningCountAfter === 0 && allowWarningPkg.includes(packageDir)) {
417
- console.log(
418
- `No need to allow warnings for ${packageDir}, it does not have any`
419
- );
420
- }
421
- if (warningCountAfter > warningCountBefore) {
422
- warnings.push(
423
- `The API Report for ${packageDir} introduces new warnings. Please fix these warnings in order to keep the API Reports tidy.`
424
- );
425
453
  }
426
454
  }
427
455
  if (warnings.length > 0) {
@@ -1150,4 +1178,4 @@ function parseArrayOption(value) {
1150
1178
  }
1151
1179
 
1152
1180
  exports.buildApiReports = buildApiReports;
1153
- //# sourceMappingURL=api-reports-0697bd2a.cjs.js.map
1181
+ //# sourceMappingURL=api-reports-143209ff.cjs.js.map
@@ -6,6 +6,7 @@ var constants = require('./constants-866c449d.cjs.js');
6
6
  var paths = require('./paths-9ab9b8a8.cjs.js');
7
7
  var fs = require('fs-extra');
8
8
  var exec = require('./exec-7bf444eb.cjs.js');
9
+ var backendCommon = require('@backstage/backend-common');
9
10
  require('@backstage/cli-common');
10
11
  require('@backstage/cli-node');
11
12
  require('minimatch');
@@ -27,10 +28,9 @@ async function generate(spec, outputDirectory) {
27
28
  constants.OPENAPI_IGNORE_FILES.join("\n")
28
29
  );
29
30
  await exec.exec(
30
- // The actual main.js file for the binary isn't executable but yarn does _something_ to make it executable.
31
- // TODO (sennyeya@): Make this use the actual binary
32
- `yarn openapi-generator-cli`,
31
+ "node",
33
32
  [
33
+ backendCommon.resolvePackagePath("@openapitools/openapi-generator-cli", "main.js"),
34
34
  "generate",
35
35
  "-i",
36
36
  resolvedOpenapiPath,
@@ -39,24 +39,27 @@ async function generate(spec, outputDirectory) {
39
39
  "-g",
40
40
  "typescript",
41
41
  "-c",
42
- "templates/typescript-backstage.yaml",
42
+ backendCommon.resolvePackagePath(
43
+ "@backstage/repo-tools",
44
+ "templates/typescript-backstage.yaml"
45
+ ),
43
46
  "--generator-key",
44
47
  "v3.0"
45
48
  ],
46
49
  {
47
50
  maxBuffer: Number.MAX_VALUE,
48
- cwd: paths.paths.ownDir,
51
+ cwd: backendCommon.resolvePackagePath("@backstage/repo-tools"),
49
52
  env: {
50
53
  ...process.env
51
- // PWD: outputDirectory,
52
54
  }
53
55
  }
54
56
  );
55
57
  await exec.exec(
56
58
  `yarn backstage-cli package lint --fix ${resolvedOutputDirectory}`
57
59
  );
58
- if (paths.paths.resolveTargetRoot("node_modules/.bin/prettier")) {
59
- await exec.exec(`yarn prettier --write ${resolvedOutputDirectory}`);
60
+ const prettier = paths.paths.resolveTargetRoot("node_modules/.bin/prettier");
61
+ if (prettier) {
62
+ await exec.exec(`${prettier} --write ${resolvedOutputDirectory}`);
60
63
  }
61
64
  fs__default["default"].removeSync(path.resolve(resolvedOutputDirectory, ".openapi-generator-ignore"));
62
65
  fs__default["default"].rmSync(path.resolve(resolvedOutputDirectory, ".openapi-generator"), {
@@ -80,4 +83,4 @@ async function singleCommand({
80
83
  }
81
84
 
82
85
  exports.singleCommand = singleCommand;
83
- //# sourceMappingURL=generate-be7745a5.cjs.js.map
86
+ //# sourceMappingURL=generate-675bd523.cjs.js.map
@@ -32,7 +32,7 @@ async function init(directoryPath) {
32
32
  );
33
33
  }
34
34
  const opticConfigFilePath = path.join(directoryPath, "optic.yml");
35
- if (!await fs__default["default"].pathExists(opticConfigFilePath)) {
35
+ if (await fs__default["default"].pathExists(opticConfigFilePath)) {
36
36
  throw new Error(`This directory already has an optic.yml file. Exiting.`);
37
37
  }
38
38
  await fs__default["default"].writeFile(
@@ -53,7 +53,7 @@ capture:
53
53
  # \u{1F527} Specify a command that will generate traffic
54
54
  command: yarn backstage-cli package test --no-watch ${ROUTER_TEST_PATHS.map(
55
55
  (e) => `"${e}"`
56
- ).join(",")}
56
+ ).join(" ")}
57
57
  `
58
58
  );
59
59
  if (await paths.paths.resolveTargetRoot("node_modules/.bin/prettier")) {
@@ -83,4 +83,4 @@ async function initCommand(paths = []) {
83
83
  }
84
84
 
85
85
  exports["default"] = initCommand;
86
- //# sourceMappingURL=init-9d8f5e0f.cjs.js.map
86
+ //# sourceMappingURL=init-5e60457a.cjs.js.map
package/dist/index.cjs.js CHANGED
@@ -62,10 +62,10 @@ function registerSchemaCommand(program) {
62
62
  "Fail on any linting severity messages, not just errors."
63
63
  ).action(lazy(() => Promise.resolve().then(function () { return require('./cjs/lint-e06bf899.cjs.js'); }).then((m) => m.bulkCommand)));
64
64
  openApiCommand.command("test [paths...]").description("Test OpenAPI schemas against written tests").option("--update", "Update the spec on failure.").action(lazy(() => Promise.resolve().then(function () { return require('./cjs/index-ed4aaab1.cjs.js'); }).then((m) => m.bulkCommand)));
65
- openApiCommand.command("init <paths...>").description("Creates any config needed for the test command.").action(lazy(() => Promise.resolve().then(function () { return require('./cjs/init-9d8f5e0f.cjs.js'); }).then((m) => m.default)));
65
+ openApiCommand.command("init <paths...>").description("Creates any config needed for the test command.").action(lazy(() => Promise.resolve().then(function () { return require('./cjs/init-5e60457a.cjs.js'); }).then((m) => m.default)));
66
66
  openApiCommand.command("generate-client").requiredOption("--input-spec <file>").requiredOption("--output-directory <directory>").action(
67
67
  lazy(
68
- () => Promise.resolve().then(function () { return require('./cjs/generate-be7745a5.cjs.js'); }).then((m) => m.singleCommand)
68
+ () => Promise.resolve().then(function () { return require('./cjs/generate-675bd523.cjs.js'); }).then((m) => m.singleCommand)
69
69
  )
70
70
  );
71
71
  }
@@ -93,7 +93,7 @@ function registerCommands(program) {
93
93
  "Turn on release tag validation for the public, beta, and alpha APIs"
94
94
  ).description("Generate an API report for selected packages").action(
95
95
  lazy(
96
- () => Promise.resolve().then(function () { return require('./cjs/api-reports-0697bd2a.cjs.js'); }).then((m) => m.buildApiReports)
96
+ () => Promise.resolve().then(function () { return require('./cjs/api-reports-143209ff.cjs.js'); }).then((m) => m.buildApiReports)
97
97
  )
98
98
  );
99
99
  program.command("type-deps").description("Find inconsistencies in types of all packages and plugins").action(lazy(() => Promise.resolve().then(function () { return require('./cjs/type-deps-5eacd931.cjs.js'); }).then((m) => m.default)));
@@ -125,7 +125,7 @@ function lazy(getActionFunc) {
125
125
  };
126
126
  }
127
127
 
128
- var version = "0.5.0-next.0";
128
+ var version = "0.5.0-next.1";
129
129
 
130
130
  const main = (argv) => {
131
131
  commander.program.name("backstage-repo-tools").version(version);
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "../../node_modules/@openapitools/openapi-generator-cli/config.schema.json",
3
+ "spaces": 2,
4
+ "generator-cli": {
5
+ "version": "6.5.0"
6
+ }
7
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/repo-tools",
3
3
  "description": "CLI for Backstage repo tooling ",
4
- "version": "0.5.0-next.0",
4
+ "version": "0.5.0-next.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -32,6 +32,7 @@
32
32
  "dependencies": {
33
33
  "@apidevtools/swagger-parser": "^10.1.0",
34
34
  "@apisyouwonthate/style-guide": "^1.4.0",
35
+ "@backstage/backend-common": "^0.20.0-next.2",
35
36
  "@backstage/catalog-model": "^1.4.3",
36
37
  "@backstage/cli-common": "^0.1.13",
37
38
  "@backstage/cli-node": "^0.2.0",
@@ -46,7 +47,7 @@
46
47
  "@stoplight/spectral-parsers": "^1.0.2",
47
48
  "@stoplight/spectral-rulesets": "^1.18.0",
48
49
  "@stoplight/spectral-runtime": "^1.1.2",
49
- "@stoplight/types": "^13.14.0",
50
+ "@stoplight/types": "^14.0.0",
50
51
  "chalk": "^4.0.0",
51
52
  "codeowners-utils": "^1.0.2",
52
53
  "commander": "^9.1.0",
@@ -62,7 +63,7 @@
62
63
  "yaml-diff-patch": "^2.0.0"
63
64
  },
64
65
  "devDependencies": {
65
- "@backstage/cli": "^0.25.0-next.1",
66
+ "@backstage/cli": "^0.25.0-next.2",
66
67
  "@backstage/types": "^1.1.1",
67
68
  "@types/is-glob": "^4.0.2",
68
69
  "@types/mock-fs": "^4.13.0",
@@ -85,7 +86,9 @@
85
86
  },
86
87
  "files": [
87
88
  "bin",
88
- "dist/**/*.js"
89
+ "dist/**/*.js",
90
+ "templates",
91
+ "openapitools.json"
89
92
  ],
90
93
  "nodemonConfig": {
91
94
  "watch": "./src",
@@ -0,0 +1,114 @@
1
+ {{>licenseInfo}}
2
+ import { DiscoveryApi } from '../types/discovery';
3
+ import { FetchApi } from '../types/fetch';
4
+ import crossFetch from 'cross-fetch';
5
+ import {pluginId} from '../pluginId';
6
+ import * as parser from 'uri-template';
7
+
8
+ {{#imports}}
9
+ import { {{classname}} } from '{{filename}}.model{{importFileExtension}}';
10
+ {{/imports}}
11
+
12
+ type TypedResponse<T> = Omit<Response, 'json'> & {
13
+ json: () => Promise<T>;
14
+ };
15
+
16
+ {{#operations}}
17
+
18
+ /**
19
+ * Options you can pass into a request for additional information.
20
+ *
21
+ * @public
22
+ */
23
+ export interface RequestOptions {
24
+ token?: string;
25
+ }
26
+
27
+ /**
28
+ * {{{description}}}{{^description}}no description{{/description}}
29
+ */
30
+ export class {{classname}}Client {
31
+ private readonly discoveryApi: DiscoveryApi;
32
+ private readonly fetchApi: FetchApi;
33
+
34
+ constructor(options: {
35
+ discoveryApi: { getBaseUrl(pluginId: string): Promise<string> };
36
+ fetchApi?: { fetch: typeof fetch };
37
+ }) {
38
+ this.discoveryApi = options.discoveryApi;
39
+ this.fetchApi = options.fetchApi || { fetch: crossFetch };
40
+ }
41
+
42
+ {{#operation}}
43
+ /**
44
+ {{#notes}}
45
+ * {{&notes}}
46
+ {{/notes}}
47
+ {{#summary}}
48
+ * {{&summary}}
49
+ {{/summary}}
50
+ {{#allParams}}
51
+ * @param {{paramName}} {{description}}
52
+ {{/allParams}}
53
+ */
54
+ public async {{nickname}}(
55
+ // @ts-ignore
56
+ request: {
57
+ {{#hasPathParams}}
58
+ path: {
59
+ {{#pathParams}}
60
+ {{paramName}}{{^required}}?{{/required}}: {{{dataType}}},
61
+ {{/pathParams}}
62
+ },
63
+ {{/hasPathParams}}
64
+ {{#hasBodyParam}}
65
+ {{#bodyParam}}
66
+ body: {{{dataType}}},
67
+ {{/bodyParam}}
68
+ {{/hasBodyParam}}
69
+ {{#hasQueryParams}}
70
+ query: {
71
+ {{#queryParams}}
72
+ {{paramName}}{{^required}}?{{/required}}: {{{dataType}}},
73
+ {{/queryParams}}
74
+ },
75
+ {{/hasQueryParams}}
76
+ {{#hasHeaderParams}}
77
+ header: {
78
+ {{#headerParams}}
79
+ {{paramName}}{{^required}}?{{/required}}: {{{dataType}}},
80
+ {{/headerParams}}
81
+ },
82
+ {{/hasHeaderParams}}
83
+ },
84
+ options?: RequestOptions
85
+ ): Promise<TypedResponse<{{{returnType}}} {{^returnType}}void{{/returnType}}>> {
86
+ const baseUrl = await this.discoveryApi.getBaseUrl(pluginId);
87
+
88
+ const uriTemplate = `{{{path}}}{{#hasQueryParams}}{?{{#queryParams}}{{baseName}}{{#isArray}}{{#isExplode}}*{{/isExplode}}{{/isArray}}{{^-last}},{{/-last}}{{/queryParams}}}{{/hasQueryParams}}`;
89
+
90
+ const uri = parser.parse(uriTemplate).expand({
91
+ {{#pathParams}}
92
+ {{baseName}}: request.path.{{paramName}},
93
+ {{/pathParams}}
94
+ {{#hasQueryParams}}
95
+ ...request.query,
96
+ {{/hasQueryParams}}
97
+ })
98
+
99
+ return await this.fetchApi.fetch(`${baseUrl}${uri}`, {
100
+ headers: {
101
+ {{#hasHeaderParams}}
102
+ ...request.header,
103
+ {{/hasHeaderParams}}
104
+ 'Content-Type': 'application/json',
105
+ ...(options?.token && { Authorization: `Bearer ${options?.token}` }),
106
+ },
107
+ method: '{{httpMethod}}',
108
+ {{#hasBodyParam}} body: JSON.stringify(request.body), {{/hasBodyParam}}
109
+ });
110
+ }
111
+
112
+ {{/operation}}
113
+ }
114
+ {{/operations}}
@@ -0,0 +1,3 @@
1
+ //
2
+
3
+ export * from './DefaultApi.client';
@@ -0,0 +1,4 @@
1
+ //
2
+
3
+ export * from './apis';
4
+ export * from './models';
@@ -0,0 +1,43 @@
1
+ //
2
+
3
+ {{#models}}
4
+ {{#model}}
5
+ {{#tsImports}}
6
+ import { {{classname}} } from '{{filename}}.model{{importFileExtension}}';
7
+ {{/tsImports}}
8
+
9
+ {{#description}}
10
+ /**
11
+ * {{{.}}}
12
+ */
13
+ {{/description}}
14
+ {{^isEnum}}
15
+ export interface {{classname}} {
16
+ {{#additionalPropertiesType}}
17
+ [key: string]: {{{additionalPropertiesType}}};
18
+ {{/additionalPropertiesType}}
19
+ {{#vars}}
20
+ {{#description}}
21
+ /**
22
+ * {{{.}}}
23
+ */
24
+ {{/description}}
25
+ '{{name}}'{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#isNullable}} | null{{/isNullable}};
26
+ {{/vars}}
27
+ }
28
+
29
+ {{#hasEnums}}
30
+
31
+ {{#vars}}
32
+ {{#isEnum}}
33
+ export type {{classname}}{{enumName}} ={{#allowableValues}}{{#values}} "{{.}}" {{^-last}}|{{/-last}}{{/values}}{{/allowableValues}};
34
+ {{/isEnum}}
35
+ {{/vars}}
36
+
37
+ {{/hasEnums}}
38
+ {{/isEnum}}
39
+ {{#isEnum}}
40
+ export type {{classname}} ={{#allowableValues}}{{#values}} "{{.}}" {{^-last}}|{{/-last}}{{/values}}{{/allowableValues}};
41
+ {{/isEnum}}
42
+ {{/model}}
43
+ {{/models}}
@@ -0,0 +1,7 @@
1
+ //
2
+
3
+ {{#models}}
4
+ {{#model}}
5
+ export * from '{{{ importPath }}}.model{{importFileExtension}}'
6
+ {{/model}}
7
+ {{/models}}
@@ -0,0 +1,6 @@
1
+
2
+ {{#servers}}
3
+ {{#-last}}
4
+ export const pluginId = "{{url}}";
5
+ {{/-last}}
6
+ {{/servers}}
@@ -0,0 +1,22 @@
1
+ /*
2
+ * Copyright 2023 The Backstage Authors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ /**
18
+ * This is a copy of the DiscoveryApi, to avoid importing core-plugin-api.
19
+ */
20
+ export type DiscoveryApi = {
21
+ getBaseUrl(pluginId: string): Promise<string>;
22
+ };
@@ -0,0 +1,22 @@
1
+ /*
2
+ * Copyright 2023 The Backstage Authors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ /**
18
+ * This is a copy of FetchApi, to avoid importing core-plugin-api.
19
+ */
20
+ export type FetchApi = {
21
+ fetch: typeof fetch;
22
+ };
@@ -0,0 +1,21 @@
1
+ templateDir: templates/typescript-backstage
2
+
3
+ files:
4
+ api.mustache:
5
+ templateType: API
6
+ # For some reason, they check for destinationFilename differences. We have to change the ending to override the file.
7
+ destinationFilename: .client.ts
8
+ model.mustache:
9
+ templateType: Model
10
+ destinationFilename: .model.ts
11
+ models/models_all.mustache:
12
+ templateType: SupportingFiles
13
+ destinationFilename: models/index.ts
14
+ types/fetch.ts: {}
15
+ types/discovery.ts: {}
16
+ apis/index.mustache:
17
+ templateType: SupportingFiles
18
+ destinationFilename: apis/index.ts
19
+ pluginId.mustache:
20
+ templateType: SupportingFiles
21
+ destinationFilename: pluginId.ts