@red-hat-developer-hub/backstage-plugin-catalog-techdoc-url-reader-backend 0.5.0 → 0.6.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/CHANGELOG.md +25 -0
- package/dist/index.d.ts +0 -1
- package/dist/plugin.cjs.js +29 -8
- package/dist/plugin.cjs.js.map +1 -1
- package/package.json +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @red-hat-developer-hub/backstage-plugin-catalog-techdoc-url-reader-backend
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 56c40be: Integrate Backstage discovery and auth services for connector communication.
|
|
8
|
+
|
|
9
|
+
The model-catalog entity provider now resolves the connector base URL via the
|
|
10
|
+
discovery service and authenticates with backend-to-backend service tokens
|
|
11
|
+
instead of unauthenticated direct HTTP calls. The `fromConfig` factory method
|
|
12
|
+
accepts `discovery` and `auth` as additional required dependencies.
|
|
13
|
+
|
|
14
|
+
The techdoc URL reader adds bearer-token authentication when fetching model
|
|
15
|
+
cards and replaces hostname-based bridge URL matching with path-based matching
|
|
16
|
+
that uses the connector plugin ID.
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- c2e3749: bump workspace to 1.52.0
|
|
21
|
+
|
|
22
|
+
## 0.5.1
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- 4418084: Upgrade workspace to Backstage 1.49.4
|
|
27
|
+
|
|
3
28
|
## 0.5.0
|
|
4
29
|
|
|
5
30
|
### Minor Changes
|
package/dist/index.d.ts
CHANGED
package/dist/plugin.cjs.js
CHANGED
|
@@ -57,13 +57,22 @@ function readBridgeConfigs(config) {
|
|
|
57
57
|
return configs.keys().map((id) => readBridgeConfig(id, configs.getConfig(id)));
|
|
58
58
|
}
|
|
59
59
|
function readBridgeConfig(id, config) {
|
|
60
|
-
|
|
60
|
+
let url = "";
|
|
61
|
+
if (config.has("baseUrl")) {
|
|
62
|
+
url = config.getString("baseUrl");
|
|
63
|
+
}
|
|
61
64
|
return { id, baseUrl: url };
|
|
62
65
|
}
|
|
63
66
|
class ModeCatalogBridgeTechdocUrlReader {
|
|
64
67
|
workDir;
|
|
65
68
|
logger;
|
|
66
69
|
bridgeConfigs;
|
|
70
|
+
// TODO we are limited in which core services can be passed into the ReaderFactory, so as a work around we
|
|
71
|
+
// define these globals that are set in the plugin init method; if we merge this plugin with the
|
|
72
|
+
// kserve-kubeflow-connector plugins, and bypass the need for plugin to plug REST calls, we can
|
|
73
|
+
// bypass the need for the discover and auth services.
|
|
74
|
+
static discovery;
|
|
75
|
+
static auth;
|
|
67
76
|
static factory = ({ config, logger }) => {
|
|
68
77
|
const reader = new ModeCatalogBridgeTechdocUrlReader(config, logger);
|
|
69
78
|
const predicate = (url) => reader.bridgePredicate(url);
|
|
@@ -83,21 +92,29 @@ class ModeCatalogBridgeTechdocUrlReader {
|
|
|
83
92
|
bridgePredicate = (url) => {
|
|
84
93
|
for (let index = 0; index < this.bridgeConfigs.length; index++) {
|
|
85
94
|
const bc = this.bridgeConfigs[index];
|
|
86
|
-
|
|
87
|
-
if (url.hostname === bcUrl.hostname && url.port === bcUrl.port && url.pathname.startsWith("/modelcard")) {
|
|
95
|
+
if (url.pathname.includes("modelcard") && url.pathname.includes(bc.id)) {
|
|
88
96
|
return true;
|
|
89
97
|
}
|
|
90
98
|
}
|
|
91
|
-
if (url.hostname === "localhost" && url.port === "9090" && url.pathname.startsWith("/modelcard")) {
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
94
99
|
return false;
|
|
95
100
|
};
|
|
96
101
|
async readUrl(url, options) {
|
|
97
102
|
this.logger.info(`ModelCatalogBridgeTechdocUrlReader.readUrl of ${url}`);
|
|
98
103
|
let response;
|
|
99
104
|
try {
|
|
105
|
+
const token = await ModeCatalogBridgeTechdocUrlReader.auth.getPluginRequestToken({
|
|
106
|
+
onBehalfOf: await ModeCatalogBridgeTechdocUrlReader.auth.getOwnServiceCredentials(),
|
|
107
|
+
targetPluginId: urlReader.urlReaderFactoriesServiceRef.id
|
|
108
|
+
});
|
|
109
|
+
let tok = token.token;
|
|
110
|
+
if (process.env.RHDH_TOKEN && process.env.RHDH_TOKEN.length > 0) {
|
|
111
|
+
tok = process.env.RHDH_TOKEN;
|
|
112
|
+
}
|
|
100
113
|
response = await fetch(url, {
|
|
114
|
+
method: "GET",
|
|
115
|
+
headers: {
|
|
116
|
+
Authorization: `Bearer ${tok}`
|
|
117
|
+
},
|
|
101
118
|
signal: options?.signal
|
|
102
119
|
});
|
|
103
120
|
} catch (e) {
|
|
@@ -148,10 +165,14 @@ class ModeCatalogBridgeTechdocUrlReader {
|
|
|
148
165
|
const catalogTechdocUrlReaderPlugin = backendPluginApi.createServiceFactory({
|
|
149
166
|
service: urlReader.urlReaderFactoriesServiceRef,
|
|
150
167
|
deps: {
|
|
151
|
-
logger: backendPluginApi.coreServices.logger
|
|
168
|
+
logger: backendPluginApi.coreServices.logger,
|
|
169
|
+
discovery: backendPluginApi.coreServices.discovery,
|
|
170
|
+
auth: backendPluginApi.coreServices.auth
|
|
152
171
|
},
|
|
153
|
-
async factory({ logger }) {
|
|
172
|
+
async factory({ logger, discovery, auth }) {
|
|
154
173
|
logger.child({ source: 'catalogTechdocUrlReaderPlugin"' }).info("Registering the model catalog bridge URL reader ");
|
|
174
|
+
ModeCatalogBridgeTechdocUrlReader.discovery = discovery;
|
|
175
|
+
ModeCatalogBridgeTechdocUrlReader.auth = auth;
|
|
155
176
|
return ModeCatalogBridgeTechdocUrlReader.factory;
|
|
156
177
|
}
|
|
157
178
|
});
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 ReaderFactory,\n ReadUrlResponseFactory,\n urlReaderFactoriesServiceRef,\n} from '@backstage/backend-defaults/urlReader';\nimport {\n coreServices,\n createServiceFactory,\n UrlReaderService,\n UrlReaderServiceReadUrlOptions,\n UrlReaderServiceSearchOptions,\n UrlReaderServiceSearchResponse,\n UrlReaderServiceReadUrlResponse,\n UrlReaderServiceReadTreeOptions,\n UrlReaderServiceReadTreeResponse,\n UrlReaderServiceReadTreeResponseFile,\n UrlReaderServiceReadTreeResponseDirOptions,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { NotFoundError, NotModifiedError } from '@backstage/errors';\nimport { Readable } from 'stream';\nimport fs from 'fs';\nimport platformPath from 'path';\nimport os from 'os';\n\nexport class ModelCatalogBridgeUrlReaderServiceReadTreeResponse\n implements UrlReaderServiceReadTreeResponse\n{\n constructor(\n private readonly workDir: string,\n public readonly etag: string,\n private readonly buffer: Promise<Buffer>,\n private readonly logger: LoggerService,\n ) {\n this.logger = logger.child({\n source: 'ModelCatalogBridgeUrlReaderServiceReadTreeResponse\"',\n });\n }\n async files(): Promise<UrlReaderServiceReadTreeResponseFile[]> {\n throw new Error(\n 'ModelCatalogBridgeUrlReaderServiceReadTreeResponse does not implement files',\n );\n }\n async archive(): Promise<Readable> {\n throw new Error(\n 'ModelCatalogBridgeUrlReaderServiceReadTreeResponse does not implement archive',\n );\n }\n\n async dir(\n options?: UrlReaderServiceReadTreeResponseDirOptions,\n ): Promise<string> {\n const dir =\n options?.targetDir ??\n (await fs.promises.mkdtemp(\n platformPath.join(this.workDir, 'backstage-'),\n ));\n\n const subDir = platformPath.join(dir, 'docs');\n const filePath = platformPath.join(subDir, 'index.md');\n try {\n fs.mkdirSync(subDir);\n const buf = await this.buffer;\n await fs.promises.writeFile(filePath, buf);\n } catch (error) {\n this.logger.error(`Error writing to file ${filePath}:`, error);\n }\n\n return dir;\n }\n}\n\nexport type BridgeConfig = {\n id: string;\n baseUrl: string;\n};\n\nexport function readBridgeConfigs(config: Config): BridgeConfig[] {\n const configs = config.getOptionalConfig('catalog.providers.modelCatalog');\n if (!configs) {\n return [];\n }\n return configs.keys().map(id => readBridgeConfig(id, configs.getConfig(id)));\n}\n\nexport function readBridgeConfig(id: string, config: Config): BridgeConfig {\n const url = config.getString('baseUrl');\n return { id, baseUrl: url };\n}\n\nexport class ModeCatalogBridgeTechdocUrlReader implements UrlReaderService {\n private readonly workDir: string;\n private readonly logger: LoggerService;\n private readonly bridgeConfigs: BridgeConfig[];\n\n static factory: ReaderFactory = ({ config, logger }) => {\n const reader = new ModeCatalogBridgeTechdocUrlReader(config, logger);\n const predicate = (url: URL) => reader.bridgePredicate(url);\n\n return [{ reader, predicate }];\n };\n\n constructor(config: Config, logger: LoggerService) {\n this.logger = logger.child({\n source: 'ModeCatalogBridgeTechdocUrlReader\"',\n });\n this.bridgeConfigs = readBridgeConfigs(config);\n this.workDir = os.tmpdir();\n\n const bkend = config.getOptionalString('backend.workingDirectory');\n if (bkend) {\n this.workDir = bkend;\n }\n }\n\n bridgePredicate = (url: URL): boolean => {\n for (let index = 0; index < this.bridgeConfigs.length; index++) {\n const bc = this.bridgeConfigs[index];\n const bcUrl = new URL(bc.baseUrl);\n if (\n url.hostname === bcUrl.hostname &&\n url.port === bcUrl.port &&\n url.pathname.startsWith('/modelcard')\n ) {\n return true;\n }\n }\n if (\n url.hostname === 'localhost' &&\n url.port === '9090' &&\n url.pathname.startsWith('/modelcard')\n ) {\n return true;\n }\n return false;\n };\n\n async readUrl(\n url: string,\n options?: UrlReaderServiceReadUrlOptions,\n ): Promise<UrlReaderServiceReadUrlResponse> {\n this.logger.info(`ModelCatalogBridgeTechdocUrlReader.readUrl of ${url}`);\n let response: Response;\n try {\n response = await fetch(url, {\n signal: options?.signal as any,\n });\n } catch (e) {\n throw new Error(`Unable to read ${url}, ${e}`);\n }\n\n if (response.ok) {\n return ReadUrlResponseFactory.fromResponse(response);\n }\n\n const message = `could not read ${url}, ${response.status} ${response.statusText}`;\n if (response.status === 404) {\n throw new NotFoundError(message);\n }\n if (response.status === 304) {\n throw new NotModifiedError();\n }\n throw new Error(message);\n }\n\n async search(\n url: string,\n options?: UrlReaderServiceSearchOptions,\n ): Promise<UrlReaderServiceSearchResponse> {\n this.logger.info(\n `ModelCatalogBridgeTechdocUrlReader.search of ${url} with options ${JSON.stringify(\n options,\n )}`,\n );\n\n throw new Error(\n 'ModeCatalogBridgeTechdocUrlReader does not implement search',\n );\n // const { pathname } = new URL(url);\n\n // if (pathname.match(/[*?]/)) {\n // throw new Error('Unsupported search pattern URL');\n // }\n\n // try {\n // const data = await this.readUrl(url, options);\n\n // return {\n // files: [\n // {\n // url: url,\n // content: data.buffer,\n // lastModifiedAt: data.lastModifiedAt,\n // },\n // ],\n // etag: data.etag ?? '',\n // };\n // } catch (error) {\n // assertError(error);\n // if (error.name === 'NotFoundError') {\n // return {\n // files: [],\n // etag: '',\n // };\n // }\n // throw error;\n // }\n }\n\n async readTree(\n url: string,\n options?: UrlReaderServiceReadTreeOptions,\n ): Promise<UrlReaderServiceReadTreeResponse> {\n this.logger.info(`ModelCatalogBridgeTechdocUrlReader.readTree of ${url}`);\n const data = await this.readUrl(url, options);\n\n const et = options?.etag;\n if (!et) {\n return await new ModelCatalogBridgeUrlReaderServiceReadTreeResponse(\n this.workDir,\n '',\n data.buffer(),\n this.logger,\n );\n }\n return await new ModelCatalogBridgeUrlReaderServiceReadTreeResponse(\n this.workDir,\n et,\n data.buffer(),\n this.logger,\n );\n }\n}\n\n/**\n * catalogTechdocUrlReaderPlugin define the factory for the ModelCatalogBridgeUrlReader service\n *\n * @public\n */\nexport const catalogTechdocUrlReaderPlugin = createServiceFactory({\n service: urlReaderFactoriesServiceRef,\n deps: {\n logger: coreServices.logger,\n },\n async factory({ logger }) {\n logger\n .child({ source: 'catalogTechdocUrlReaderPlugin\"' })\n .info('Registering the model catalog bridge URL reader ');\n\n return ModeCatalogBridgeTechdocUrlReader.factory;\n },\n});\n"],"names":["fs","platformPath","os","ReadUrlResponseFactory","NotFoundError","NotModifiedError","createServiceFactory","urlReaderFactoriesServiceRef","coreServices"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright Red Hat, Inc.\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 ReaderFactory,\n ReadUrlResponseFactory,\n urlReaderFactoriesServiceRef,\n} from '@backstage/backend-defaults/urlReader';\nimport {\n coreServices,\n createServiceFactory,\n UrlReaderService,\n UrlReaderServiceReadUrlOptions,\n UrlReaderServiceSearchOptions,\n UrlReaderServiceSearchResponse,\n UrlReaderServiceReadUrlResponse,\n UrlReaderServiceReadTreeOptions,\n UrlReaderServiceReadTreeResponse,\n UrlReaderServiceReadTreeResponseFile,\n UrlReaderServiceReadTreeResponseDirOptions,\n LoggerService,\n DiscoveryService,\n AuthService,\n} from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { NotFoundError, NotModifiedError } from '@backstage/errors';\nimport { Readable } from 'stream';\nimport fs from 'fs';\nimport platformPath from 'path';\nimport os from 'os';\n\nexport class ModelCatalogBridgeUrlReaderServiceReadTreeResponse\n implements UrlReaderServiceReadTreeResponse\n{\n constructor(\n private readonly workDir: string,\n public readonly etag: string,\n private readonly buffer: Promise<Buffer>,\n private readonly logger: LoggerService,\n ) {\n this.logger = logger.child({\n source: 'ModelCatalogBridgeUrlReaderServiceReadTreeResponse\"',\n });\n }\n async files(): Promise<UrlReaderServiceReadTreeResponseFile[]> {\n throw new Error(\n 'ModelCatalogBridgeUrlReaderServiceReadTreeResponse does not implement files',\n );\n }\n async archive(): Promise<Readable> {\n throw new Error(\n 'ModelCatalogBridgeUrlReaderServiceReadTreeResponse does not implement archive',\n );\n }\n\n async dir(\n options?: UrlReaderServiceReadTreeResponseDirOptions,\n ): Promise<string> {\n const dir =\n options?.targetDir ??\n (await fs.promises.mkdtemp(\n platformPath.join(this.workDir, 'backstage-'),\n ));\n\n const subDir = platformPath.join(dir, 'docs');\n const filePath = platformPath.join(subDir, 'index.md');\n try {\n fs.mkdirSync(subDir);\n const buf = await this.buffer;\n await fs.promises.writeFile(filePath, buf);\n } catch (error) {\n this.logger.error(`Error writing to file ${filePath}:`, error);\n }\n\n return dir;\n }\n}\n\nexport type BridgeConfig = {\n id: string;\n baseUrl: string;\n};\n\nexport function readBridgeConfigs(config: Config): BridgeConfig[] {\n const configs = config.getOptionalConfig('catalog.providers.modelCatalog');\n if (!configs) {\n return [];\n }\n return configs.keys().map(id => readBridgeConfig(id, configs.getConfig(id)));\n}\n\nexport function readBridgeConfig(id: string, config: Config): BridgeConfig {\n let url = '';\n if (config.has('baseUrl')) {\n url = config.getString('baseUrl');\n }\n return { id, baseUrl: url };\n}\n\nexport class ModeCatalogBridgeTechdocUrlReader implements UrlReaderService {\n private readonly workDir: string;\n private readonly logger: LoggerService;\n private readonly bridgeConfigs: BridgeConfig[];\n\n // TODO we are limited in which core services can be passed into the ReaderFactory, so as a work around we\n // define these globals that are set in the plugin init method; if we merge this plugin with the\n // kserve-kubeflow-connector plugins, and bypass the need for plugin to plug REST calls, we can\n // bypass the need for the discover and auth services.\n static discovery: DiscoveryService;\n static auth: AuthService;\n\n static factory: ReaderFactory = ({ config, logger }) => {\n const reader = new ModeCatalogBridgeTechdocUrlReader(config, logger);\n const predicate = (url: URL) => reader.bridgePredicate(url);\n\n return [{ reader, predicate }];\n };\n\n constructor(config: Config, logger: LoggerService) {\n this.logger = logger.child({\n source: 'ModeCatalogBridgeTechdocUrlReader\"',\n });\n this.bridgeConfigs = readBridgeConfigs(config);\n this.workDir = os.tmpdir();\n\n const bkend = config.getOptionalString('backend.workingDirectory');\n if (bkend) {\n this.workDir = bkend;\n }\n }\n\n bridgePredicate = (url: URL): boolean => {\n for (let index = 0; index < this.bridgeConfigs.length; index++) {\n const bc = this.bridgeConfigs[index];\n if (url.pathname.includes('modelcard') && url.pathname.includes(bc.id)) {\n return true;\n }\n }\n return false;\n };\n\n async readUrl(\n url: string,\n options?: UrlReaderServiceReadUrlOptions,\n ): Promise<UrlReaderServiceReadUrlResponse> {\n this.logger.info(`ModelCatalogBridgeTechdocUrlReader.readUrl of ${url}`);\n let response: Response;\n try {\n // TODO so the token obtained here looks like a toke, but it still got\n // 401 / unauthorized when trying to access the router REST endpoints\n // of the kserve-kubeflow-connector; hence, are using of the static\n // admin token 'RHDH_TOKEN' that immediately follows; *IF* we determin\n // that the model card integration works with the upstream version\n // of kubeflow model catalog/registry, we'll need to dive into possible\n // alternatives to this work around. Possibly merging the techdoc\n // reader extension into the kserve-kubeflow-connector and byppassing\n // the need for the plugin to plugin REST call might be required here.\n const token =\n await ModeCatalogBridgeTechdocUrlReader.auth.getPluginRequestToken({\n onBehalfOf:\n await ModeCatalogBridgeTechdocUrlReader.auth.getOwnServiceCredentials(),\n targetPluginId: urlReaderFactoriesServiceRef.id,\n });\n let tok = token.token;\n if (process.env.RHDH_TOKEN && process.env.RHDH_TOKEN.length > 0) {\n tok = process.env.RHDH_TOKEN;\n }\n response = await fetch(url, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${tok}`,\n },\n signal: options?.signal as any,\n });\n } catch (e) {\n throw new Error(`Unable to read ${url}, ${e}`);\n }\n\n if (response.ok) {\n return ReadUrlResponseFactory.fromResponse(response);\n }\n\n const message = `could not read ${url}, ${response.status} ${response.statusText}`;\n if (response.status === 404) {\n throw new NotFoundError(message);\n }\n if (response.status === 304) {\n throw new NotModifiedError();\n }\n throw new Error(message);\n }\n\n async search(\n url: string,\n options?: UrlReaderServiceSearchOptions,\n ): Promise<UrlReaderServiceSearchResponse> {\n this.logger.info(\n `ModelCatalogBridgeTechdocUrlReader.search of ${url} with options ${JSON.stringify(\n options,\n )}`,\n );\n\n throw new Error(\n 'ModeCatalogBridgeTechdocUrlReader does not implement search',\n );\n // const { pathname } = new URL(url);\n\n // if (pathname.match(/[*?]/)) {\n // throw new Error('Unsupported search pattern URL');\n // }\n\n // try {\n // const data = await this.readUrl(url, options);\n\n // return {\n // files: [\n // {\n // url: url,\n // content: data.buffer,\n // lastModifiedAt: data.lastModifiedAt,\n // },\n // ],\n // etag: data.etag ?? '',\n // };\n // } catch (error) {\n // assertError(error);\n // if (error.name === 'NotFoundError') {\n // return {\n // files: [],\n // etag: '',\n // };\n // }\n // throw error;\n // }\n }\n\n async readTree(\n url: string,\n options?: UrlReaderServiceReadTreeOptions,\n ): Promise<UrlReaderServiceReadTreeResponse> {\n this.logger.info(`ModelCatalogBridgeTechdocUrlReader.readTree of ${url}`);\n const data = await this.readUrl(url, options);\n\n const et = options?.etag;\n if (!et) {\n return await new ModelCatalogBridgeUrlReaderServiceReadTreeResponse(\n this.workDir,\n '',\n data.buffer(),\n this.logger,\n );\n }\n return await new ModelCatalogBridgeUrlReaderServiceReadTreeResponse(\n this.workDir,\n et,\n data.buffer(),\n this.logger,\n );\n }\n}\n\n/**\n * catalogTechdocUrlReaderPlugin define the factory for the ModelCatalogBridgeUrlReader service\n *\n * @public\n */\nexport const catalogTechdocUrlReaderPlugin = createServiceFactory({\n service: urlReaderFactoriesServiceRef,\n deps: {\n logger: coreServices.logger,\n discovery: coreServices.discovery,\n auth: coreServices.auth,\n },\n async factory({ logger, discovery, auth }) {\n logger\n .child({ source: 'catalogTechdocUrlReaderPlugin\"' })\n .info('Registering the model catalog bridge URL reader ');\n\n ModeCatalogBridgeTechdocUrlReader.discovery = discovery;\n ModeCatalogBridgeTechdocUrlReader.auth = auth;\n\n return ModeCatalogBridgeTechdocUrlReader.factory;\n },\n});\n"],"names":["fs","platformPath","os","urlReaderFactoriesServiceRef","ReadUrlResponseFactory","NotFoundError","NotModifiedError","createServiceFactory","coreServices"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@red-hat-developer-hub/backstage-plugin-catalog-techdoc-url-reader-backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -34,19 +34,19 @@
|
|
|
34
34
|
"postpack": "backstage-cli package postpack"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@backstage/backend-defaults": "^0.
|
|
38
|
-
"@backstage/backend-plugin-api": "^1.
|
|
39
|
-
"@backstage/catalog-client": "^1.
|
|
40
|
-
"@backstage/config": "^1.3.
|
|
41
|
-
"@backstage/errors": "^1.
|
|
42
|
-
"@backstage/plugin-catalog-node": "^
|
|
37
|
+
"@backstage/backend-defaults": "^0.17.3",
|
|
38
|
+
"@backstage/backend-plugin-api": "^1.9.2",
|
|
39
|
+
"@backstage/catalog-client": "^1.16.0",
|
|
40
|
+
"@backstage/config": "^1.3.8",
|
|
41
|
+
"@backstage/errors": "^1.3.1",
|
|
42
|
+
"@backstage/plugin-catalog-node": "^2.2.2",
|
|
43
43
|
"express": "^4.17.1",
|
|
44
44
|
"express-promise-router": "^4.1.0",
|
|
45
45
|
"zod": "^3.22.4"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@backstage/backend-test-utils": "^1.
|
|
49
|
-
"@backstage/cli": "^0.
|
|
48
|
+
"@backstage/backend-test-utils": "^1.11.4",
|
|
49
|
+
"@backstage/cli": "^0.36.3",
|
|
50
50
|
"@types/express": "^4.17.6",
|
|
51
51
|
"@types/supertest": "^2.0.12",
|
|
52
52
|
"supertest": "^6.2.4",
|