@axis-backstage/plugin-readme-backend 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # Readme backend
2
+
3
+ Welcome to the readme backend plugin!
4
+
5
+ The plugin retrieves README.md files from the entity source location. The corresponding frontend plugin responsible for displaying this information is the [Readme plugin](https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/readme).
6
+
7
+ The plugin searches for a README file in the entity source location with any of the following file types:
8
+
9
+ ```ts
10
+ { name: 'README', type: 'text/plain' },
11
+ { name: 'README.md', type: 'text/markdown' },
12
+ { name: 'README.rst', type: 'text/plain' },
13
+ { name: 'README.txt', type: 'text/plain' },
14
+ { name: 'README.MD', type: 'text/markdown' },
15
+ ```
16
+
17
+ The plugin can also handle symlinks.
18
+
19
+ ## Setup
20
+
21
+ The following sections will help you get the Readme Backend plugin setup and running.
22
+
23
+ ### Installation
24
+
25
+ Install the plugin by following the example below:
26
+
27
+ ```bash
28
+ # From your Backstage root directory
29
+ yarn add --cwd packages/backend @axis-backstage/plugin-readme-backend
30
+ ```
31
+
32
+ ### Integrating
33
+
34
+ Here's how to get the backend plugin up and running:
35
+
36
+ 1. Create a new file named `packages/backend/src/plugins/readme.ts`, and add the following to it:
37
+
38
+ ```ts
39
+ import { createRouter } from '@axis-backstage/plugin-readme-backend';
40
+ import { Router } from 'express';
41
+ import { PluginEnvironment } from '../types';
42
+
43
+ eexport default async function createPlugin(
44
+ env: PluginEnvironment,
45
+ ): Promise<Router> {
46
+ return await createRouter({
47
+ logger: env.logger,
48
+ config: env.config,
49
+ reader: env.reader,
50
+ discovery: env.discovery,
51
+ tokenManager: env.tokenManager,
52
+ });
53
+ }
54
+ ```
55
+
56
+ 2. Wire this into the overall backend router by adding the following to `packages/backend/src/index.ts`:
57
+
58
+ ```ts
59
+ import readme from './plugins/readme';
60
+ ...
61
+
62
+ async function main() {
63
+ // Add this line under the other lines that follow the useHotMemoize pattern
64
+ const readmeEnv = useHotMemoize(module, () => createEnv('readme'),
65
+
66
+ // Add this under the lines that add their routers to apiRouter
67
+ apiRouter.use('/readme', await readme(readmeEnv));
68
+ }
69
+ ```
70
+
71
+ 3. Now run `yarn start-backend` from the repo root.
72
+
73
+ 4. In another terminal, run the command: `curl localhost:7007/api/readme/health`. The request should return `{"status":"ok"}`.
74
+
75
+ ### New Backend System
76
+
77
+ The Readme backend plugin has support for the [new backend system](https://backstage.io/docs/backend-system/). Here is how you can set it up:
78
+
79
+ In your `packages/backend/src/index.ts` make the following changes:
80
+
81
+ ```diff
82
+ + import { readmePlugin } from '@axis-backstage/readme-backend';
83
+
84
+ const backend = createBackend();
85
+ + backend.add(readmePlugin());
86
+ // ... other feature additions
87
+
88
+ backend.start();
89
+ ```
90
+
91
+ ### Troubleshooting
92
+
93
+ If the backend fails to provide README content for an entity, it could be due to several reasons.
94
+
95
+ #### No Integration Found for Entity
96
+
97
+ This error message indicates that there is no current integration with the external provider where the README file is located, such as GitHub, GitLab, or Gerrit. When the integration is missing, the backend does not have permission to access the README content.
98
+
99
+ To resolve this issue, set up the integration for the external provider where the README file is located. You can find more information about Backstage integrations in the [Backstage upstream documentation](https://backstage.io/docs/integrations/).
100
+
101
+ #### Not a Valid Location for Source Target
102
+
103
+ This error means that the entity source location cannot be found or is not a valid URL. The `entity source location` is always the same directory as the catalog-info.yaml file.
104
+
105
+ To debug this error, ensure that the entity source location is valid for the current entity. You can find the entity source location in the entity's catalog-info.yaml file. See the example below:
106
+
107
+ ```yaml
108
+ annotations:
109
+ backstage.io/source-location: url:https://github.com/AxisCommunications/backstage-plugins/blob/main/
110
+ ```
111
+
112
+ #### README Not Found for Entity
113
+
114
+ This error indicates that no README, README.md, README.rst, README.txt, or README.MD file was found for that entity. To resolve this error, ensure that there is a README file located in the entity source location with one of the following formats: **md**, **rst**, or **txt**.
@@ -0,0 +1,162 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var backendCommon = require('@backstage/backend-common');
6
+ var express = require('express');
7
+ var Router = require('express-promise-router');
8
+ var integration = require('@backstage/integration');
9
+ var catalogModel = require('@backstage/catalog-model');
10
+ var catalogClient = require('@backstage/catalog-client');
11
+ var backendPluginApi = require('@backstage/backend-plugin-api');
12
+
13
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
14
+
15
+ var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
16
+ var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
17
+
18
+ const DETECT_SYMLINKS_REGEX = "^(w+|.|/|-)+$";
19
+ const isSymLink = (content) => {
20
+ const lines = content.split("\n");
21
+ if (lines.length > 1)
22
+ return false;
23
+ const line = lines[0];
24
+ if (line.includes(" "))
25
+ return false;
26
+ const regex = RegExp(DETECT_SYMLINKS_REGEX);
27
+ return regex.test(content);
28
+ };
29
+
30
+ const DEFAULT_TTL = 1800 * 1e3;
31
+ const README_TYPES = [
32
+ { name: "README", type: "text/plain" },
33
+ { name: "README.md", type: "text/markdown" },
34
+ { name: "README.rst", type: "text/plain" },
35
+ { name: "README.txt", type: "text/plain" },
36
+ { name: "README.MD", type: "text/markdown" }
37
+ ];
38
+ async function createRouter(options) {
39
+ const { logger, config, reader, discovery, tokenManager } = options;
40
+ const catalogClient$1 = new catalogClient.CatalogClient({ discoveryApi: discovery });
41
+ const pluginCache = backendCommon.CacheManager.fromConfig(config).forPlugin("readme");
42
+ const cache = pluginCache.getClient({ defaultTtl: DEFAULT_TTL });
43
+ logger.info("Initializing readme backend");
44
+ const integrations = integration.ScmIntegrations.fromConfig(config);
45
+ const router = Router__default["default"]();
46
+ router.use(express__default["default"].json());
47
+ router.get("/health", (_, response) => {
48
+ response.json({ status: "ok" });
49
+ });
50
+ router.get("/:entityRef", async (request, response) => {
51
+ const { entityRef } = request.params;
52
+ const cacheDoc = await cache.get(entityRef);
53
+ if (cacheDoc) {
54
+ logger.info(`Loading README for ${entityRef} from cache.`);
55
+ response.type(cacheDoc.type);
56
+ response.send(cacheDoc.content);
57
+ return;
58
+ }
59
+ const { token } = await tokenManager.getToken();
60
+ const entity = await catalogClient$1.getEntityByRef(entityRef, { token });
61
+ if (!entity) {
62
+ logger.info(`No integration found for ${entityRef}`);
63
+ response.status(500).json({ error: `No integration found for ${entityRef}` });
64
+ return;
65
+ }
66
+ const source = catalogModel.getEntitySourceLocation(entity);
67
+ if (!source || source.type !== "url") {
68
+ logger.info(`Not valid location for ${source.target}`);
69
+ response.status(404).json({
70
+ error: `Not valid location for ${source.target}`
71
+ });
72
+ return;
73
+ }
74
+ const integration = integrations.byUrl(source.target);
75
+ if (!integration) {
76
+ logger.info(`No integration found for ${source.target}`);
77
+ response.status(500).json({ error: `No integration found for ${source.target}` });
78
+ return;
79
+ }
80
+ for (const fileType of README_TYPES) {
81
+ const url = integration.resolveUrl({
82
+ url: fileType.name,
83
+ base: source.target
84
+ });
85
+ let content;
86
+ try {
87
+ logger.info(`Fetch README ${entityRef}: ${url}, ${fileType.type} `);
88
+ response.type(fileType.type);
89
+ const urlResponse = await reader.readUrl(url);
90
+ content = (await urlResponse.buffer()).toString("utf-8");
91
+ if (isSymLink(content)) {
92
+ const symLinkUrl = integration.resolveUrl({
93
+ url: content,
94
+ base: source.target
95
+ });
96
+ const symLinkUrlResponse = await reader.readUrl(symLinkUrl);
97
+ content = (await symLinkUrlResponse.buffer()).toString("utf-8");
98
+ }
99
+ cache.set(entityRef, {
100
+ name: fileType.name,
101
+ type: fileType.type,
102
+ content
103
+ });
104
+ response.send(content);
105
+ return;
106
+ } catch (error) {
107
+ if (error instanceof Error && error.name === "NotFoundError") {
108
+ continue;
109
+ } else {
110
+ response.status(500).json({
111
+ error: `Readme failure: ${error}`
112
+ });
113
+ break;
114
+ }
115
+ }
116
+ }
117
+ logger.info(`Readme not found for ${entityRef}`);
118
+ response.status(404).json({
119
+ error: "Readme not found."
120
+ });
121
+ });
122
+ router.use(backendCommon.errorHandler());
123
+ return router;
124
+ }
125
+
126
+ const readmePlugin = backendPluginApi.createBackendPlugin({
127
+ pluginId: "readme",
128
+ register(env) {
129
+ env.registerInit({
130
+ deps: {
131
+ logger: backendPluginApi.coreServices.logger,
132
+ config: backendPluginApi.coreServices.rootConfig,
133
+ reader: backendPluginApi.coreServices.urlReader,
134
+ discovery: backendPluginApi.coreServices.discovery,
135
+ tokenManager: backendPluginApi.coreServices.tokenManager,
136
+ httpRouter: backendPluginApi.coreServices.httpRouter
137
+ },
138
+ async init({
139
+ logger,
140
+ config,
141
+ reader,
142
+ discovery,
143
+ tokenManager,
144
+ httpRouter
145
+ }) {
146
+ httpRouter.use(
147
+ await createRouter({
148
+ logger: backendCommon.loggerToWinstonLogger(logger),
149
+ config,
150
+ reader,
151
+ discovery,
152
+ tokenManager
153
+ })
154
+ );
155
+ }
156
+ });
157
+ }
158
+ });
159
+
160
+ exports.createRouter = createRouter;
161
+ exports["default"] = readmePlugin;
162
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/lib.ts","../src/service/router.ts","../src/plugin.ts"],"sourcesContent":["const DETECT_SYMLINKS_REGEX = '^(w+|.|/|-)+$';\n\nexport const isSymLink = (content: string): boolean => {\n const lines = content.split('\\n');\n if (lines.length > 1) return false;\n const line = lines[0];\n if (line.includes(' ')) return false;\n\n const regex = RegExp(DETECT_SYMLINKS_REGEX);\n return regex.test(content);\n};\n","import {\n errorHandler,\n TokenManager,\n UrlReader,\n} from '@backstage/backend-common';\nimport { Config } from '@backstage/config';\nimport { CacheManager } from '@backstage/backend-common';\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport { Logger } from 'winston';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { getEntitySourceLocation } from '@backstage/catalog-model';\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { DiscoveryApi } from '@backstage/plugin-permission-common';\nimport { isSymLink } from '../lib';\n\n/**\n * Constructs a readme router.\n * @public\n */\nexport interface RouterOptions {\n /**\n * Implementation of Winston logger\n */\n logger: Logger;\n\n /**\n * Backstage config object\n */\n config: Config;\n\n /**\n * Backstage url reader instance\n */\n reader: UrlReader;\n\n /**\n * Backstage discovery api instance\n */\n discovery: DiscoveryApi;\n\n /**\n * Backstage token manager instance\n */\n tokenManager: TokenManager;\n}\n\nconst DEFAULT_TTL = 1800 * 1000;\n\ninterface FileType {\n name: string;\n type: string;\n}\ninterface ReadmeFile extends FileType {\n content: string;\n}\n\nconst README_TYPES: FileType[] = [\n { name: 'README', type: 'text/plain' },\n { name: 'README.md', type: 'text/markdown' },\n { name: 'README.rst', type: 'text/plain' },\n { name: 'README.txt', type: 'text/plain' },\n { name: 'README.MD', type: 'text/markdown' },\n];\n\n/**\n * Constructs a readme router.\n *\n * @public\n */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const { logger, config, reader, discovery, tokenManager } = options;\n const catalogClient = new CatalogClient({ discoveryApi: discovery });\n\n const pluginCache = CacheManager.fromConfig(config).forPlugin('readme');\n const cache = pluginCache.getClient({ defaultTtl: DEFAULT_TTL });\n\n logger.info('Initializing readme backend');\n const integrations = ScmIntegrations.fromConfig(config);\n const router = Router();\n router.use(express.json());\n\n router.get('/health', (_, response) => {\n response.json({ status: 'ok' });\n });\n\n router.get('/:entityRef', async (request, response) => {\n const { entityRef } = request.params;\n const cacheDoc = (await cache.get(entityRef)) as ReadmeFile | undefined;\n\n if (cacheDoc) {\n logger.info(`Loading README for ${entityRef} from cache.`);\n response.type(cacheDoc.type);\n response.send(cacheDoc.content);\n return;\n }\n const { token } = await tokenManager.getToken();\n const entity = await catalogClient.getEntityByRef(entityRef, { token });\n if (!entity) {\n logger.info(`No integration found for ${entityRef}`);\n response\n .status(500)\n .json({ error: `No integration found for ${entityRef}` });\n return;\n }\n const source = getEntitySourceLocation(entity);\n\n if (!source || source.type !== 'url') {\n logger.info(`Not valid location for ${source.target}`);\n response.status(404).json({\n error: `Not valid location for ${source.target}`,\n });\n return;\n }\n const integration = integrations.byUrl(source.target);\n\n if (!integration) {\n logger.info(`No integration found for ${source.target}`);\n response\n .status(500)\n .json({ error: `No integration found for ${source.target}` });\n return;\n }\n\n for (const fileType of README_TYPES) {\n const url = integration.resolveUrl({\n url: fileType.name,\n base: source.target,\n });\n\n let content;\n\n try {\n logger.info(`Fetch README ${entityRef}: ${url}, ${fileType.type} `);\n response.type(fileType.type);\n\n const urlResponse = await reader.readUrl(url);\n content = (await urlResponse.buffer()).toString('utf-8');\n\n if (isSymLink(content)) {\n const symLinkUrl = integration.resolveUrl({\n url: content,\n base: source.target,\n });\n const symLinkUrlResponse = await reader.readUrl(symLinkUrl);\n content = (await symLinkUrlResponse.buffer()).toString('utf-8');\n }\n\n cache.set(entityRef, {\n name: fileType.name,\n type: fileType.type,\n content: content,\n });\n response.send(content);\n return;\n } catch (error: unknown) {\n if (error instanceof Error && error.name === 'NotFoundError') {\n // Try the next readme type\n continue;\n } else {\n response.status(500).json({\n error: `Readme failure: ${error}`,\n });\n break;\n }\n }\n }\n logger.info(`Readme not found for ${entityRef}`);\n response.status(404).json({\n error: 'Readme not found.',\n });\n });\n\n router.use(errorHandler());\n return router;\n}\n","import { loggerToWinstonLogger } from '@backstage/backend-common';\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './service/router';\n\n/**\n * The Readme backend plugin.\n *\n * @public\n */\nexport const readmePlugin = createBackendPlugin({\n pluginId: 'readme',\n register(env) {\n env.registerInit({\n deps: {\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n reader: coreServices.urlReader,\n discovery: coreServices.discovery,\n tokenManager: coreServices.tokenManager,\n httpRouter: coreServices.httpRouter,\n },\n async init({\n logger,\n config,\n reader,\n discovery,\n tokenManager,\n httpRouter,\n }) {\n httpRouter.use(\n await createRouter({\n logger: loggerToWinstonLogger(logger),\n config,\n reader,\n discovery,\n tokenManager,\n }),\n );\n },\n });\n },\n});\n"],"names":["catalogClient","CatalogClient","CacheManager","ScmIntegrations","Router","express","getEntitySourceLocation","errorHandler","createBackendPlugin","coreServices","loggerToWinstonLogger"],"mappings":";;;;;;;;;;;;;;;;;AAAA,MAAM,qBAAwB,GAAA,eAAA,CAAA;AAEjB,MAAA,SAAA,GAAY,CAAC,OAA6B,KAAA;AACrD,EAAM,MAAA,KAAA,GAAQ,OAAQ,CAAA,KAAA,CAAM,IAAI,CAAA,CAAA;AAChC,EAAA,IAAI,MAAM,MAAS,GAAA,CAAA;AAAG,IAAO,OAAA,KAAA,CAAA;AAC7B,EAAM,MAAA,IAAA,GAAO,MAAM,CAAC,CAAA,CAAA;AACpB,EAAI,IAAA,IAAA,CAAK,SAAS,GAAG,CAAA;AAAG,IAAO,OAAA,KAAA,CAAA;AAE/B,EAAM,MAAA,KAAA,GAAQ,OAAO,qBAAqB,CAAA,CAAA;AAC1C,EAAO,OAAA,KAAA,CAAM,KAAK,OAAO,CAAA,CAAA;AAC3B,CAAA;;ACqCA,MAAM,cAAc,IAAO,GAAA,GAAA,CAAA;AAU3B,MAAM,YAA2B,GAAA;AAAA,EAC/B,EAAE,IAAA,EAAM,QAAU,EAAA,IAAA,EAAM,YAAa,EAAA;AAAA,EACrC,EAAE,IAAA,EAAM,WAAa,EAAA,IAAA,EAAM,eAAgB,EAAA;AAAA,EAC3C,EAAE,IAAA,EAAM,YAAc,EAAA,IAAA,EAAM,YAAa,EAAA;AAAA,EACzC,EAAE,IAAA,EAAM,YAAc,EAAA,IAAA,EAAM,YAAa,EAAA;AAAA,EACzC,EAAE,IAAA,EAAM,WAAa,EAAA,IAAA,EAAM,eAAgB,EAAA;AAC7C,CAAA,CAAA;AAOA,eAAsB,aACpB,OACyB,EAAA;AACzB,EAAA,MAAM,EAAE,MAAQ,EAAA,MAAA,EAAQ,MAAQ,EAAA,SAAA,EAAW,cAAiB,GAAA,OAAA,CAAA;AAC5D,EAAA,MAAMA,kBAAgB,IAAIC,2BAAA,CAAc,EAAE,YAAA,EAAc,WAAW,CAAA,CAAA;AAEnE,EAAA,MAAM,cAAcC,0BAAa,CAAA,UAAA,CAAW,MAAM,CAAA,CAAE,UAAU,QAAQ,CAAA,CAAA;AACtE,EAAA,MAAM,QAAQ,WAAY,CAAA,SAAA,CAAU,EAAE,UAAA,EAAY,aAAa,CAAA,CAAA;AAE/D,EAAA,MAAA,CAAO,KAAK,6BAA6B,CAAA,CAAA;AACzC,EAAM,MAAA,YAAA,GAAeC,2BAAgB,CAAA,UAAA,CAAW,MAAM,CAAA,CAAA;AACtD,EAAA,MAAM,SAASC,0BAAO,EAAA,CAAA;AACtB,EAAO,MAAA,CAAA,GAAA,CAAIC,2BAAQ,CAAA,IAAA,EAAM,CAAA,CAAA;AAEzB,EAAA,MAAA,CAAO,GAAI,CAAA,SAAA,EAAW,CAAC,CAAA,EAAG,QAAa,KAAA;AACrC,IAAA,QAAA,CAAS,IAAK,CAAA,EAAE,MAAQ,EAAA,IAAA,EAAM,CAAA,CAAA;AAAA,GAC/B,CAAA,CAAA;AAED,EAAA,MAAA,CAAO,GAAI,CAAA,aAAA,EAAe,OAAO,OAAA,EAAS,QAAa,KAAA;AACrD,IAAM,MAAA,EAAE,SAAU,EAAA,GAAI,OAAQ,CAAA,MAAA,CAAA;AAC9B,IAAA,MAAM,QAAY,GAAA,MAAM,KAAM,CAAA,GAAA,CAAI,SAAS,CAAA,CAAA;AAE3C,IAAA,IAAI,QAAU,EAAA;AACZ,MAAO,MAAA,CAAA,IAAA,CAAK,CAAsB,mBAAA,EAAA,SAAS,CAAc,YAAA,CAAA,CAAA,CAAA;AACzD,MAAS,QAAA,CAAA,IAAA,CAAK,SAAS,IAAI,CAAA,CAAA;AAC3B,MAAS,QAAA,CAAA,IAAA,CAAK,SAAS,OAAO,CAAA,CAAA;AAC9B,MAAA,OAAA;AAAA,KACF;AACA,IAAA,MAAM,EAAE,KAAA,EAAU,GAAA,MAAM,aAAa,QAAS,EAAA,CAAA;AAC9C,IAAA,MAAM,SAAS,MAAML,eAAA,CAAc,eAAe,SAAW,EAAA,EAAE,OAAO,CAAA,CAAA;AACtE,IAAA,IAAI,CAAC,MAAQ,EAAA;AACX,MAAO,MAAA,CAAA,IAAA,CAAK,CAA4B,yBAAA,EAAA,SAAS,CAAE,CAAA,CAAA,CAAA;AACnD,MACG,QAAA,CAAA,MAAA,CAAO,GAAG,CACV,CAAA,IAAA,CAAK,EAAE,KAAO,EAAA,CAAA,yBAAA,EAA4B,SAAS,CAAA,CAAA,EAAI,CAAA,CAAA;AAC1D,MAAA,OAAA;AAAA,KACF;AACA,IAAM,MAAA,MAAA,GAASM,qCAAwB,MAAM,CAAA,CAAA;AAE7C,IAAA,IAAI,CAAC,MAAA,IAAU,MAAO,CAAA,IAAA,KAAS,KAAO,EAAA;AACpC,MAAA,MAAA,CAAO,IAAK,CAAA,CAAA,uBAAA,EAA0B,MAAO,CAAA,MAAM,CAAE,CAAA,CAAA,CAAA;AACrD,MAAS,QAAA,CAAA,MAAA,CAAO,GAAG,CAAA,CAAE,IAAK,CAAA;AAAA,QACxB,KAAA,EAAO,CAA0B,uBAAA,EAAA,MAAA,CAAO,MAAM,CAAA,CAAA;AAAA,OAC/C,CAAA,CAAA;AACD,MAAA,OAAA;AAAA,KACF;AACA,IAAA,MAAM,WAAc,GAAA,YAAA,CAAa,KAAM,CAAA,MAAA,CAAO,MAAM,CAAA,CAAA;AAEpD,IAAA,IAAI,CAAC,WAAa,EAAA;AAChB,MAAA,MAAA,CAAO,IAAK,CAAA,CAAA,yBAAA,EAA4B,MAAO,CAAA,MAAM,CAAE,CAAA,CAAA,CAAA;AACvD,MACG,QAAA,CAAA,MAAA,CAAO,GAAG,CAAA,CACV,IAAK,CAAA,EAAE,OAAO,CAA4B,yBAAA,EAAA,MAAA,CAAO,MAAM,CAAA,CAAA,EAAI,CAAA,CAAA;AAC9D,MAAA,OAAA;AAAA,KACF;AAEA,IAAA,KAAA,MAAW,YAAY,YAAc,EAAA;AACnC,MAAM,MAAA,GAAA,GAAM,YAAY,UAAW,CAAA;AAAA,QACjC,KAAK,QAAS,CAAA,IAAA;AAAA,QACd,MAAM,MAAO,CAAA,MAAA;AAAA,OACd,CAAA,CAAA;AAED,MAAI,IAAA,OAAA,CAAA;AAEJ,MAAI,IAAA;AACF,QAAO,MAAA,CAAA,IAAA,CAAK,gBAAgB,SAAS,CAAA,EAAA,EAAK,GAAG,CAAK,EAAA,EAAA,QAAA,CAAS,IAAI,CAAG,CAAA,CAAA,CAAA,CAAA;AAClE,QAAS,QAAA,CAAA,IAAA,CAAK,SAAS,IAAI,CAAA,CAAA;AAE3B,QAAA,MAAM,WAAc,GAAA,MAAM,MAAO,CAAA,OAAA,CAAQ,GAAG,CAAA,CAAA;AAC5C,QAAA,OAAA,GAAA,CAAW,MAAM,WAAA,CAAY,MAAO,EAAA,EAAG,SAAS,OAAO,CAAA,CAAA;AAEvD,QAAI,IAAA,SAAA,CAAU,OAAO,CAAG,EAAA;AACtB,UAAM,MAAA,UAAA,GAAa,YAAY,UAAW,CAAA;AAAA,YACxC,GAAK,EAAA,OAAA;AAAA,YACL,MAAM,MAAO,CAAA,MAAA;AAAA,WACd,CAAA,CAAA;AACD,UAAA,MAAM,kBAAqB,GAAA,MAAM,MAAO,CAAA,OAAA,CAAQ,UAAU,CAAA,CAAA;AAC1D,UAAA,OAAA,GAAA,CAAW,MAAM,kBAAA,CAAmB,MAAO,EAAA,EAAG,SAAS,OAAO,CAAA,CAAA;AAAA,SAChE;AAEA,QAAA,KAAA,CAAM,IAAI,SAAW,EAAA;AAAA,UACnB,MAAM,QAAS,CAAA,IAAA;AAAA,UACf,MAAM,QAAS,CAAA,IAAA;AAAA,UACf,OAAA;AAAA,SACD,CAAA,CAAA;AACD,QAAA,QAAA,CAAS,KAAK,OAAO,CAAA,CAAA;AACrB,QAAA,OAAA;AAAA,eACO,KAAgB,EAAA;AACvB,QAAA,IAAI,KAAiB,YAAA,KAAA,IAAS,KAAM,CAAA,IAAA,KAAS,eAAiB,EAAA;AAE5D,UAAA,SAAA;AAAA,SACK,MAAA;AACL,UAAS,QAAA,CAAA,MAAA,CAAO,GAAG,CAAA,CAAE,IAAK,CAAA;AAAA,YACxB,KAAA,EAAO,mBAAmB,KAAK,CAAA,CAAA;AAAA,WAChC,CAAA,CAAA;AACD,UAAA,MAAA;AAAA,SACF;AAAA,OACF;AAAA,KACF;AACA,IAAO,MAAA,CAAA,IAAA,CAAK,CAAwB,qBAAA,EAAA,SAAS,CAAE,CAAA,CAAA,CAAA;AAC/C,IAAS,QAAA,CAAA,MAAA,CAAO,GAAG,CAAA,CAAE,IAAK,CAAA;AAAA,MACxB,KAAO,EAAA,mBAAA;AAAA,KACR,CAAA,CAAA;AAAA,GACF,CAAA,CAAA;AAED,EAAO,MAAA,CAAA,GAAA,CAAIC,4BAAc,CAAA,CAAA;AACzB,EAAO,OAAA,MAAA,CAAA;AACT;;ACrKO,MAAM,eAAeC,oCAAoB,CAAA;AAAA,EAC9C,QAAU,EAAA,QAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,QAAQC,6BAAa,CAAA,MAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,UAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,SAAA;AAAA,QACrB,WAAWA,6BAAa,CAAA,SAAA;AAAA,QACxB,cAAcA,6BAAa,CAAA,YAAA;AAAA,QAC3B,YAAYA,6BAAa,CAAA,UAAA;AAAA,OAC3B;AAAA,MACA,MAAM,IAAK,CAAA;AAAA,QACT,MAAA;AAAA,QACA,MAAA;AAAA,QACA,MAAA;AAAA,QACA,SAAA;AAAA,QACA,YAAA;AAAA,QACA,UAAA;AAAA,OACC,EAAA;AACD,QAAW,UAAA,CAAA,GAAA;AAAA,UACT,MAAM,YAAa,CAAA;AAAA,YACjB,MAAA,EAAQC,oCAAsB,MAAM,CAAA;AAAA,YACpC,MAAA;AAAA,YACA,MAAA;AAAA,YACA,SAAA;AAAA,YACA,YAAA;AAAA,WACD,CAAA;AAAA,SACH,CAAA;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AACF,CAAC;;;;;"}
@@ -0,0 +1,48 @@
1
+ import { UrlReader, TokenManager } from '@backstage/backend-common';
2
+ import { Config } from '@backstage/config';
3
+ import express from 'express';
4
+ import { Logger } from 'winston';
5
+ import { DiscoveryApi } from '@backstage/plugin-permission-common';
6
+ import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
7
+
8
+ /**
9
+ * Constructs a readme router.
10
+ * @public
11
+ */
12
+ interface RouterOptions {
13
+ /**
14
+ * Implementation of Winston logger
15
+ */
16
+ logger: Logger;
17
+ /**
18
+ * Backstage config object
19
+ */
20
+ config: Config;
21
+ /**
22
+ * Backstage url reader instance
23
+ */
24
+ reader: UrlReader;
25
+ /**
26
+ * Backstage discovery api instance
27
+ */
28
+ discovery: DiscoveryApi;
29
+ /**
30
+ * Backstage token manager instance
31
+ */
32
+ tokenManager: TokenManager;
33
+ }
34
+ /**
35
+ * Constructs a readme router.
36
+ *
37
+ * @public
38
+ */
39
+ declare function createRouter(options: RouterOptions): Promise<express.Router>;
40
+
41
+ /**
42
+ * The Readme backend plugin.
43
+ *
44
+ * @public
45
+ */
46
+ declare const readmePlugin: () => _backstage_backend_plugin_api.BackendFeature;
47
+
48
+ export { RouterOptions, createRouter, readmePlugin as default };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@axis-backstage/plugin-readme-backend",
3
+ "version": "0.1.0",
4
+ "main": "dist/index.cjs.js",
5
+ "types": "dist/index.d.ts",
6
+ "license": "Apache-2.0",
7
+ "publishConfig": {
8
+ "access": "public",
9
+ "main": "dist/index.cjs.js",
10
+ "types": "dist/index.d.ts"
11
+ },
12
+ "backstage": {
13
+ "role": "backend-plugin"
14
+ },
15
+ "scripts": {
16
+ "start": "backstage-cli package start",
17
+ "build": "backstage-cli package build",
18
+ "lint": "backstage-cli package lint",
19
+ "test": "backstage-cli package test",
20
+ "clean": "backstage-cli package clean",
21
+ "prepack": "backstage-cli package prepack",
22
+ "postpack": "backstage-cli package postpack"
23
+ },
24
+ "dependencies": {
25
+ "@backstage/backend-common": "^0.19.8",
26
+ "@backstage/backend-plugin-api": "^0.6.7",
27
+ "@backstage/catalog-client": "^1.4.6",
28
+ "@backstage/catalog-model": "^1.4.3",
29
+ "@backstage/config": "^1.1.1",
30
+ "@backstage/integration": "^1.7.2",
31
+ "@backstage/plugin-permission-common": "^0.7.10",
32
+ "@types/express": "*",
33
+ "express": "^4.17.1",
34
+ "express-promise-router": "^4.1.0",
35
+ "node-fetch": "^2.6.7",
36
+ "winston": "^3.2.1",
37
+ "yn": "^4.0.0"
38
+ },
39
+ "devDependencies": {
40
+ "@backstage/cli": "^0.23.0",
41
+ "@types/supertest": "^2.0.12",
42
+ "msw": "^1.0.0",
43
+ "supertest": "^6.2.4"
44
+ },
45
+ "files": [
46
+ "dist"
47
+ ]
48
+ }