@red-hat-developer-hub/backstage-plugin-catalog-techdoc-url-reader-backend 0.5.0 → 0.6.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,36 @@
1
1
  # @red-hat-developer-hub/backstage-plugin-catalog-techdoc-url-reader-backend
2
2
 
3
+ ## 0.6.1
4
+
5
+ ### Patch Changes
6
+
7
+ - f7984e7: add remaining unit tests needed for feature readiness
8
+
9
+ ## 0.6.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 56c40be: Integrate Backstage discovery and auth services for connector communication.
14
+
15
+ The model-catalog entity provider now resolves the connector base URL via the
16
+ discovery service and authenticates with backend-to-backend service tokens
17
+ instead of unauthenticated direct HTTP calls. The `fromConfig` factory method
18
+ accepts `discovery` and `auth` as additional required dependencies.
19
+
20
+ The techdoc URL reader adds bearer-token authentication when fetching model
21
+ cards and replaces hostname-based bridge URL matching with path-based matching
22
+ that uses the connector plugin ID.
23
+
24
+ ### Patch Changes
25
+
26
+ - c2e3749: bump workspace to 1.52.0
27
+
28
+ ## 0.5.1
29
+
30
+ ### Patch Changes
31
+
32
+ - 4418084: Upgrade workspace to Backstage 1.49.4
33
+
3
34
  ## 0.5.0
4
35
 
5
36
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
3
2
  import { ReaderFactory } from '@backstage/backend-defaults/urlReader';
4
3
 
@@ -37,6 +37,13 @@ class ModelCatalogBridgeUrlReaderServiceReadTreeResponse {
37
37
  const dir = options?.targetDir ?? await fs__default.default.promises.mkdtemp(
38
38
  platformPath__default.default.join(this.workDir, "backstage-")
39
39
  );
40
+ const mkdocsPath = platformPath__default.default.join(dir, "mkdocs.yml");
41
+ const mkdocsContent = "site_name: Model Card\nnav:\n - Home: index.md\n";
42
+ try {
43
+ await fs__default.default.promises.writeFile(mkdocsPath, mkdocsContent);
44
+ } catch (error) {
45
+ this.logger.error(`Error writing mkdocs.yml to ${mkdocsPath}:`, error);
46
+ }
40
47
  const subDir = platformPath__default.default.join(dir, "docs");
41
48
  const filePath = platformPath__default.default.join(subDir, "index.md");
42
49
  try {
@@ -54,20 +61,53 @@ function readBridgeConfigs(config) {
54
61
  if (!configs) {
55
62
  return [];
56
63
  }
57
- return configs.keys().map((id) => readBridgeConfig(id, configs.getConfig(id)));
64
+ const result = [];
65
+ for (const connectorId of configs.keys()) {
66
+ const connectorConfig = configs.getConfig(connectorId);
67
+ for (const clusterKey of connectorConfig.keys()) {
68
+ const clusterConfig = connectorConfig.getOptionalConfig(clusterKey);
69
+ if (!clusterConfig) {
70
+ continue;
71
+ }
72
+ if (clusterConfig.has("frequency") || clusterConfig.has("timeout")) {
73
+ continue;
74
+ }
75
+ result.push(readBridgeConfig(connectorId, clusterConfig));
76
+ }
77
+ }
78
+ return result;
79
+ }
80
+ function safeGetOptionalString(config, key) {
81
+ try {
82
+ return config.getOptionalString(key) ?? "";
83
+ } catch {
84
+ return "";
85
+ }
58
86
  }
59
87
  function readBridgeConfig(id, config) {
60
- const url = config.getString("baseUrl");
61
- return { id, baseUrl: url };
88
+ return {
89
+ id,
90
+ name: safeGetOptionalString(config, "name"),
91
+ kubeflowModelCatalogUrl: safeGetOptionalString(
92
+ config,
93
+ "kubeflow-model-catalog-url"
94
+ ),
95
+ defaultOwner: safeGetOptionalString(config, "default-owner"),
96
+ defaultLifecycle: safeGetOptionalString(config, "default-lifecycle")
97
+ };
62
98
  }
63
99
  class ModeCatalogBridgeTechdocUrlReader {
64
100
  workDir;
65
101
  logger;
66
102
  bridgeConfigs;
103
+ // TODO we are limited in which core services can be passed into the ReaderFactory, so as a work around we
104
+ // define these globals that are set in the plugin init method; if we merge this plugin with the
105
+ // kserve-kubeflow-connector plugins, and bypass the need for plugin to plugin REST calls, we can
106
+ // bypass the need for the auth service.
107
+ static auth;
67
108
  static factory = ({ config, logger }) => {
68
109
  const reader = new ModeCatalogBridgeTechdocUrlReader(config, logger);
69
- const predicate = (url) => reader.bridgePredicate(url);
70
- return [{ reader, predicate }];
110
+ return [{ reader, predicate: reader.bridgePredicate }];
71
111
  };
72
112
  constructor(config, logger) {
73
113
  this.logger = logger.child({
@@ -80,24 +120,38 @@ class ModeCatalogBridgeTechdocUrlReader {
80
120
  this.workDir = bkend;
81
121
  }
82
122
  }
83
- bridgePredicate = (url) => {
123
+ matchingBridgeConfig(url) {
84
124
  for (let index = 0; index < this.bridgeConfigs.length; index++) {
85
125
  const bc = this.bridgeConfigs[index];
86
- const bcUrl = new URL(bc.baseUrl);
87
- if (url.hostname === bcUrl.hostname && url.port === bcUrl.port && url.pathname.startsWith("/modelcard")) {
88
- return true;
126
+ if (url.pathname.includes("modelcard") && url.pathname.includes(bc.id)) {
127
+ return bc;
89
128
  }
90
129
  }
91
- if (url.hostname === "localhost" && url.port === "9090" && url.pathname.startsWith("/modelcard")) {
92
- return true;
93
- }
94
- return false;
130
+ return void 0;
131
+ }
132
+ bridgePredicate = (url) => {
133
+ return this.matchingBridgeConfig(url) !== void 0;
95
134
  };
96
135
  async readUrl(url, options) {
97
136
  this.logger.info(`ModelCatalogBridgeTechdocUrlReader.readUrl of ${url}`);
137
+ const parsedUrl = new URL(url);
138
+ const bc = this.matchingBridgeConfig(parsedUrl);
139
+ if (!bc) {
140
+ throw new Error(`No matching bridge config for ${url}`);
141
+ }
98
142
  let response;
99
143
  try {
144
+ const token = await ModeCatalogBridgeTechdocUrlReader.auth.getPluginRequestToken({
145
+ onBehalfOf: await ModeCatalogBridgeTechdocUrlReader.auth.getOwnServiceCredentials(),
146
+ targetPluginId: bc.id
147
+ });
148
+ const tok = token.token;
149
+ this.logger.info(`Using service-to-service token for ${url}`);
100
150
  response = await fetch(url, {
151
+ method: "GET",
152
+ headers: {
153
+ Authorization: `Bearer ${tok}`
154
+ },
101
155
  signal: options?.signal
102
156
  });
103
157
  } catch (e) {
@@ -148,10 +202,12 @@ class ModeCatalogBridgeTechdocUrlReader {
148
202
  const catalogTechdocUrlReaderPlugin = backendPluginApi.createServiceFactory({
149
203
  service: urlReader.urlReaderFactoriesServiceRef,
150
204
  deps: {
151
- logger: backendPluginApi.coreServices.logger
205
+ logger: backendPluginApi.coreServices.logger,
206
+ auth: backendPluginApi.coreServices.auth
152
207
  },
153
- async factory({ logger }) {
208
+ async factory({ logger, auth }) {
154
209
  logger.child({ source: 'catalogTechdocUrlReaderPlugin"' }).info("Registering the model catalog bridge URL reader ");
210
+ ModeCatalogBridgeTechdocUrlReader.auth = auth;
155
211
  return ModeCatalogBridgeTechdocUrlReader.factory;
156
212
  }
157
213
  });
@@ -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 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 // Write a minimal mkdocs.yml that the TechDocs builder (mkdocs)\n // requires at the project root.\n const mkdocsPath = platformPath.join(dir, 'mkdocs.yml');\n const mkdocsContent = 'site_name: Model Card\\nnav:\\n - Home: index.md\\n';\n try {\n await fs.promises.writeFile(mkdocsPath, mkdocsContent);\n } catch (error) {\n this.logger.error(`Error writing mkdocs.yml to ${mkdocsPath}:`, error);\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 name: string;\n kubeflowModelCatalogUrl: string;\n defaultOwner: string;\n defaultLifecycle: string;\n};\n\nexport function readBridgeConfigs(config: Config): BridgeConfig[] {\n const configs = config.getOptionalConfig('catalog.providers.modelCatalog');\n if (!configs) {\n return [];\n }\n const result: BridgeConfig[] = [];\n for (const connectorId of configs.keys()) {\n const connectorConfig = configs.getConfig(connectorId);\n for (const clusterKey of connectorConfig.keys()) {\n const clusterConfig = connectorConfig.getOptionalConfig(clusterKey);\n if (!clusterConfig) {\n continue;\n }\n if (clusterConfig.has('frequency') || clusterConfig.has('timeout')) {\n continue;\n }\n result.push(readBridgeConfig(connectorId, clusterConfig));\n }\n }\n return result;\n}\n\nfunction safeGetOptionalString(config: Config, key: string): string {\n try {\n return config.getOptionalString(key) ?? '';\n } catch {\n // Backstage ConfigReader throws TypeError for empty-string values\n // (e.g. from env var substitution like ${VAR:-}).\n return '';\n }\n}\n\nexport function readBridgeConfig(id: string, config: Config): BridgeConfig {\n return {\n id,\n name: safeGetOptionalString(config, 'name'),\n kubeflowModelCatalogUrl: safeGetOptionalString(\n config,\n 'kubeflow-model-catalog-url',\n ),\n defaultOwner: safeGetOptionalString(config, 'default-owner'),\n defaultLifecycle: safeGetOptionalString(config, 'default-lifecycle'),\n };\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 plugin REST calls, we can\n // bypass the need for the auth service.\n static auth: AuthService;\n\n static factory: ReaderFactory = ({ config, logger }) => {\n const reader = new ModeCatalogBridgeTechdocUrlReader(config, logger);\n return [{ reader, predicate: reader.bridgePredicate }];\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 private matchingBridgeConfig(url: URL): BridgeConfig | undefined {\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 bc;\n }\n }\n return undefined;\n }\n\n bridgePredicate = (url: URL): boolean => {\n return this.matchingBridgeConfig(url) !== undefined;\n };\n\n async readUrl(\n url: string,\n options?: UrlReaderServiceReadUrlOptions,\n ): Promise<UrlReaderServiceReadUrlResponse> {\n this.logger.info(`ModelCatalogBridgeTechdocUrlReader.readUrl of ${url}`);\n const parsedUrl = new URL(url);\n const bc = this.matchingBridgeConfig(parsedUrl);\n if (!bc) {\n throw new Error(`No matching bridge config for ${url}`);\n }\n let response: Response;\n try {\n const token =\n await ModeCatalogBridgeTechdocUrlReader.auth.getPluginRequestToken({\n onBehalfOf:\n await ModeCatalogBridgeTechdocUrlReader.auth.getOwnServiceCredentials(),\n targetPluginId: bc.id,\n });\n const tok = token.token;\n this.logger.info(`Using service-to-service token for ${url}`);\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 auth: coreServices.auth,\n },\n async factory({ logger, auth }) {\n logger\n .child({ source: 'catalogTechdocUrlReaderPlugin\"' })\n .info('Registering the model catalog bridge URL reader ');\n\n ModeCatalogBridgeTechdocUrlReader.auth = auth;\n\n return ModeCatalogBridgeTechdocUrlReader.factory;\n },\n});\n"],"names":["fs","platformPath","os","ReadUrlResponseFactory","NotFoundError","NotModifiedError","createServiceFactory","urlReaderFactoriesServiceRef","coreServices"],"mappings":";;;;;;;;;;;;;;;AA2CO,MAAM,kDAAA,CAEb;AAAA,EACE,WAAA,CACmB,OAAA,EACD,IAAA,EACC,MAAA,EACA,MAAA,EACjB;AAJiB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AACD,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AACC,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAEjB,IAAA,IAAA,CAAK,MAAA,GAAS,OAAO,KAAA,CAAM;AAAA,MACzB,MAAA,EAAQ;AAAA,KACT,CAAA;AAAA,EACH;AAAA,EACA,MAAM,KAAA,GAAyD;AAC7D,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAAA,EACA,MAAM,OAAA,GAA6B;AACjC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAAA,EAEA,MAAM,IACJ,OAAA,EACiB;AACjB,IAAA,MAAM,GAAA,GACJ,OAAA,EAAS,SAAA,IACR,MAAMA,oBAAG,QAAA,CAAS,OAAA;AAAA,MACjBC,6BAAA,CAAa,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,YAAY;AAAA,KAC9C;AAIF,IAAA,MAAM,UAAA,GAAaA,6BAAA,CAAa,IAAA,CAAK,GAAA,EAAK,YAAY,CAAA;AACtD,IAAA,MAAM,aAAA,GAAgB,mDAAA;AACtB,IAAA,IAAI;AACF,MAAA,MAAMD,mBAAA,CAAG,QAAA,CAAS,SAAA,CAAU,UAAA,EAAY,aAAa,CAAA;AAAA,IACvD,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,4BAAA,EAA+B,UAAU,KAAK,KAAK,CAAA;AAAA,IACvE;AAEA,IAAA,MAAM,MAAA,GAASC,6BAAA,CAAa,IAAA,CAAK,GAAA,EAAK,MAAM,CAAA;AAC5C,IAAA,MAAM,QAAA,GAAWA,6BAAA,CAAa,IAAA,CAAK,MAAA,EAAQ,UAAU,CAAA;AACrD,IAAA,IAAI;AACF,MAAAD,mBAAA,CAAG,UAAU,MAAM,CAAA;AACnB,MAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,MAAA;AACvB,MAAA,MAAMA,mBAAA,CAAG,QAAA,CAAS,SAAA,CAAU,QAAA,EAAU,GAAG,CAAA;AAAA,IAC3C,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,sBAAA,EAAyB,QAAQ,KAAK,KAAK,CAAA;AAAA,IAC/D;AAEA,IAAA,OAAO,GAAA;AAAA,EACT;AACF;AAUO,SAAS,kBAAkB,MAAA,EAAgC;AAChE,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,iBAAA,CAAkB,gCAAgC,CAAA;AACzE,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,OAAO,EAAC;AAAA,EACV;AACA,EAAA,MAAM,SAAyB,EAAC;AAChC,EAAA,KAAA,MAAW,WAAA,IAAe,OAAA,CAAQ,IAAA,EAAK,EAAG;AACxC,IAAA,MAAM,eAAA,GAAkB,OAAA,CAAQ,SAAA,CAAU,WAAW,CAAA;AACrD,IAAA,KAAA,MAAW,UAAA,IAAc,eAAA,CAAgB,IAAA,EAAK,EAAG;AAC/C,MAAA,MAAM,aAAA,GAAgB,eAAA,CAAgB,iBAAA,CAAkB,UAAU,CAAA;AAClE,MAAA,IAAI,CAAC,aAAA,EAAe;AAClB,QAAA;AAAA,MACF;AACA,MAAA,IAAI,cAAc,GAAA,CAAI,WAAW,KAAK,aAAA,CAAc,GAAA,CAAI,SAAS,CAAA,EAAG;AAClE,QAAA;AAAA,MACF;AACA,MAAA,MAAA,CAAO,IAAA,CAAK,gBAAA,CAAiB,WAAA,EAAa,aAAa,CAAC,CAAA;AAAA,IAC1D;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,qBAAA,CAAsB,QAAgB,GAAA,EAAqB;AAClE,EAAA,IAAI;AACF,IAAA,OAAO,MAAA,CAAO,iBAAA,CAAkB,GAAG,CAAA,IAAK,EAAA;AAAA,EAC1C,CAAA,CAAA,MAAQ;AAGN,IAAA,OAAO,EAAA;AAAA,EACT;AACF;AAEO,SAAS,gBAAA,CAAiB,IAAY,MAAA,EAA8B;AACzE,EAAA,OAAO;AAAA,IACL,EAAA;AAAA,IACA,IAAA,EAAM,qBAAA,CAAsB,MAAA,EAAQ,MAAM,CAAA;AAAA,IAC1C,uBAAA,EAAyB,qBAAA;AAAA,MACvB,MAAA;AAAA,MACA;AAAA,KACF;AAAA,IACA,YAAA,EAAc,qBAAA,CAAsB,MAAA,EAAQ,eAAe,CAAA;AAAA,IAC3D,gBAAA,EAAkB,qBAAA,CAAsB,MAAA,EAAQ,mBAAmB;AAAA,GACrE;AACF;AAEO,MAAM,iCAAA,CAA8D;AAAA,EACxD,OAAA;AAAA,EACA,MAAA;AAAA,EACA,aAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMjB,OAAO,IAAA;AAAA,EAEP,OAAO,OAAA,GAAyB,CAAC,EAAE,MAAA,EAAQ,QAAO,KAAM;AACtD,IAAA,MAAM,MAAA,GAAS,IAAI,iCAAA,CAAkC,MAAA,EAAQ,MAAM,CAAA;AACnE,IAAA,OAAO,CAAC,EAAE,MAAA,EAAQ,SAAA,EAAW,MAAA,CAAO,iBAAiB,CAAA;AAAA,EACvD,CAAA;AAAA,EAEA,WAAA,CAAY,QAAgB,MAAA,EAAuB;AACjD,IAAA,IAAA,CAAK,MAAA,GAAS,OAAO,KAAA,CAAM;AAAA,MACzB,MAAA,EAAQ;AAAA,KACT,CAAA;AACD,IAAA,IAAA,CAAK,aAAA,GAAgB,kBAAkB,MAAM,CAAA;AAC7C,IAAA,IAAA,CAAK,OAAA,GAAUE,oBAAG,MAAA,EAAO;AAEzB,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,iBAAA,CAAkB,0BAA0B,CAAA;AACjE,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,IAAA,CAAK,OAAA,GAAU,KAAA;AAAA,IACjB;AAAA,EACF;AAAA,EAEQ,qBAAqB,GAAA,EAAoC;AAC/D,IAAA,KAAA,IAAS,QAAQ,CAAA,EAAG,KAAA,GAAQ,IAAA,CAAK,aAAA,CAAc,QAAQ,KAAA,EAAA,EAAS;AAC9D,MAAA,MAAM,EAAA,GAAK,IAAA,CAAK,aAAA,CAAc,KAAK,CAAA;AACnC,MAAA,IAAI,GAAA,CAAI,QAAA,CAAS,QAAA,CAAS,WAAW,CAAA,IAAK,IAAI,QAAA,CAAS,QAAA,CAAS,EAAA,CAAG,EAAE,CAAA,EAAG;AACtE,QAAA,OAAO,EAAA;AAAA,MACT;AAAA,IACF;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EAEA,eAAA,GAAkB,CAAC,GAAA,KAAsB;AACvC,IAAA,OAAO,IAAA,CAAK,oBAAA,CAAqB,GAAG,CAAA,KAAM,MAAA;AAAA,EAC5C,CAAA;AAAA,EAEA,MAAM,OAAA,CACJ,GAAA,EACA,OAAA,EAC0C;AAC1C,IAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,8CAAA,EAAiD,GAAG,CAAA,CAAE,CAAA;AACvE,IAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,GAAG,CAAA;AAC7B,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,oBAAA,CAAqB,SAAS,CAAA;AAC9C,IAAA,IAAI,CAAC,EAAA,EAAI;AACP,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,8BAAA,EAAiC,GAAG,CAAA,CAAE,CAAA;AAAA,IACxD;AACA,IAAA,IAAI,QAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAM,KAAA,GACJ,MAAM,iCAAA,CAAkC,IAAA,CAAK,qBAAA,CAAsB;AAAA,QACjE,UAAA,EACE,MAAM,iCAAA,CAAkC,IAAA,CAAK,wBAAA,EAAyB;AAAA,QACxE,gBAAgB,EAAA,CAAG;AAAA,OACpB,CAAA;AACH,MAAA,MAAM,MAAM,KAAA,CAAM,KAAA;AAClB,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,mCAAA,EAAsC,GAAG,CAAA,CAAE,CAAA;AAC5D,MAAA,QAAA,GAAW,MAAM,MAAM,GAAA,EAAK;AAAA,QAC1B,MAAA,EAAQ,KAAA;AAAA,QACR,OAAA,EAAS;AAAA,UACP,aAAA,EAAe,UAAU,GAAG,CAAA;AAAA,SAC9B;AAAA,QACA,QAAQ,OAAA,EAAS;AAAA,OAClB,CAAA;AAAA,IACH,SAAS,CAAA,EAAG;AACV,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,eAAA,EAAkB,GAAG,CAAA,EAAA,EAAK,CAAC,CAAA,CAAE,CAAA;AAAA,IAC/C;AAEA,IAAA,IAAI,SAAS,EAAA,EAAI;AACf,MAAA,OAAOC,gCAAA,CAAuB,aAAa,QAAQ,CAAA;AAAA,IACrD;AAEA,IAAA,MAAM,OAAA,GAAU,kBAAkB,GAAG,CAAA,EAAA,EAAK,SAAS,MAAM,CAAA,CAAA,EAAI,SAAS,UAAU,CAAA,CAAA;AAChF,IAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,MAAA,MAAM,IAAIC,qBAAc,OAAO,CAAA;AAAA,IACjC;AACA,IAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,MAAA,MAAM,IAAIC,uBAAA,EAAiB;AAAA,IAC7B;AACA,IAAA,MAAM,IAAI,MAAM,OAAO,CAAA;AAAA,EACzB;AAAA,EAEA,MAAM,MAAA,CACJ,GAAA,EACA,OAAA,EACyC;AACzC,IAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MACV,CAAA,6CAAA,EAAgD,GAAG,CAAA,cAAA,EAAiB,IAAA,CAAK,SAAA;AAAA,QACvE;AAAA,OACD,CAAA;AAAA,KACH;AAEA,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EA8BF;AAAA,EAEA,MAAM,QAAA,CACJ,GAAA,EACA,OAAA,EAC2C;AAC3C,IAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,CAAA,+CAAA,EAAkD,GAAG,CAAA,CAAE,CAAA;AACxE,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,CAAQ,KAAK,OAAO,CAAA;AAE5C,IAAA,MAAM,KAAK,OAAA,EAAS,IAAA;AACpB,IAAA,IAAI,CAAC,EAAA,EAAI;AACP,MAAA,OAAO,MAAM,IAAI,kDAAA;AAAA,QACf,IAAA,CAAK,OAAA;AAAA,QACL,EAAA;AAAA,QACA,KAAK,MAAA,EAAO;AAAA,QACZ,IAAA,CAAK;AAAA,OACP;AAAA,IACF;AACA,IAAA,OAAO,MAAM,IAAI,kDAAA;AAAA,MACf,IAAA,CAAK,OAAA;AAAA,MACL,EAAA;AAAA,MACA,KAAK,MAAA,EAAO;AAAA,MACZ,IAAA,CAAK;AAAA,KACP;AAAA,EACF;AACF;AAOO,MAAM,gCAAgCC,qCAAA,CAAqB;AAAA,EAChE,OAAA,EAASC,sCAAA;AAAA,EACT,IAAA,EAAM;AAAA,IACJ,QAAQC,6BAAA,CAAa,MAAA;AAAA,IACrB,MAAMA,6BAAA,CAAa;AAAA,GACrB;AAAA,EACA,MAAM,OAAA,CAAQ,EAAE,MAAA,EAAQ,MAAK,EAAG;AAC9B,IAAA,MAAA,CACG,MAAM,EAAE,MAAA,EAAQ,kCAAkC,CAAA,CAClD,KAAK,kDAAkD,CAAA;AAE1D,IAAA,iCAAA,CAAkC,IAAA,GAAO,IAAA;AAEzC,IAAA,OAAO,iCAAA,CAAkC,OAAA;AAAA,EAC3C;AACF,CAAC;;;;;;;;"}
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.5.0",
3
+ "version": "0.6.1",
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.13.1",
38
- "@backstage/backend-plugin-api": "^1.5.0",
39
- "@backstage/catalog-client": "^1.12.1",
40
- "@backstage/config": "^1.3.6",
41
- "@backstage/errors": "^1.2.7",
42
- "@backstage/plugin-catalog-node": "^1.20.0",
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.10.1",
49
- "@backstage/cli": "^0.34.5",
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",