@backstage/plugin-techdocs-backend 2.1.7-next.0 → 2.1.7-next.2
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/DocsBuilder/builder.cjs.js +2 -4
- package/dist/DocsBuilder/builder.cjs.js.map +1 -1
- package/dist/cache/TechDocsCache.cjs.js +3 -3
- package/dist/cache/TechDocsCache.cjs.js.map +1 -1
- package/dist/plugin.cjs.js +3 -3
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/service/CachedEntityLoader.cjs.js +9 -12
- package/dist/service/CachedEntityLoader.cjs.js.map +1 -1
- package/dist/service/DocsSynchronizer.cjs.js +5 -6
- package/dist/service/DocsSynchronizer.cjs.js.map +1 -1
- package/dist/service/router.cjs.js +9 -28
- package/dist/service/router.cjs.js.map +1 -1
- package/package.json +12 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @backstage/plugin-techdocs-backend
|
|
2
2
|
|
|
3
|
+
## 2.1.7-next.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 482ceed: Migrated from `assertError` to `toError` for error handling.
|
|
8
|
+
- 5e32f77: Migrated internal usage of the deprecated `catalogServiceRef` from `@backstage/plugin-catalog-node/alpha` to the stable `catalogServiceRef` from `@backstage/plugin-catalog-node`.
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
- @backstage/errors@1.3.0-next.0
|
|
11
|
+
- @backstage/plugin-catalog-node@2.2.0-next.2
|
|
12
|
+
- @backstage/plugin-techdocs-node@1.14.5-next.2
|
|
13
|
+
- @backstage/integration@2.0.1-next.0
|
|
14
|
+
- @backstage/backend-plugin-api@1.9.0-next.2
|
|
15
|
+
- @backstage/catalog-client@1.14.1-next.0
|
|
16
|
+
- @backstage/catalog-model@1.7.8-next.0
|
|
17
|
+
- @backstage/config@1.3.7-next.0
|
|
18
|
+
|
|
19
|
+
## 2.1.7-next.1
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies
|
|
24
|
+
- @backstage/backend-plugin-api@1.9.0-next.1
|
|
25
|
+
- @backstage/plugin-catalog-node@2.1.1-next.1
|
|
26
|
+
- @backstage/plugin-techdocs-node@1.14.5-next.1
|
|
27
|
+
|
|
3
28
|
## 2.1.7-next.0
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
|
@@ -151,9 +151,8 @@ class DocsBuilder {
|
|
|
151
151
|
try {
|
|
152
152
|
fs__default.default.remove(preparedDir);
|
|
153
153
|
} catch (error) {
|
|
154
|
-
errors.assertError(error);
|
|
155
154
|
this.logger.debug(
|
|
156
|
-
`Error removing prepared directory ${error.message}`
|
|
155
|
+
`Error removing prepared directory ${errors.toError(error).message}`
|
|
157
156
|
);
|
|
158
157
|
}
|
|
159
158
|
}
|
|
@@ -162,9 +161,8 @@ class DocsBuilder {
|
|
|
162
161
|
try {
|
|
163
162
|
fs__default.default.remove(outputDir);
|
|
164
163
|
} catch (error) {
|
|
165
|
-
errors.assertError(error);
|
|
166
164
|
this.logger.debug(
|
|
167
|
-
`Error removing generated directory ${error.message}`
|
|
165
|
+
`Error removing generated directory ${errors.toError(error).message}`
|
|
168
166
|
);
|
|
169
167
|
}
|
|
170
168
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.cjs.js","sources":["../../src/DocsBuilder/builder.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n DEFAULT_NAMESPACE,\n Entity,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { assertError, isError } from '@backstage/errors';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport {\n GeneratorBase,\n GeneratorBuilder,\n getLocationForEntity,\n PreparerBase,\n PreparerBuilder,\n PublisherBase,\n} from '@backstage/plugin-techdocs-node';\nimport fs from 'fs-extra';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { Writable } from 'node:stream';\nimport { Logger } from 'winston';\nimport { BuildMetadataStorage } from './BuildMetadataStorage';\nimport { TechDocsCache } from '../cache';\n\ntype DocsBuilderArguments = {\n preparers: PreparerBuilder;\n generators: GeneratorBuilder;\n publisher: PublisherBase;\n entity: Entity;\n logger: Logger;\n config: Config;\n scmIntegrations: ScmIntegrationRegistry;\n logStream?: Writable;\n cache?: TechDocsCache;\n};\n\nexport class DocsBuilder {\n private preparer: PreparerBase;\n private generator: GeneratorBase;\n private publisher: PublisherBase;\n private entity: Entity;\n private logger: Logger;\n private config: Config;\n private scmIntegrations: ScmIntegrationRegistry;\n private logStream: Writable | undefined;\n private cache?: TechDocsCache;\n\n constructor({\n preparers,\n generators,\n publisher,\n entity,\n logger,\n config,\n scmIntegrations,\n logStream,\n cache,\n }: DocsBuilderArguments) {\n this.preparer = preparers.get(entity);\n this.generator = generators.get(entity);\n this.publisher = publisher;\n this.entity = entity;\n this.logger = logger;\n this.config = config;\n this.scmIntegrations = scmIntegrations;\n this.logStream = logStream;\n this.cache = cache;\n }\n\n /**\n * Build the docs and return whether they have been newly generated or have been cached\n * @returns true, if the docs have been built. false, if the cached docs are still up-to-date.\n */\n public async build(): Promise<boolean> {\n if (!this.entity.metadata.uid) {\n throw new Error(\n 'Trying to build documentation for entity not in software catalog',\n );\n }\n\n /**\n * Prepare (and cache check)\n */\n\n this.logger.info(\n `Step 1 of 3: Preparing docs for entity ${stringifyEntityRef(\n this.entity,\n )}`,\n );\n\n // If available, use the etag stored in techdocs_metadata.json to\n // check if docs are outdated and need to be regenerated.\n let storedEtag: string | undefined;\n if (await this.publisher.hasDocsBeenGenerated(this.entity)) {\n try {\n storedEtag = (\n await this.publisher.fetchTechDocsMetadata({\n namespace: this.entity.metadata.namespace ?? DEFAULT_NAMESPACE,\n kind: this.entity.kind,\n name: this.entity.metadata.name,\n })\n ).etag;\n } catch (err) {\n // Proceed with a fresh build\n this.logger.warn(\n `Unable to read techdocs_metadata.json, proceeding with fresh build, error ${err}.`,\n );\n }\n }\n\n let preparedDir: string | undefined;\n let outputDir: string | undefined;\n\n try {\n let newEtag: string;\n try {\n const preparerResponse = await this.preparer.prepare(this.entity, {\n etag: storedEtag,\n logger: this.logger,\n });\n\n preparedDir = preparerResponse.preparedDir;\n newEtag = preparerResponse.etag;\n } catch (err) {\n if (isError(err) && err.name === 'NotModifiedError') {\n // No need to prepare anymore since cache is valid.\n // Set last check happened to now\n new BuildMetadataStorage(this.entity.metadata.uid).setLastUpdated();\n this.logger.debug(\n `Docs for ${stringifyEntityRef(\n this.entity,\n )} are unmodified. Using cache, skipping generate and prepare`,\n );\n return false;\n }\n throw err;\n }\n\n this.logger.info(\n `Prepare step completed for entity ${stringifyEntityRef(\n this.entity,\n )}, stored at ${preparedDir}`,\n );\n\n /**\n * Generate\n */\n\n this.logger.info(\n `Step 2 of 3: Generating docs for entity ${stringifyEntityRef(\n this.entity,\n )}`,\n );\n\n const workingDir = this.config.getOptionalString(\n 'backend.workingDirectory',\n );\n const tmpdirPath = workingDir || os.tmpdir();\n // Fixes a problem with macOS returning a path that is a symlink\n const tmpdirResolvedPath = fs.realpathSync(tmpdirPath);\n outputDir = await fs.mkdtemp(\n path.join(tmpdirResolvedPath, 'techdocs-tmp-'),\n );\n\n const parsedLocationAnnotation = getLocationForEntity(\n this.entity,\n this.scmIntegrations,\n );\n await this.generator.run({\n inputDir: preparedDir,\n outputDir,\n parsedLocationAnnotation,\n etag: newEtag,\n logger: this.logger,\n logStream: this.logStream,\n siteOptions: {\n name: this.entity.metadata.title ?? this.entity.metadata.name,\n },\n });\n\n /**\n * Publish\n */\n\n this.logger.info(\n `Step 3 of 3: Publishing docs for entity ${stringifyEntityRef(\n this.entity,\n )}`,\n );\n\n const published = await this.publisher.publish({\n entity: this.entity,\n directory: outputDir,\n });\n\n // Invalidate the cache for any published objects.\n if (this.cache && published && published?.objects?.length) {\n this.logger.debug(\n `Invalidating ${published.objects.length} cache objects`,\n );\n await this.cache.invalidateMultiple(published.objects);\n }\n } finally {\n // Remove Prepared directory since it is no longer needed.\n // Caveat: Can not remove prepared directory in case of git preparer since the\n // local git repository is used to get etag on subsequent requests.\n if (preparedDir && this.preparer.shouldCleanPreparedDirectory()) {\n this.logger.debug(`Removing prepared directory ${preparedDir}`);\n try {\n // Not a blocker hence no need to await this.\n fs.remove(preparedDir);\n } catch (error) {\n assertError(error);\n this.logger.debug(\n `Error removing prepared directory ${error.message}`,\n );\n }\n }\n\n if (outputDir) {\n this.logger.debug(`Removing generated directory ${outputDir}`);\n try {\n // Not a blocker hence no need to await this.\n fs.remove(outputDir);\n } catch (error) {\n assertError(error);\n this.logger.debug(\n `Error removing generated directory ${error.message}`,\n );\n }\n }\n }\n\n // Update the last check time for the entity\n new BuildMetadataStorage(this.entity.metadata.uid).setLastUpdated();\n\n return true;\n }\n}\n"],"names":["stringifyEntityRef","DEFAULT_NAMESPACE","isError","BuildMetadataStorage","os","fs","path","getLocationForEntity","assertError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"builder.cjs.js","sources":["../../src/DocsBuilder/builder.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n DEFAULT_NAMESPACE,\n Entity,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { isError, toError } from '@backstage/errors';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport {\n GeneratorBase,\n GeneratorBuilder,\n getLocationForEntity,\n PreparerBase,\n PreparerBuilder,\n PublisherBase,\n} from '@backstage/plugin-techdocs-node';\nimport fs from 'fs-extra';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { Writable } from 'node:stream';\nimport { Logger } from 'winston';\nimport { BuildMetadataStorage } from './BuildMetadataStorage';\nimport { TechDocsCache } from '../cache';\n\ntype DocsBuilderArguments = {\n preparers: PreparerBuilder;\n generators: GeneratorBuilder;\n publisher: PublisherBase;\n entity: Entity;\n logger: Logger;\n config: Config;\n scmIntegrations: ScmIntegrationRegistry;\n logStream?: Writable;\n cache?: TechDocsCache;\n};\n\nexport class DocsBuilder {\n private preparer: PreparerBase;\n private generator: GeneratorBase;\n private publisher: PublisherBase;\n private entity: Entity;\n private logger: Logger;\n private config: Config;\n private scmIntegrations: ScmIntegrationRegistry;\n private logStream: Writable | undefined;\n private cache?: TechDocsCache;\n\n constructor({\n preparers,\n generators,\n publisher,\n entity,\n logger,\n config,\n scmIntegrations,\n logStream,\n cache,\n }: DocsBuilderArguments) {\n this.preparer = preparers.get(entity);\n this.generator = generators.get(entity);\n this.publisher = publisher;\n this.entity = entity;\n this.logger = logger;\n this.config = config;\n this.scmIntegrations = scmIntegrations;\n this.logStream = logStream;\n this.cache = cache;\n }\n\n /**\n * Build the docs and return whether they have been newly generated or have been cached\n * @returns true, if the docs have been built. false, if the cached docs are still up-to-date.\n */\n public async build(): Promise<boolean> {\n if (!this.entity.metadata.uid) {\n throw new Error(\n 'Trying to build documentation for entity not in software catalog',\n );\n }\n\n /**\n * Prepare (and cache check)\n */\n\n this.logger.info(\n `Step 1 of 3: Preparing docs for entity ${stringifyEntityRef(\n this.entity,\n )}`,\n );\n\n // If available, use the etag stored in techdocs_metadata.json to\n // check if docs are outdated and need to be regenerated.\n let storedEtag: string | undefined;\n if (await this.publisher.hasDocsBeenGenerated(this.entity)) {\n try {\n storedEtag = (\n await this.publisher.fetchTechDocsMetadata({\n namespace: this.entity.metadata.namespace ?? DEFAULT_NAMESPACE,\n kind: this.entity.kind,\n name: this.entity.metadata.name,\n })\n ).etag;\n } catch (err) {\n // Proceed with a fresh build\n this.logger.warn(\n `Unable to read techdocs_metadata.json, proceeding with fresh build, error ${err}.`,\n );\n }\n }\n\n let preparedDir: string | undefined;\n let outputDir: string | undefined;\n\n try {\n let newEtag: string;\n try {\n const preparerResponse = await this.preparer.prepare(this.entity, {\n etag: storedEtag,\n logger: this.logger,\n });\n\n preparedDir = preparerResponse.preparedDir;\n newEtag = preparerResponse.etag;\n } catch (err) {\n if (isError(err) && err.name === 'NotModifiedError') {\n // No need to prepare anymore since cache is valid.\n // Set last check happened to now\n new BuildMetadataStorage(this.entity.metadata.uid).setLastUpdated();\n this.logger.debug(\n `Docs for ${stringifyEntityRef(\n this.entity,\n )} are unmodified. Using cache, skipping generate and prepare`,\n );\n return false;\n }\n throw err;\n }\n\n this.logger.info(\n `Prepare step completed for entity ${stringifyEntityRef(\n this.entity,\n )}, stored at ${preparedDir}`,\n );\n\n /**\n * Generate\n */\n\n this.logger.info(\n `Step 2 of 3: Generating docs for entity ${stringifyEntityRef(\n this.entity,\n )}`,\n );\n\n const workingDir = this.config.getOptionalString(\n 'backend.workingDirectory',\n );\n const tmpdirPath = workingDir || os.tmpdir();\n // Fixes a problem with macOS returning a path that is a symlink\n const tmpdirResolvedPath = fs.realpathSync(tmpdirPath);\n outputDir = await fs.mkdtemp(\n path.join(tmpdirResolvedPath, 'techdocs-tmp-'),\n );\n\n const parsedLocationAnnotation = getLocationForEntity(\n this.entity,\n this.scmIntegrations,\n );\n await this.generator.run({\n inputDir: preparedDir,\n outputDir,\n parsedLocationAnnotation,\n etag: newEtag,\n logger: this.logger,\n logStream: this.logStream,\n siteOptions: {\n name: this.entity.metadata.title ?? this.entity.metadata.name,\n },\n });\n\n /**\n * Publish\n */\n\n this.logger.info(\n `Step 3 of 3: Publishing docs for entity ${stringifyEntityRef(\n this.entity,\n )}`,\n );\n\n const published = await this.publisher.publish({\n entity: this.entity,\n directory: outputDir,\n });\n\n // Invalidate the cache for any published objects.\n if (this.cache && published && published?.objects?.length) {\n this.logger.debug(\n `Invalidating ${published.objects.length} cache objects`,\n );\n await this.cache.invalidateMultiple(published.objects);\n }\n } finally {\n // Remove Prepared directory since it is no longer needed.\n // Caveat: Can not remove prepared directory in case of git preparer since the\n // local git repository is used to get etag on subsequent requests.\n if (preparedDir && this.preparer.shouldCleanPreparedDirectory()) {\n this.logger.debug(`Removing prepared directory ${preparedDir}`);\n try {\n // Not a blocker hence no need to await this.\n fs.remove(preparedDir);\n } catch (error) {\n this.logger.debug(\n `Error removing prepared directory ${toError(error).message}`,\n );\n }\n }\n\n if (outputDir) {\n this.logger.debug(`Removing generated directory ${outputDir}`);\n try {\n // Not a blocker hence no need to await this.\n fs.remove(outputDir);\n } catch (error) {\n this.logger.debug(\n `Error removing generated directory ${toError(error).message}`,\n );\n }\n }\n }\n\n // Update the last check time for the entity\n new BuildMetadataStorage(this.entity.metadata.uid).setLastUpdated();\n\n return true;\n }\n}\n"],"names":["stringifyEntityRef","DEFAULT_NAMESPACE","isError","BuildMetadataStorage","os","fs","path","getLocationForEntity","toError"],"mappings":";;;;;;;;;;;;;;;;AAmDO,MAAM,WAAA,CAAY;AAAA,EACf,QAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,eAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EAER,WAAA,CAAY;AAAA,IACV,SAAA;AAAA,IACA,UAAA;AAAA,IACA,SAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,eAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF,EAAyB;AACvB,IAAA,IAAA,CAAK,QAAA,GAAW,SAAA,CAAU,GAAA,CAAI,MAAM,CAAA;AACpC,IAAA,IAAA,CAAK,SAAA,GAAY,UAAA,CAAW,GAAA,CAAI,MAAM,CAAA;AACtC,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AACjB,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,eAAA,GAAkB,eAAA;AACvB,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AACjB,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,KAAA,GAA0B;AACrC,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,GAAA,EAAK;AAC7B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAMA,IAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MACV,CAAA,uCAAA,EAA0CA,+BAAA;AAAA,QACxC,IAAA,CAAK;AAAA,OACN,CAAA;AAAA,KACH;AAIA,IAAA,IAAI,UAAA;AACJ,IAAA,IAAI,MAAM,IAAA,CAAK,SAAA,CAAU,oBAAA,CAAqB,IAAA,CAAK,MAAM,CAAA,EAAG;AAC1D,MAAA,IAAI;AACF,QAAA,UAAA,GAAA,CACE,MAAM,IAAA,CAAK,SAAA,CAAU,qBAAA,CAAsB;AAAA,UACzC,SAAA,EAAW,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,SAAA,IAAaC,8BAAA;AAAA,UAC7C,IAAA,EAAM,KAAK,MAAA,CAAO,IAAA;AAAA,UAClB,IAAA,EAAM,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS;AAAA,SAC5B,CAAA,EACD,IAAA;AAAA,MACJ,SAAS,GAAA,EAAK;AAEZ,QAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,UACV,6EAA6E,GAAG,CAAA,CAAA;AAAA,SAClF;AAAA,MACF;AAAA,IACF;AAEA,IAAA,IAAI,WAAA;AACJ,IAAA,IAAI,SAAA;AAEJ,IAAA,IAAI;AACF,MAAA,IAAI,OAAA;AACJ,MAAA,IAAI;AACF,QAAA,MAAM,mBAAmB,MAAM,IAAA,CAAK,QAAA,CAAS,OAAA,CAAQ,KAAK,MAAA,EAAQ;AAAA,UAChE,IAAA,EAAM,UAAA;AAAA,UACN,QAAQ,IAAA,CAAK;AAAA,SACd,CAAA;AAED,QAAA,WAAA,GAAc,gBAAA,CAAiB,WAAA;AAC/B,QAAA,OAAA,GAAU,gBAAA,CAAiB,IAAA;AAAA,MAC7B,SAAS,GAAA,EAAK;AACZ,QAAA,IAAIC,cAAA,CAAQ,GAAG,CAAA,IAAK,GAAA,CAAI,SAAS,kBAAA,EAAoB;AAGnD,UAAA,IAAIC,0CAAqB,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,GAAG,EAAE,cAAA,EAAe;AAClE,UAAA,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,YACV,CAAA,SAAA,EAAYH,+BAAA;AAAA,cACV,IAAA,CAAK;AAAA,aACN,CAAA,2DAAA;AAAA,WACH;AACA,UAAA,OAAO,KAAA;AAAA,QACT;AACA,QAAA,MAAM,GAAA;AAAA,MACR;AAEA,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,QACV,CAAA,kCAAA,EAAqCA,+BAAA;AAAA,UACnC,IAAA,CAAK;AAAA,SACN,eAAe,WAAW,CAAA;AAAA,OAC7B;AAMA,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,QACV,CAAA,wCAAA,EAA2CA,+BAAA;AAAA,UACzC,IAAA,CAAK;AAAA,SACN,CAAA;AAAA,OACH;AAEA,MAAA,MAAM,UAAA,GAAa,KAAK,MAAA,CAAO,iBAAA;AAAA,QAC7B;AAAA,OACF;AACA,MAAA,MAAM,UAAA,GAAa,UAAA,IAAcI,mBAAA,CAAG,MAAA,EAAO;AAE3C,MAAA,MAAM,kBAAA,GAAqBC,mBAAA,CAAG,YAAA,CAAa,UAAU,CAAA;AACrD,MAAA,SAAA,GAAY,MAAMA,mBAAA,CAAG,OAAA;AAAA,QACnBC,qBAAA,CAAK,IAAA,CAAK,kBAAA,EAAoB,eAAe;AAAA,OAC/C;AAEA,MAAA,MAAM,wBAAA,GAA2BC,uCAAA;AAAA,QAC/B,IAAA,CAAK,MAAA;AAAA,QACL,IAAA,CAAK;AAAA,OACP;AACA,MAAA,MAAM,IAAA,CAAK,UAAU,GAAA,CAAI;AAAA,QACvB,QAAA,EAAU,WAAA;AAAA,QACV,SAAA;AAAA,QACA,wBAAA;AAAA,QACA,IAAA,EAAM,OAAA;AAAA,QACN,QAAQ,IAAA,CAAK,MAAA;AAAA,QACb,WAAW,IAAA,CAAK,SAAA;AAAA,QAChB,WAAA,EAAa;AAAA,UACX,MAAM,IAAA,CAAK,MAAA,CAAO,SAAS,KAAA,IAAS,IAAA,CAAK,OAAO,QAAA,CAAS;AAAA;AAC3D,OACD,CAAA;AAMD,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,QACV,CAAA,wCAAA,EAA2CP,+BAAA;AAAA,UACzC,IAAA,CAAK;AAAA,SACN,CAAA;AAAA,OACH;AAEA,MAAA,MAAM,SAAA,GAAY,MAAM,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ;AAAA,QAC7C,QAAQ,IAAA,CAAK,MAAA;AAAA,QACb,SAAA,EAAW;AAAA,OACZ,CAAA;AAGD,MAAA,IAAI,IAAA,CAAK,KAAA,IAAS,SAAA,IAAa,SAAA,EAAW,SAAS,MAAA,EAAQ;AACzD,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,UACV,CAAA,aAAA,EAAgB,SAAA,CAAU,OAAA,CAAQ,MAAM,CAAA,cAAA;AAAA,SAC1C;AACA,QAAA,MAAM,IAAA,CAAK,KAAA,CAAM,kBAAA,CAAmB,SAAA,CAAU,OAAO,CAAA;AAAA,MACvD;AAAA,IACF,CAAA,SAAE;AAIA,MAAA,IAAI,WAAA,IAAe,IAAA,CAAK,QAAA,CAAS,4BAAA,EAA6B,EAAG;AAC/D,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,4BAAA,EAA+B,WAAW,CAAA,CAAE,CAAA;AAC9D,QAAA,IAAI;AAEF,UAAAK,mBAAA,CAAG,OAAO,WAAW,CAAA;AAAA,QACvB,SAAS,KAAA,EAAO;AACd,UAAA,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,YACV,CAAA,kCAAA,EAAqCG,cAAA,CAAQ,KAAK,CAAA,CAAE,OAAO,CAAA;AAAA,WAC7D;AAAA,QACF;AAAA,MACF;AAEA,MAAA,IAAI,SAAA,EAAW;AACb,QAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,6BAAA,EAAgC,SAAS,CAAA,CAAE,CAAA;AAC7D,QAAA,IAAI;AAEF,UAAAH,mBAAA,CAAG,OAAO,SAAS,CAAA;AAAA,QACrB,SAAS,KAAA,EAAO;AACd,UAAA,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,YACV,CAAA,mCAAA,EAAsCG,cAAA,CAAQ,KAAK,CAAA,CAAE,OAAO,CAAA;AAAA,WAC9D;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,IAAA,IAAIL,0CAAqB,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,GAAG,EAAE,cAAA,EAAe;AAElE,IAAA,OAAO,IAAA;AAAA,EACT;AACF;;;;"}
|
|
@@ -49,9 +49,9 @@ class TechDocsCache {
|
|
|
49
49
|
this.logger.debug(`Cache miss: ${path}`);
|
|
50
50
|
return response;
|
|
51
51
|
} catch (e) {
|
|
52
|
-
errors.
|
|
53
|
-
this.logger.warn(`Error getting cache entry ${path}: ${
|
|
54
|
-
this.logger.debug(
|
|
52
|
+
const error = errors.toError(e);
|
|
53
|
+
this.logger.warn(`Error getting cache entry ${path}: ${error.message}`);
|
|
54
|
+
this.logger.debug(error.message, error);
|
|
55
55
|
return void 0;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TechDocsCache.cjs.js","sources":["../../src/cache/TechDocsCache.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {
|
|
1
|
+
{"version":3,"file":"TechDocsCache.cjs.js","sources":["../../src/cache/TechDocsCache.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { CustomErrorBase, toError } from '@backstage/errors';\nimport { Config, readDurationFromConfig } from '@backstage/config';\nimport { CacheService, LoggerService } from '@backstage/backend-plugin-api';\nimport { durationToMilliseconds } from '@backstage/types';\n\nexport class CacheInvalidationError extends CustomErrorBase {}\n\nexport class TechDocsCache {\n protected readonly cache: CacheService;\n protected readonly logger: LoggerService;\n protected readonly readTimeout: number;\n\n private constructor({\n cache,\n logger,\n readTimeout,\n }: {\n cache: CacheService;\n logger: LoggerService;\n readTimeout: number;\n }) {\n this.cache = cache;\n this.logger = logger;\n this.readTimeout = readTimeout;\n }\n\n static fromConfig(\n config: Config,\n { cache, logger }: { cache: CacheService; logger: LoggerService },\n ) {\n let readTimeout: number;\n if (config.has('techdocs.cache.readTimeout')) {\n if (typeof config.get('techdocs.cache.readTimeout') === 'number') {\n readTimeout = config.getNumber('techdocs.cache.readTimeout');\n } else {\n readTimeout = durationToMilliseconds(\n readDurationFromConfig(config, {\n key: 'techdocs.cache.readTimeout',\n }),\n );\n }\n } else {\n readTimeout = 1000;\n }\n return new TechDocsCache({ cache, logger, readTimeout });\n }\n\n async get(path: string): Promise<Buffer | undefined> {\n try {\n // Promise.race ensures we don't hang the client for long if the cache is\n // temporarily unreachable.\n const response = (await Promise.race([\n this.cache.get(path),\n new Promise(cancelAfter => setTimeout(cancelAfter, this.readTimeout)),\n ])) as string | undefined;\n\n if (response !== undefined) {\n this.logger.debug(`Cache hit: ${path}`);\n return Buffer.from(response, 'base64');\n }\n\n this.logger.debug(`Cache miss: ${path}`);\n return response;\n } catch (e) {\n const error = toError(e);\n this.logger.warn(`Error getting cache entry ${path}: ${error.message}`);\n this.logger.debug(error.message, error);\n return undefined;\n }\n }\n\n async set(path: string, data: Buffer): Promise<void> {\n this.logger.debug(`Writing cache entry for ${path}`);\n this.cache\n .set(path, data.toString('base64'))\n .catch(e => this.logger.error('write error', e));\n }\n\n async invalidate(path: string): Promise<void> {\n return this.cache.delete(path);\n }\n\n async invalidateMultiple(\n paths: string[],\n ): Promise<PromiseSettledResult<void>[]> {\n const settled = await Promise.allSettled(\n paths.map(path => this.cache.delete(path)),\n );\n const rejected = settled.filter(\n s => s.status === 'rejected',\n ) as PromiseRejectedResult[];\n\n if (rejected.length) {\n throw new CacheInvalidationError(\n 'TechDocs cache invalidation error',\n rejected,\n );\n }\n\n return settled;\n }\n}\n"],"names":["CustomErrorBase","config","durationToMilliseconds","readDurationFromConfig","toError"],"mappings":"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"}
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
4
|
var pluginTechdocsNode = require('@backstage/plugin-techdocs-node');
|
|
5
|
-
var
|
|
5
|
+
var pluginCatalogNode = require('@backstage/plugin-catalog-node');
|
|
6
6
|
var router = require('./service/router.cjs.js');
|
|
7
7
|
|
|
8
8
|
const techdocsPlugin = backendPluginApi.createBackendPlugin({
|
|
@@ -67,7 +67,7 @@ const techdocsPlugin = backendPluginApi.createBackendPlugin({
|
|
|
67
67
|
cache: backendPluginApi.coreServices.cache,
|
|
68
68
|
httpAuth: backendPluginApi.coreServices.httpAuth,
|
|
69
69
|
auth: backendPluginApi.coreServices.auth,
|
|
70
|
-
catalog:
|
|
70
|
+
catalog: pluginCatalogNode.catalogServiceRef
|
|
71
71
|
},
|
|
72
72
|
async init({
|
|
73
73
|
config,
|
|
@@ -111,7 +111,7 @@ const techdocsPlugin = backendPluginApi.createBackendPlugin({
|
|
|
111
111
|
discovery,
|
|
112
112
|
httpAuth,
|
|
113
113
|
auth,
|
|
114
|
-
|
|
114
|
+
catalog
|
|
115
115
|
})
|
|
116
116
|
);
|
|
117
117
|
http.addAuthPolicy({
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport {\n DocsBuildStrategy,\n Generators,\n PreparerBase,\n Preparers,\n Publisher,\n PublisherBase,\n PublisherSettings,\n PublisherType,\n RemoteProtocol,\n techdocsBuildsExtensionPoint,\n TechdocsGenerator,\n techdocsGeneratorExtensionPoint,\n techdocsPreparerExtensionPoint,\n techdocsPublisherExtensionPoint,\n} from '@backstage/plugin-techdocs-node';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport {\n DocsBuildStrategy,\n Generators,\n PreparerBase,\n Preparers,\n Publisher,\n PublisherBase,\n PublisherSettings,\n PublisherType,\n RemoteProtocol,\n techdocsBuildsExtensionPoint,\n TechdocsGenerator,\n techdocsGeneratorExtensionPoint,\n techdocsPreparerExtensionPoint,\n techdocsPublisherExtensionPoint,\n} from '@backstage/plugin-techdocs-node';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node';\nimport * as winston from 'winston';\nimport { createRouter } from './service/router';\n\n/**\n * The TechDocs plugin is responsible for serving and building documentation for any entity.\n * @public\n */\nexport const techdocsPlugin = createBackendPlugin({\n pluginId: 'techdocs',\n register(env) {\n let docsBuildStrategy: DocsBuildStrategy | undefined;\n let buildLogTransport: winston.transport | undefined;\n env.registerExtensionPoint(techdocsBuildsExtensionPoint, {\n setBuildStrategy(buildStrategy: DocsBuildStrategy) {\n if (docsBuildStrategy) {\n throw new Error('DocsBuildStrategy may only be set once');\n }\n docsBuildStrategy = buildStrategy;\n },\n setBuildLogTransport(transport: winston.transport) {\n if (buildLogTransport) {\n throw new Error('BuildLogTransport may only be set once');\n }\n buildLogTransport = transport;\n },\n });\n\n let customTechdocsGenerator: TechdocsGenerator | undefined;\n env.registerExtensionPoint(techdocsGeneratorExtensionPoint, {\n setTechdocsGenerator(generator: TechdocsGenerator) {\n if (customTechdocsGenerator) {\n throw new Error('TechdocsGenerator may only be set once');\n }\n\n customTechdocsGenerator = generator;\n },\n });\n\n const customPreparers = new Map<RemoteProtocol, PreparerBase>();\n env.registerExtensionPoint(techdocsPreparerExtensionPoint, {\n registerPreparer(protocol: RemoteProtocol, preparer: PreparerBase) {\n if (customPreparers.has(protocol)) {\n throw new Error(\n `Preparer for protocol ${protocol} is already registered`,\n );\n }\n customPreparers.set(protocol, preparer);\n },\n });\n\n let customTechdocsPublisher: PublisherBase | undefined;\n const publisherSettings: PublisherSettings = {};\n env.registerExtensionPoint(techdocsPublisherExtensionPoint, {\n registerPublisher(type: PublisherType, publisher: PublisherBase) {\n if (customTechdocsPublisher) {\n throw new Error(`Publisher for type ${type} is already registered`);\n }\n customTechdocsPublisher = publisher;\n },\n registerPublisherSettings<T extends keyof PublisherSettings>(\n publisher: T,\n settings: PublisherSettings[T],\n ) {\n publisherSettings[publisher] = settings;\n },\n });\n\n env.registerInit({\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n urlReader: coreServices.urlReader,\n http: coreServices.httpRouter,\n discovery: coreServices.discovery,\n cache: coreServices.cache,\n httpAuth: coreServices.httpAuth,\n auth: coreServices.auth,\n catalog: catalogServiceRef,\n },\n async init({\n config,\n logger,\n urlReader,\n http,\n discovery,\n cache,\n httpAuth,\n auth,\n catalog,\n }) {\n // Preparers are responsible for fetching source files for documentation.\n const preparers = await Preparers.fromConfig(config, {\n reader: urlReader,\n logger: logger,\n });\n for (const [protocol, preparer] of customPreparers.entries()) {\n preparers.register(protocol, preparer);\n }\n\n // Generators are used for generating documentation sites.\n const generators = await Generators.fromConfig(config, {\n logger: logger,\n customGenerator: customTechdocsGenerator,\n });\n\n // Publisher is used for\n // 1. Publishing generated files to storage\n // 2. Fetching files from storage and passing them to TechDocs frontend.\n const publisher = await Publisher.fromConfig(config, {\n logger: logger,\n discovery: discovery,\n customPublisher: customTechdocsPublisher,\n publisherSettings,\n });\n\n // checks if the publisher is working and logs the result\n await publisher.getReadiness();\n\n http.use(\n await createRouter({\n logger: logger,\n cache,\n docsBuildStrategy,\n buildLogTransport,\n preparers,\n generators,\n publisher,\n config,\n discovery,\n httpAuth,\n auth,\n catalog,\n }),\n );\n\n http.addAuthPolicy({\n path: '/static',\n allow: 'user-cookie',\n });\n },\n });\n },\n});\n"],"names":["createBackendPlugin","techdocsBuildsExtensionPoint","techdocsGeneratorExtensionPoint","techdocsPreparerExtensionPoint","techdocsPublisherExtensionPoint","coreServices","catalogServiceRef","Preparers","Generators","Publisher","createRouter"],"mappings":"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"}
|
|
@@ -3,22 +3,20 @@
|
|
|
3
3
|
var catalogModel = require('@backstage/catalog-model');
|
|
4
4
|
|
|
5
5
|
class CachedEntityLoader {
|
|
6
|
-
auth;
|
|
7
6
|
catalog;
|
|
8
7
|
cache;
|
|
9
8
|
readTimeout = 1e3;
|
|
10
|
-
constructor({
|
|
11
|
-
this.auth = auth;
|
|
9
|
+
constructor({ catalog, cache }) {
|
|
12
10
|
this.catalog = catalog;
|
|
13
11
|
this.cache = cache;
|
|
14
12
|
}
|
|
15
|
-
async load(credentials, entityRef
|
|
13
|
+
async load(credentials, entityRef) {
|
|
16
14
|
const cacheKey = this.getCacheKey(entityRef, credentials);
|
|
17
15
|
let result = await this.getFromCache(cacheKey);
|
|
18
16
|
if (result) {
|
|
19
17
|
return result;
|
|
20
18
|
}
|
|
21
|
-
result = await this.catalog.getEntityByRef(entityRef, {
|
|
19
|
+
result = await this.catalog.getEntityByRef(entityRef, { credentials });
|
|
22
20
|
if (result) {
|
|
23
21
|
this.cache.set(cacheKey, result, { ttl: 5e3 });
|
|
24
22
|
}
|
|
@@ -31,13 +29,12 @@ class CachedEntityLoader {
|
|
|
31
29
|
]);
|
|
32
30
|
}
|
|
33
31
|
getCacheKey(entityName, credentials) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return key.join(":");
|
|
32
|
+
return [
|
|
33
|
+
"catalog",
|
|
34
|
+
catalogModel.stringifyEntityRef(entityName),
|
|
35
|
+
String(credentials)
|
|
36
|
+
// these have a well defined toString method
|
|
37
|
+
].join(":");
|
|
41
38
|
}
|
|
42
39
|
}
|
|
43
40
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CachedEntityLoader.cjs.js","sources":["../../src/service/CachedEntityLoader.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n
|
|
1
|
+
{"version":3,"file":"CachedEntityLoader.cjs.js","sources":["../../src/service/CachedEntityLoader.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n BackstageCredentials,\n CacheService,\n} from '@backstage/backend-plugin-api';\nimport {\n Entity,\n CompoundEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport type CachedEntityLoaderOptions = {\n catalog: CatalogService;\n cache: CacheService;\n};\n\nexport class CachedEntityLoader {\n private readonly catalog: CatalogService;\n private readonly cache: CacheService;\n private readonly readTimeout = 1000;\n\n constructor({ catalog, cache }: CachedEntityLoaderOptions) {\n this.catalog = catalog;\n this.cache = cache;\n }\n\n async load(\n credentials: BackstageCredentials,\n entityRef: CompoundEntityRef,\n ): Promise<Entity | undefined> {\n const cacheKey = this.getCacheKey(entityRef, credentials);\n let result = await this.getFromCache(cacheKey);\n\n if (result) {\n return result;\n }\n\n result = await this.catalog.getEntityByRef(entityRef, { credentials });\n\n if (result) {\n this.cache.set(cacheKey, result, { ttl: 5000 });\n }\n\n return result;\n }\n\n private async getFromCache(key: string): Promise<Entity | undefined> {\n // Promise.race ensures we don't hang the client for long if the cache is\n // temporarily unreachable.\n return (await Promise.race([\n this.cache.get(key),\n new Promise(cancelAfter => setTimeout(cancelAfter, this.readTimeout)),\n ])) as Entity | undefined;\n }\n\n private getCacheKey(\n entityName: CompoundEntityRef,\n credentials: BackstageCredentials,\n ): string {\n return [\n 'catalog',\n stringifyEntityRef(entityName),\n String(credentials), // these have a well defined toString method\n ].join(':');\n }\n}\n"],"names":["stringifyEntityRef"],"mappings":";;;;AAgCO,MAAM,kBAAA,CAAmB;AAAA,EACb,OAAA;AAAA,EACA,KAAA;AAAA,EACA,WAAA,GAAc,GAAA;AAAA,EAE/B,WAAA,CAAY,EAAE,OAAA,EAAS,KAAA,EAAM,EAA8B;AACzD,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AACf,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AAAA,EACf;AAAA,EAEA,MAAM,IAAA,CACJ,WAAA,EACA,SAAA,EAC6B;AAC7B,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,WAAA,CAAY,SAAA,EAAW,WAAW,CAAA;AACxD,IAAA,IAAI,MAAA,GAAS,MAAM,IAAA,CAAK,YAAA,CAAa,QAAQ,CAAA;AAE7C,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,CAAQ,eAAe,SAAA,EAAW,EAAE,aAAa,CAAA;AAErE,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,IAAA,CAAK,MAAM,GAAA,CAAI,QAAA,EAAU,QAAQ,EAAE,GAAA,EAAK,KAAM,CAAA;AAAA,IAChD;AAEA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EAEA,MAAc,aAAa,GAAA,EAA0C;AAGnE,IAAA,OAAQ,MAAM,QAAQ,IAAA,CAAK;AAAA,MACzB,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAAA,MAClB,IAAI,OAAA,CAAQ,CAAA,WAAA,KAAe,WAAW,WAAA,EAAa,IAAA,CAAK,WAAW,CAAC;AAAA,KACrE,CAAA;AAAA,EACH;AAAA,EAEQ,WAAA,CACN,YACA,WAAA,EACQ;AACR,IAAA,OAAO;AAAA,MACL,SAAA;AAAA,MACAA,gCAAmB,UAAU,CAAA;AAAA,MAC7B,OAAO,WAAW;AAAA;AAAA,KACpB,CAAE,KAAK,GAAG,CAAA;AAAA,EACZ;AACF;;;;"}
|
|
@@ -107,13 +107,13 @@ class DocsSynchronizer {
|
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
109
|
} catch (e) {
|
|
110
|
-
errors.
|
|
110
|
+
const buildError = errors.toError(e);
|
|
111
111
|
const msg = `Failed to build the docs page for entity ${catalogModel.stringifyEntityRef(
|
|
112
112
|
entity
|
|
113
|
-
)}: ${
|
|
113
|
+
)}: ${buildError.message}`;
|
|
114
114
|
taskLogger.error(msg);
|
|
115
|
-
this.logger.error(msg,
|
|
116
|
-
error(
|
|
115
|
+
this.logger.error(msg, buildError);
|
|
116
|
+
error(buildError);
|
|
117
117
|
return;
|
|
118
118
|
}
|
|
119
119
|
for (let attempt = 0; attempt < 5; attempt++) {
|
|
@@ -180,9 +180,8 @@ class DocsSynchronizer {
|
|
|
180
180
|
finish({ updated: false });
|
|
181
181
|
}
|
|
182
182
|
} catch (e) {
|
|
183
|
-
errors.assertError(e);
|
|
184
183
|
this.logger.error(
|
|
185
|
-
`Error syncing cache for ${entityTripletPath}: ${e.message}`
|
|
184
|
+
`Error syncing cache for ${entityTripletPath}: ${errors.toError(e).message}`
|
|
186
185
|
);
|
|
187
186
|
finish({ updated: false });
|
|
188
187
|
} finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocsSynchronizer.cjs.js","sources":["../../src/service/DocsSynchronizer.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n DEFAULT_NAMESPACE,\n Entity,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { assertError, NotFoundError } from '@backstage/errors';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport {\n GeneratorBuilder,\n PreparerBuilder,\n PublisherBase,\n} from '@backstage/plugin-techdocs-node';\nimport pLimit, { Limit } from 'p-limit';\nimport { PassThrough } from 'node:stream';\nimport * as winston from 'winston';\nimport { TechDocsCache } from '../cache';\nimport {\n BuildMetadataStorage,\n DocsBuilder,\n shouldCheckForUpdate,\n} from '../DocsBuilder';\nimport { DiscoveryService, LoggerService } from '@backstage/backend-plugin-api';\n\nexport type DocsSynchronizerSyncOpts = {\n log: (message: string) => void;\n error: (e: Error) => void;\n finish: (result: { updated: boolean }) => void;\n};\n\nexport class DocsSynchronizer {\n private readonly publisher: PublisherBase;\n private readonly logger: LoggerService;\n private readonly buildLogTransport?: winston.transport;\n private readonly config: Config;\n private readonly scmIntegrations: ScmIntegrationRegistry;\n private readonly cache: TechDocsCache | undefined;\n private readonly buildLimiter: Limit;\n\n constructor({\n publisher,\n logger,\n buildLogTransport,\n config,\n scmIntegrations,\n cache,\n }: {\n publisher: PublisherBase;\n logger: LoggerService;\n buildLogTransport?: winston.transport;\n config: Config;\n scmIntegrations: ScmIntegrationRegistry;\n cache: TechDocsCache | undefined;\n }) {\n this.config = config;\n this.logger = logger;\n this.buildLogTransport = buildLogTransport;\n this.publisher = publisher;\n this.scmIntegrations = scmIntegrations;\n this.cache = cache;\n\n // Single host/process: limit concurrent builds up to 10 at a time.\n this.buildLimiter = pLimit(10);\n }\n\n async doSync({\n responseHandler: { log, error, finish },\n entity,\n preparers,\n generators,\n }: {\n responseHandler: DocsSynchronizerSyncOpts;\n entity: Entity;\n preparers: PreparerBuilder;\n generators: GeneratorBuilder;\n }) {\n // create a new logger to log data to the caller\n const taskLogger = winston.createLogger({\n level: process.env.LOG_LEVEL || 'info',\n format: winston.format.combine(\n winston.format.colorize(),\n winston.format.timestamp(),\n winston.format.simple(),\n ),\n defaultMeta: {},\n });\n\n // create an in-memory stream to forward logs to the event-stream\n const logStream = new PassThrough();\n logStream.on('data', async data => {\n log(data.toString().trim());\n });\n\n taskLogger.add(new winston.transports.Stream({ stream: logStream }));\n if (this.buildLogTransport) {\n taskLogger.add(this.buildLogTransport);\n }\n\n // check if the last update check was too recent\n if (!shouldCheckForUpdate(entity.metadata.uid!)) {\n finish({ updated: false });\n return;\n }\n\n let foundDocs = false;\n\n try {\n const docsBuilder = new DocsBuilder({\n preparers,\n generators,\n publisher: this.publisher,\n logger: taskLogger,\n entity,\n config: this.config,\n scmIntegrations: this.scmIntegrations,\n logStream,\n cache: this.cache,\n });\n\n const interval = setInterval(() => {\n taskLogger.info(\n 'The docs building process is taking a little bit longer to process this entity. Please bear with us.',\n );\n }, 10000);\n const updated = await this.buildLimiter(() => docsBuilder.build());\n clearInterval(interval);\n\n if (!updated) {\n finish({ updated: false });\n return;\n }\n } catch (e) {\n assertError(e);\n const msg = `Failed to build the docs page for entity ${stringifyEntityRef(\n entity,\n )}: ${e.message}`;\n taskLogger.error(msg);\n this.logger.error(msg, e);\n error(e);\n return;\n }\n\n // With a maximum of ~5 seconds wait, check if the files got published and if docs will be fetched\n // on the user's page. If not, respond with a message asking them to check back later.\n // The delay here is to make sure GCS/AWS/etc. registers newly uploaded files which is usually <1 second\n for (let attempt = 0; attempt < 5; attempt++) {\n if (await this.publisher.hasDocsBeenGenerated(entity)) {\n foundDocs = true;\n break;\n }\n await new Promise(r => setTimeout(r, 1000));\n }\n if (!foundDocs) {\n this.logger.error(\n 'Published files are taking longer to show up in storage. Something went wrong.',\n );\n error(\n new NotFoundError(\n 'Sorry! It took too long for the generated docs to show up in storage. Are you sure the docs project is generating an `index.html` file? Otherwise, check back later.',\n ),\n );\n return;\n }\n\n finish({ updated: true });\n }\n\n async doCacheSync({\n responseHandler: { finish },\n discovery,\n token,\n entity,\n }: {\n responseHandler: DocsSynchronizerSyncOpts;\n discovery: DiscoveryService;\n token: string | undefined;\n entity: Entity;\n }) {\n // Check if the last update check was too recent.\n if (!shouldCheckForUpdate(entity.metadata.uid!) || !this.cache) {\n finish({ updated: false });\n return;\n }\n\n // Fetch techdocs_metadata.json from the publisher and from cache.\n const baseUrl = await discovery.getBaseUrl('techdocs');\n const namespace = entity.metadata?.namespace || DEFAULT_NAMESPACE;\n const kind = entity.kind;\n const name = entity.metadata.name;\n const legacyPathCasing =\n this.config.getOptionalBoolean(\n 'techdocs.legacyUseCaseSensitiveTripletPaths',\n ) || false;\n const tripletPath = `${namespace}/${kind}/${name}`;\n const entityTripletPath = `${\n legacyPathCasing ? tripletPath : tripletPath.toLocaleLowerCase('en-US')\n }`;\n try {\n const [sourceMetadata, cachedMetadata] = await Promise.all([\n this.publisher.fetchTechDocsMetadata({ namespace, kind, name }),\n fetch(\n `${baseUrl}/static/docs/${entityTripletPath}/techdocs_metadata.json`,\n {\n headers: token ? { Authorization: `Bearer ${token}` } : {},\n },\n ).then(\n f =>\n f.json().catch(() => undefined) as ReturnType<\n PublisherBase['fetchTechDocsMetadata']\n >,\n ),\n ]);\n\n // If build timestamps differ, merge their files[] lists and invalidate all objects.\n if (sourceMetadata.build_timestamp !== cachedMetadata.build_timestamp) {\n const files = [\n ...new Set([\n ...(sourceMetadata.files || []),\n ...(cachedMetadata.files || []),\n ]),\n ].map(f => `${entityTripletPath}/${f}`);\n await this.cache.invalidateMultiple(files);\n finish({ updated: true });\n } else {\n finish({ updated: false });\n }\n } catch (e) {\n assertError(e);\n // In case of error, log and allow the user to go about their business.\n this.logger.error(\n `Error syncing cache for ${entityTripletPath}: ${e.message}`,\n );\n finish({ updated: false });\n } finally {\n // Update the last check time for the entity\n new BuildMetadataStorage(entity.metadata.uid!).setLastUpdated();\n }\n }\n}\n"],"names":["pLimit","winston","PassThrough","shouldCheckForUpdate","DocsBuilder","assertError","stringifyEntityRef","NotFoundError","DEFAULT_NAMESPACE","BuildMetadataStorage"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DocsSynchronizer.cjs.js","sources":["../../src/service/DocsSynchronizer.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n DEFAULT_NAMESPACE,\n Entity,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { NotFoundError, toError } from '@backstage/errors';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport {\n GeneratorBuilder,\n PreparerBuilder,\n PublisherBase,\n} from '@backstage/plugin-techdocs-node';\nimport pLimit, { Limit } from 'p-limit';\nimport { PassThrough } from 'node:stream';\nimport * as winston from 'winston';\nimport { TechDocsCache } from '../cache';\nimport {\n BuildMetadataStorage,\n DocsBuilder,\n shouldCheckForUpdate,\n} from '../DocsBuilder';\nimport { DiscoveryService, LoggerService } from '@backstage/backend-plugin-api';\n\nexport type DocsSynchronizerSyncOpts = {\n log: (message: string) => void;\n error: (e: Error) => void;\n finish: (result: { updated: boolean }) => void;\n};\n\nexport class DocsSynchronizer {\n private readonly publisher: PublisherBase;\n private readonly logger: LoggerService;\n private readonly buildLogTransport?: winston.transport;\n private readonly config: Config;\n private readonly scmIntegrations: ScmIntegrationRegistry;\n private readonly cache: TechDocsCache | undefined;\n private readonly buildLimiter: Limit;\n\n constructor({\n publisher,\n logger,\n buildLogTransport,\n config,\n scmIntegrations,\n cache,\n }: {\n publisher: PublisherBase;\n logger: LoggerService;\n buildLogTransport?: winston.transport;\n config: Config;\n scmIntegrations: ScmIntegrationRegistry;\n cache: TechDocsCache | undefined;\n }) {\n this.config = config;\n this.logger = logger;\n this.buildLogTransport = buildLogTransport;\n this.publisher = publisher;\n this.scmIntegrations = scmIntegrations;\n this.cache = cache;\n\n // Single host/process: limit concurrent builds up to 10 at a time.\n this.buildLimiter = pLimit(10);\n }\n\n async doSync({\n responseHandler: { log, error, finish },\n entity,\n preparers,\n generators,\n }: {\n responseHandler: DocsSynchronizerSyncOpts;\n entity: Entity;\n preparers: PreparerBuilder;\n generators: GeneratorBuilder;\n }) {\n // create a new logger to log data to the caller\n const taskLogger = winston.createLogger({\n level: process.env.LOG_LEVEL || 'info',\n format: winston.format.combine(\n winston.format.colorize(),\n winston.format.timestamp(),\n winston.format.simple(),\n ),\n defaultMeta: {},\n });\n\n // create an in-memory stream to forward logs to the event-stream\n const logStream = new PassThrough();\n logStream.on('data', async data => {\n log(data.toString().trim());\n });\n\n taskLogger.add(new winston.transports.Stream({ stream: logStream }));\n if (this.buildLogTransport) {\n taskLogger.add(this.buildLogTransport);\n }\n\n // check if the last update check was too recent\n if (!shouldCheckForUpdate(entity.metadata.uid!)) {\n finish({ updated: false });\n return;\n }\n\n let foundDocs = false;\n\n try {\n const docsBuilder = new DocsBuilder({\n preparers,\n generators,\n publisher: this.publisher,\n logger: taskLogger,\n entity,\n config: this.config,\n scmIntegrations: this.scmIntegrations,\n logStream,\n cache: this.cache,\n });\n\n const interval = setInterval(() => {\n taskLogger.info(\n 'The docs building process is taking a little bit longer to process this entity. Please bear with us.',\n );\n }, 10000);\n const updated = await this.buildLimiter(() => docsBuilder.build());\n clearInterval(interval);\n\n if (!updated) {\n finish({ updated: false });\n return;\n }\n } catch (e) {\n const buildError = toError(e);\n const msg = `Failed to build the docs page for entity ${stringifyEntityRef(\n entity,\n )}: ${buildError.message}`;\n taskLogger.error(msg);\n this.logger.error(msg, buildError);\n error(buildError);\n return;\n }\n\n // With a maximum of ~5 seconds wait, check if the files got published and if docs will be fetched\n // on the user's page. If not, respond with a message asking them to check back later.\n // The delay here is to make sure GCS/AWS/etc. registers newly uploaded files which is usually <1 second\n for (let attempt = 0; attempt < 5; attempt++) {\n if (await this.publisher.hasDocsBeenGenerated(entity)) {\n foundDocs = true;\n break;\n }\n await new Promise(r => setTimeout(r, 1000));\n }\n if (!foundDocs) {\n this.logger.error(\n 'Published files are taking longer to show up in storage. Something went wrong.',\n );\n error(\n new NotFoundError(\n 'Sorry! It took too long for the generated docs to show up in storage. Are you sure the docs project is generating an `index.html` file? Otherwise, check back later.',\n ),\n );\n return;\n }\n\n finish({ updated: true });\n }\n\n async doCacheSync({\n responseHandler: { finish },\n discovery,\n token,\n entity,\n }: {\n responseHandler: DocsSynchronizerSyncOpts;\n discovery: DiscoveryService;\n token: string | undefined;\n entity: Entity;\n }) {\n // Check if the last update check was too recent.\n if (!shouldCheckForUpdate(entity.metadata.uid!) || !this.cache) {\n finish({ updated: false });\n return;\n }\n\n // Fetch techdocs_metadata.json from the publisher and from cache.\n const baseUrl = await discovery.getBaseUrl('techdocs');\n const namespace = entity.metadata?.namespace || DEFAULT_NAMESPACE;\n const kind = entity.kind;\n const name = entity.metadata.name;\n const legacyPathCasing =\n this.config.getOptionalBoolean(\n 'techdocs.legacyUseCaseSensitiveTripletPaths',\n ) || false;\n const tripletPath = `${namespace}/${kind}/${name}`;\n const entityTripletPath = `${\n legacyPathCasing ? tripletPath : tripletPath.toLocaleLowerCase('en-US')\n }`;\n try {\n const [sourceMetadata, cachedMetadata] = await Promise.all([\n this.publisher.fetchTechDocsMetadata({ namespace, kind, name }),\n fetch(\n `${baseUrl}/static/docs/${entityTripletPath}/techdocs_metadata.json`,\n {\n headers: token ? { Authorization: `Bearer ${token}` } : {},\n },\n ).then(\n f =>\n f.json().catch(() => undefined) as ReturnType<\n PublisherBase['fetchTechDocsMetadata']\n >,\n ),\n ]);\n\n // If build timestamps differ, merge their files[] lists and invalidate all objects.\n if (sourceMetadata.build_timestamp !== cachedMetadata.build_timestamp) {\n const files = [\n ...new Set([\n ...(sourceMetadata.files || []),\n ...(cachedMetadata.files || []),\n ]),\n ].map(f => `${entityTripletPath}/${f}`);\n await this.cache.invalidateMultiple(files);\n finish({ updated: true });\n } else {\n finish({ updated: false });\n }\n } catch (e) {\n // In case of error, log and allow the user to go about their business.\n this.logger.error(\n `Error syncing cache for ${entityTripletPath}: ${toError(e).message}`,\n );\n finish({ updated: false });\n } finally {\n // Update the last check time for the entity\n new BuildMetadataStorage(entity.metadata.uid!).setLastUpdated();\n }\n }\n}\n"],"names":["pLimit","winston","PassThrough","shouldCheckForUpdate","DocsBuilder","toError","stringifyEntityRef","NotFoundError","DEFAULT_NAMESPACE","BuildMetadataStorage"],"mappings":"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"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var catalogClient = require('@backstage/catalog-client');
|
|
4
3
|
var catalogModel = require('@backstage/catalog-model');
|
|
5
4
|
var config = require('@backstage/config');
|
|
6
5
|
var errors = require('@backstage/errors');
|
|
@@ -23,13 +22,11 @@ function isOutOfTheBoxOption(opt) {
|
|
|
23
22
|
}
|
|
24
23
|
async function createRouter(options) {
|
|
25
24
|
const router = router__default.default();
|
|
26
|
-
const { publisher, config: config$1, logger, discovery, httpAuth, auth } = options;
|
|
27
|
-
const catalogClient$1 = options.catalogClient ?? new catalogClient.CatalogClient({ discoveryApi: discovery });
|
|
25
|
+
const { publisher, config: config$1, logger, discovery, httpAuth, auth, catalog } = options;
|
|
28
26
|
const docsBuildStrategy = options.docsBuildStrategy ?? DefaultDocsBuildStrategy.DefaultDocsBuildStrategy.fromConfig(config$1);
|
|
29
27
|
const buildLogTransport = options.buildLogTransport;
|
|
30
28
|
const entityLoader = new CachedEntityLoader.CachedEntityLoader({
|
|
31
|
-
|
|
32
|
-
catalog: catalogClient$1,
|
|
29
|
+
catalog,
|
|
33
30
|
cache: options.cache
|
|
34
31
|
});
|
|
35
32
|
let cache;
|
|
@@ -60,11 +57,7 @@ async function createRouter(options) {
|
|
|
60
57
|
const { kind, namespace, name } = req.params;
|
|
61
58
|
const entityName = { kind, namespace, name };
|
|
62
59
|
const credentials = await httpAuth.credentials(req);
|
|
63
|
-
const
|
|
64
|
-
onBehalfOf: credentials,
|
|
65
|
-
targetPluginId: "catalog"
|
|
66
|
-
});
|
|
67
|
-
const entity = await entityLoader.load(credentials, entityName, token);
|
|
60
|
+
const entity = await entityLoader.load(credentials, entityName);
|
|
68
61
|
if (!entity) {
|
|
69
62
|
throw new errors.NotFoundError(
|
|
70
63
|
`Unable to get metadata for '${catalogModel.stringifyEntityRef(entityName)}'`
|
|
@@ -91,11 +84,7 @@ async function createRouter(options) {
|
|
|
91
84
|
const { kind, namespace, name } = req.params;
|
|
92
85
|
const entityName = { kind, namespace, name };
|
|
93
86
|
const credentials = await httpAuth.credentials(req);
|
|
94
|
-
const
|
|
95
|
-
onBehalfOf: credentials,
|
|
96
|
-
targetPluginId: "catalog"
|
|
97
|
-
});
|
|
98
|
-
const entity = await entityLoader.load(credentials, entityName, token);
|
|
87
|
+
const entity = await entityLoader.load(credentials, entityName);
|
|
99
88
|
if (!entity) {
|
|
100
89
|
throw new errors.NotFoundError(
|
|
101
90
|
`Unable to get metadata for '${catalogModel.stringifyEntityRef(entityName)}'`
|
|
@@ -119,15 +108,11 @@ async function createRouter(options) {
|
|
|
119
108
|
router.get("/sync/:namespace/:kind/:name", async (req, res) => {
|
|
120
109
|
const { kind, namespace, name } = req.params;
|
|
121
110
|
const credentials = await httpAuth.credentials(req);
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
111
|
+
const entity = await entityLoader.load(credentials, {
|
|
112
|
+
kind,
|
|
113
|
+
namespace,
|
|
114
|
+
name
|
|
125
115
|
});
|
|
126
|
-
const entity = await entityLoader.load(
|
|
127
|
-
credentials,
|
|
128
|
-
{ kind, namespace, name },
|
|
129
|
-
token
|
|
130
|
-
);
|
|
131
116
|
if (!entity?.metadata?.uid) {
|
|
132
117
|
throw new errors.NotFoundError("Entity metadata UID missing");
|
|
133
118
|
}
|
|
@@ -175,11 +160,7 @@ async function createRouter(options) {
|
|
|
175
160
|
const credentials = await httpAuth.credentials(req, {
|
|
176
161
|
allowLimitedAccess: true
|
|
177
162
|
});
|
|
178
|
-
const
|
|
179
|
-
onBehalfOf: credentials,
|
|
180
|
-
targetPluginId: "catalog"
|
|
181
|
-
});
|
|
182
|
-
const entity = await entityLoader.load(credentials, entityName, token);
|
|
163
|
+
const entity = await entityLoader.load(credentials, entityName);
|
|
183
164
|
if (!entity) {
|
|
184
165
|
throw new errors.NotFoundError(
|
|
185
166
|
`Entity not found for ${catalogModel.stringifyEntityRef(entityName)}`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.cjs.js","sources":["../../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CatalogApi, CatalogClient } from '@backstage/catalog-client';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { Config, readDurationFromConfig } from '@backstage/config';\nimport { NotFoundError } from '@backstage/errors';\nimport {\n DocsBuildStrategy,\n GeneratorBuilder,\n getLocationForEntity,\n PreparerBuilder,\n PublisherBase,\n} from '@backstage/plugin-techdocs-node';\nimport express, { Response } from 'express';\nimport Router from 'express-promise-router';\nimport { Knex } from 'knex';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { DocsSynchronizer, DocsSynchronizerSyncOpts } from './DocsSynchronizer';\nimport { createCacheMiddleware, TechDocsCache } from '../cache';\nimport { CachedEntityLoader } from './CachedEntityLoader';\nimport { DefaultDocsBuildStrategy } from './DefaultDocsBuildStrategy';\nimport * as winston from 'winston';\nimport {\n AuthService,\n CacheService,\n DiscoveryService,\n HttpAuthService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport { durationToMilliseconds } from '@backstage/types';\n\n/**\n * Required dependencies for running TechDocs in the \"out-of-the-box\"\n * deployment configuration (prepare/generate/publish all in the Backend).\n *\n * @internal\n */\nexport type OutOfTheBoxDeploymentOptions = {\n preparers: PreparerBuilder;\n generators: GeneratorBuilder;\n publisher: PublisherBase;\n logger: LoggerService;\n discovery: DiscoveryService;\n database?: Knex; // TODO: Make database required when we're implementing database stuff.\n config: Config;\n cache: CacheService;\n docsBuildStrategy?: DocsBuildStrategy;\n buildLogTransport?: winston.transport;\n catalogClient?: CatalogApi;\n httpAuth: HttpAuthService;\n auth: AuthService;\n};\n\n/**\n * Required dependencies for running TechDocs in the \"recommended\" deployment\n * configuration (prepare/generate handled externally in CI/CD).\n *\n * @internal\n */\nexport type RecommendedDeploymentOptions = {\n publisher: PublisherBase;\n logger: LoggerService;\n discovery: DiscoveryService;\n config: Config;\n cache: CacheService;\n docsBuildStrategy?: DocsBuildStrategy;\n buildLogTransport?: winston.transport;\n catalogClient?: CatalogApi;\n httpAuth: HttpAuthService;\n auth: AuthService;\n};\n\n/**\n * One of the two deployment configurations must be provided.\n *\n * @internal\n */\nexport type RouterOptions =\n | RecommendedDeploymentOptions\n | OutOfTheBoxDeploymentOptions;\n\n/**\n * Typeguard to help createRouter() understand when we are in a \"recommended\"\n * deployment vs. when we are in an out-of-the-box deployment configuration.\n *\n * @internal\n */\nfunction isOutOfTheBoxOption(\n opt: RouterOptions,\n): opt is OutOfTheBoxDeploymentOptions {\n return (opt as OutOfTheBoxDeploymentOptions).preparers !== undefined;\n}\n\n/**\n * Creates a techdocs router.\n *\n * @internal\n */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const router = Router();\n const { publisher, config, logger, discovery, httpAuth, auth } = options;\n\n const catalogClient =\n options.catalogClient ?? new CatalogClient({ discoveryApi: discovery });\n const docsBuildStrategy =\n options.docsBuildStrategy ?? DefaultDocsBuildStrategy.fromConfig(config);\n const buildLogTransport = options.buildLogTransport;\n\n // Entities are cached to optimize the /static/docs request path, which can be called many times\n // when loading a single techdocs page.\n const entityLoader = new CachedEntityLoader({\n auth,\n catalog: catalogClient,\n cache: options.cache,\n });\n\n // Set up a cache client if configured.\n let cache: TechDocsCache | undefined;\n if (config.has('techdocs.cache.ttl')) {\n let ttlMs: number;\n if (typeof config.get('techdocs.cache.ttl') === 'number') {\n ttlMs = config.getNumber('techdocs.cache.ttl');\n } else {\n ttlMs = durationToMilliseconds(\n readDurationFromConfig(config, {\n key: 'techdocs.cache.ttl',\n }),\n );\n }\n const cacheClient = options.cache.withOptions({ defaultTtl: ttlMs });\n cache = TechDocsCache.fromConfig(config, { cache: cacheClient, logger });\n }\n\n const scmIntegrations = ScmIntegrations.fromConfig(config);\n const docsSynchronizer = new DocsSynchronizer({\n publisher,\n logger,\n buildLogTransport,\n config,\n scmIntegrations,\n cache,\n });\n\n router.get('/metadata/techdocs/:namespace/:kind/:name', async (req, res) => {\n const { kind, namespace, name } = req.params;\n const entityName = { kind, namespace, name };\n\n const credentials = await httpAuth.credentials(req);\n\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: 'catalog',\n });\n\n // Verify that the related entity exists and the current user has permission to view it.\n const entity = await entityLoader.load(credentials, entityName, token);\n\n if (!entity) {\n throw new NotFoundError(\n `Unable to get metadata for '${stringifyEntityRef(entityName)}'`,\n );\n }\n\n try {\n const techdocsMetadata = await publisher.fetchTechDocsMetadata(\n entityName,\n );\n\n res.json(techdocsMetadata);\n } catch (err) {\n logger.info(\n `Unable to get metadata for '${stringifyEntityRef(\n entityName,\n )}' with error ${err}`,\n );\n throw new NotFoundError(\n `Unable to get metadata for '${stringifyEntityRef(entityName)}'`,\n err,\n );\n }\n });\n\n router.get('/metadata/entity/:namespace/:kind/:name', async (req, res) => {\n const { kind, namespace, name } = req.params;\n const entityName = { kind, namespace, name };\n\n const credentials = await httpAuth.credentials(req);\n\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: 'catalog',\n });\n\n const entity = await entityLoader.load(credentials, entityName, token);\n\n if (!entity) {\n throw new NotFoundError(\n `Unable to get metadata for '${stringifyEntityRef(entityName)}'`,\n );\n }\n\n try {\n const locationMetadata = getLocationForEntity(entity, scmIntegrations);\n res.json({ ...entity, locationMetadata });\n } catch (err) {\n logger.info(\n `Unable to get metadata for '${stringifyEntityRef(\n entityName,\n )}' with error ${err}`,\n );\n throw new NotFoundError(\n `Unable to get metadata for '${stringifyEntityRef(entityName)}'`,\n err,\n );\n }\n });\n\n // Check if docs are the latest version and trigger rebuilds if not\n // Responds with an event-stream that closes after the build finished\n // Responds with an immediate success if rebuild not needed\n // If a build is required, responds with a success when finished\n router.get('/sync/:namespace/:kind/:name', async (req, res) => {\n const { kind, namespace, name } = req.params;\n\n const credentials = await httpAuth.credentials(req);\n\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: 'catalog',\n });\n\n const entity = await entityLoader.load(\n credentials,\n { kind, namespace, name },\n token,\n );\n\n if (!entity?.metadata?.uid) {\n throw new NotFoundError('Entity metadata UID missing');\n }\n\n const responseHandler: DocsSynchronizerSyncOpts = createEventStream(res);\n\n // By default, techdocs-backend will only try to build documentation for an entity if techdocs.builder is set to\n // 'local'. If set to 'external', it will assume that an external process (e.g. CI/CD pipeline\n // of the repository) is responsible for building and publishing documentation to the storage provider.\n // Altering the implementation of the injected docsBuildStrategy allows for more complex behaviours, based on\n // either config or the properties of the entity (e.g. annotations, labels, spec fields etc.).\n const shouldBuild = await docsBuildStrategy.shouldBuild({ entity });\n if (!shouldBuild) {\n // However, if caching is enabled, take the opportunity to check and\n // invalidate stale cache entries.\n if (cache) {\n const { token: techDocsToken } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: 'techdocs',\n });\n await docsSynchronizer.doCacheSync({\n responseHandler,\n discovery,\n token: techDocsToken,\n entity,\n });\n return;\n }\n responseHandler.finish({ updated: false });\n return;\n }\n\n // Set the synchronization and build process if \"out-of-the-box\" configuration is provided.\n if (isOutOfTheBoxOption(options)) {\n const { preparers, generators } = options;\n\n await docsSynchronizer.doSync({\n responseHandler,\n entity,\n preparers,\n generators,\n });\n return;\n }\n\n responseHandler.error(\n new Error(\n \"Invalid configuration. docsBuildStrategy.shouldBuild returned 'true', but no 'preparer' was provided to the router initialization.\",\n ),\n );\n });\n\n // Ensures that the related entity exists and the current user has permission to view it.\n if (config.getOptionalBoolean('permission.enabled')) {\n router.use(\n '/static/docs/:namespace/:kind/:name',\n async (req, _res, next) => {\n const { kind, namespace, name } = req.params;\n const entityName = { kind, namespace, name };\n\n const credentials = await httpAuth.credentials(req, {\n allowLimitedAccess: true,\n });\n\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: 'catalog',\n });\n\n const entity = await entityLoader.load(credentials, entityName, token);\n\n if (!entity) {\n throw new NotFoundError(\n `Entity not found for ${stringifyEntityRef(entityName)}`,\n );\n }\n\n next();\n },\n );\n }\n\n // If a cache manager was provided, attach the cache middleware.\n if (cache) {\n router.use(createCacheMiddleware({ logger, cache }));\n }\n\n // Route middleware which serves files from the storage set in the publisher.\n router.use('/static/docs', publisher.docsRouter());\n\n return router;\n}\n\n/**\n * Create an event-stream response that emits the events 'log', 'error', and 'finish'.\n *\n * @param res - the response to write the event-stream to\n * @returns A tuple of <log, error, finish> callbacks to emit messages. A call to 'error' or 'finish'\n * will close the event-stream.\n */\nexport function createEventStream(\n res: Response<any, any>,\n): DocsSynchronizerSyncOpts {\n // Mandatory headers and http status to keep connection open\n res.writeHead(200, {\n Connection: 'keep-alive',\n 'Cache-Control': 'no-cache',\n 'Content-Type': 'text/event-stream',\n });\n\n // client closes connection\n res.socket?.on('close', () => {\n res.end();\n });\n\n // write the event to the stream\n const send = (type: 'error' | 'finish' | 'log', data: any) => {\n res.write(`event: ${type}\\ndata: ${JSON.stringify(data)}\\n\\n`);\n\n // res.flush() is only available with the compression middleware\n if (res.flush) {\n res.flush();\n }\n };\n\n return {\n log: data => {\n send('log', data);\n },\n\n error: e => {\n send('error', e.message);\n res.end();\n },\n\n finish: result => {\n send('finish', result);\n res.end();\n },\n };\n}\n"],"names":["Router","config","catalogClient","CatalogClient","DefaultDocsBuildStrategy","CachedEntityLoader","durationToMilliseconds","readDurationFromConfig","TechDocsCache","ScmIntegrations","DocsSynchronizer","NotFoundError","stringifyEntityRef","getLocationForEntity","createCacheMiddleware"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"router.cjs.js","sources":["../../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { Config, readDurationFromConfig } from '@backstage/config';\nimport { NotFoundError } from '@backstage/errors';\nimport {\n DocsBuildStrategy,\n GeneratorBuilder,\n getLocationForEntity,\n PreparerBuilder,\n PublisherBase,\n} from '@backstage/plugin-techdocs-node';\nimport express, { Response } from 'express';\nimport Router from 'express-promise-router';\nimport { Knex } from 'knex';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { DocsSynchronizer, DocsSynchronizerSyncOpts } from './DocsSynchronizer';\nimport { createCacheMiddleware, TechDocsCache } from '../cache';\nimport { CachedEntityLoader } from './CachedEntityLoader';\nimport { DefaultDocsBuildStrategy } from './DefaultDocsBuildStrategy';\nimport * as winston from 'winston';\nimport {\n AuthService,\n CacheService,\n DiscoveryService,\n HttpAuthService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { durationToMilliseconds } from '@backstage/types';\n\n/**\n * Required dependencies for running TechDocs in the \"out-of-the-box\"\n * deployment configuration (prepare/generate/publish all in the Backend).\n *\n * @internal\n */\nexport type OutOfTheBoxDeploymentOptions = {\n preparers: PreparerBuilder;\n generators: GeneratorBuilder;\n publisher: PublisherBase;\n logger: LoggerService;\n discovery: DiscoveryService;\n database?: Knex; // TODO: Make database required when we're implementing database stuff.\n config: Config;\n cache: CacheService;\n docsBuildStrategy?: DocsBuildStrategy;\n buildLogTransport?: winston.transport;\n catalog: CatalogService;\n httpAuth: HttpAuthService;\n auth: AuthService;\n};\n\n/**\n * Required dependencies for running TechDocs in the \"recommended\" deployment\n * configuration (prepare/generate handled externally in CI/CD).\n *\n * @internal\n */\nexport type RecommendedDeploymentOptions = {\n publisher: PublisherBase;\n logger: LoggerService;\n discovery: DiscoveryService;\n config: Config;\n cache: CacheService;\n docsBuildStrategy?: DocsBuildStrategy;\n buildLogTransport?: winston.transport;\n catalog: CatalogService;\n httpAuth: HttpAuthService;\n auth: AuthService;\n};\n\n/**\n * One of the two deployment configurations must be provided.\n *\n * @internal\n */\nexport type RouterOptions =\n | RecommendedDeploymentOptions\n | OutOfTheBoxDeploymentOptions;\n\n/**\n * Typeguard to help createRouter() understand when we are in a \"recommended\"\n * deployment vs. when we are in an out-of-the-box deployment configuration.\n *\n * @internal\n */\nfunction isOutOfTheBoxOption(\n opt: RouterOptions,\n): opt is OutOfTheBoxDeploymentOptions {\n return (opt as OutOfTheBoxDeploymentOptions).preparers !== undefined;\n}\n\n/**\n * Creates a techdocs router.\n *\n * @internal\n */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const router = Router();\n const { publisher, config, logger, discovery, httpAuth, auth, catalog } =\n options;\n\n const docsBuildStrategy =\n options.docsBuildStrategy ?? DefaultDocsBuildStrategy.fromConfig(config);\n const buildLogTransport = options.buildLogTransport;\n\n // Entities are cached to optimize the /static/docs request path, which can be called many times\n // when loading a single techdocs page.\n const entityLoader = new CachedEntityLoader({\n catalog,\n cache: options.cache,\n });\n\n // Set up a cache client if configured.\n let cache: TechDocsCache | undefined;\n if (config.has('techdocs.cache.ttl')) {\n let ttlMs: number;\n if (typeof config.get('techdocs.cache.ttl') === 'number') {\n ttlMs = config.getNumber('techdocs.cache.ttl');\n } else {\n ttlMs = durationToMilliseconds(\n readDurationFromConfig(config, {\n key: 'techdocs.cache.ttl',\n }),\n );\n }\n const cacheClient = options.cache.withOptions({ defaultTtl: ttlMs });\n cache = TechDocsCache.fromConfig(config, { cache: cacheClient, logger });\n }\n\n const scmIntegrations = ScmIntegrations.fromConfig(config);\n const docsSynchronizer = new DocsSynchronizer({\n publisher,\n logger,\n buildLogTransport,\n config,\n scmIntegrations,\n cache,\n });\n\n router.get('/metadata/techdocs/:namespace/:kind/:name', async (req, res) => {\n const { kind, namespace, name } = req.params;\n const entityName = { kind, namespace, name };\n\n const credentials = await httpAuth.credentials(req);\n\n // Verify that the related entity exists and the current user has permission to view it.\n const entity = await entityLoader.load(credentials, entityName);\n\n if (!entity) {\n throw new NotFoundError(\n `Unable to get metadata for '${stringifyEntityRef(entityName)}'`,\n );\n }\n\n try {\n const techdocsMetadata = await publisher.fetchTechDocsMetadata(\n entityName,\n );\n\n res.json(techdocsMetadata);\n } catch (err) {\n logger.info(\n `Unable to get metadata for '${stringifyEntityRef(\n entityName,\n )}' with error ${err}`,\n );\n throw new NotFoundError(\n `Unable to get metadata for '${stringifyEntityRef(entityName)}'`,\n err,\n );\n }\n });\n\n router.get('/metadata/entity/:namespace/:kind/:name', async (req, res) => {\n const { kind, namespace, name } = req.params;\n const entityName = { kind, namespace, name };\n\n const credentials = await httpAuth.credentials(req);\n\n const entity = await entityLoader.load(credentials, entityName);\n\n if (!entity) {\n throw new NotFoundError(\n `Unable to get metadata for '${stringifyEntityRef(entityName)}'`,\n );\n }\n\n try {\n const locationMetadata = getLocationForEntity(entity, scmIntegrations);\n res.json({ ...entity, locationMetadata });\n } catch (err) {\n logger.info(\n `Unable to get metadata for '${stringifyEntityRef(\n entityName,\n )}' with error ${err}`,\n );\n throw new NotFoundError(\n `Unable to get metadata for '${stringifyEntityRef(entityName)}'`,\n err,\n );\n }\n });\n\n // Check if docs are the latest version and trigger rebuilds if not\n // Responds with an event-stream that closes after the build finished\n // Responds with an immediate success if rebuild not needed\n // If a build is required, responds with a success when finished\n router.get('/sync/:namespace/:kind/:name', async (req, res) => {\n const { kind, namespace, name } = req.params;\n\n const credentials = await httpAuth.credentials(req);\n\n const entity = await entityLoader.load(credentials, {\n kind,\n namespace,\n name,\n });\n\n if (!entity?.metadata?.uid) {\n throw new NotFoundError('Entity metadata UID missing');\n }\n\n const responseHandler: DocsSynchronizerSyncOpts = createEventStream(res);\n\n // By default, techdocs-backend will only try to build documentation for an entity if techdocs.builder is set to\n // 'local'. If set to 'external', it will assume that an external process (e.g. CI/CD pipeline\n // of the repository) is responsible for building and publishing documentation to the storage provider.\n // Altering the implementation of the injected docsBuildStrategy allows for more complex behaviours, based on\n // either config or the properties of the entity (e.g. annotations, labels, spec fields etc.).\n const shouldBuild = await docsBuildStrategy.shouldBuild({ entity });\n if (!shouldBuild) {\n // However, if caching is enabled, take the opportunity to check and\n // invalidate stale cache entries.\n if (cache) {\n const { token: techDocsToken } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: 'techdocs',\n });\n await docsSynchronizer.doCacheSync({\n responseHandler,\n discovery,\n token: techDocsToken,\n entity,\n });\n return;\n }\n responseHandler.finish({ updated: false });\n return;\n }\n\n // Set the synchronization and build process if \"out-of-the-box\" configuration is provided.\n if (isOutOfTheBoxOption(options)) {\n const { preparers, generators } = options;\n\n await docsSynchronizer.doSync({\n responseHandler,\n entity,\n preparers,\n generators,\n });\n return;\n }\n\n responseHandler.error(\n new Error(\n \"Invalid configuration. docsBuildStrategy.shouldBuild returned 'true', but no 'preparer' was provided to the router initialization.\",\n ),\n );\n });\n\n // Ensures that the related entity exists and the current user has permission to view it.\n if (config.getOptionalBoolean('permission.enabled')) {\n router.use(\n '/static/docs/:namespace/:kind/:name',\n async (req, _res, next) => {\n const { kind, namespace, name } = req.params;\n const entityName = { kind, namespace, name };\n\n const credentials = await httpAuth.credentials(req, {\n allowLimitedAccess: true,\n });\n\n const entity = await entityLoader.load(credentials, entityName);\n\n if (!entity) {\n throw new NotFoundError(\n `Entity not found for ${stringifyEntityRef(entityName)}`,\n );\n }\n\n next();\n },\n );\n }\n\n // If a cache manager was provided, attach the cache middleware.\n if (cache) {\n router.use(createCacheMiddleware({ logger, cache }));\n }\n\n // Route middleware which serves files from the storage set in the publisher.\n router.use('/static/docs', publisher.docsRouter());\n\n return router;\n}\n\n/**\n * Create an event-stream response that emits the events 'log', 'error', and 'finish'.\n *\n * @param res - the response to write the event-stream to\n * @returns A tuple of <log, error, finish> callbacks to emit messages. A call to 'error' or 'finish'\n * will close the event-stream.\n */\nexport function createEventStream(\n res: Response<any, any>,\n): DocsSynchronizerSyncOpts {\n // Mandatory headers and http status to keep connection open\n res.writeHead(200, {\n Connection: 'keep-alive',\n 'Cache-Control': 'no-cache',\n 'Content-Type': 'text/event-stream',\n });\n\n // client closes connection\n res.socket?.on('close', () => {\n res.end();\n });\n\n // write the event to the stream\n const send = (type: 'error' | 'finish' | 'log', data: any) => {\n res.write(`event: ${type}\\ndata: ${JSON.stringify(data)}\\n\\n`);\n\n // res.flush() is only available with the compression middleware\n if (res.flush) {\n res.flush();\n }\n };\n\n return {\n log: data => {\n send('log', data);\n },\n\n error: e => {\n send('error', e.message);\n res.end();\n },\n\n finish: result => {\n send('finish', result);\n res.end();\n },\n };\n}\n"],"names":["Router","config","DefaultDocsBuildStrategy","CachedEntityLoader","durationToMilliseconds","readDurationFromConfig","TechDocsCache","ScmIntegrations","DocsSynchronizer","NotFoundError","stringifyEntityRef","getLocationForEntity","createCacheMiddleware"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-techdocs-backend",
|
|
3
|
-
"version": "2.1.7-next.
|
|
3
|
+
"version": "2.1.7-next.2",
|
|
4
4
|
"description": "The Backstage backend plugin that renders technical documentation for your components",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "backend-plugin",
|
|
@@ -75,14 +75,14 @@
|
|
|
75
75
|
"test": "backstage-cli package test"
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"@backstage/backend-plugin-api": "1.
|
|
79
|
-
"@backstage/catalog-client": "1.14.0",
|
|
80
|
-
"@backstage/catalog-model": "1.7.
|
|
81
|
-
"@backstage/config": "1.3.
|
|
82
|
-
"@backstage/errors": "1.
|
|
83
|
-
"@backstage/integration": "2.0.0",
|
|
84
|
-
"@backstage/plugin-catalog-node": "2.
|
|
85
|
-
"@backstage/plugin-techdocs-node": "1.14.5-next.
|
|
78
|
+
"@backstage/backend-plugin-api": "1.9.0-next.2",
|
|
79
|
+
"@backstage/catalog-client": "1.14.1-next.0",
|
|
80
|
+
"@backstage/catalog-model": "1.7.8-next.0",
|
|
81
|
+
"@backstage/config": "1.3.7-next.0",
|
|
82
|
+
"@backstage/errors": "1.3.0-next.0",
|
|
83
|
+
"@backstage/integration": "2.0.1-next.0",
|
|
84
|
+
"@backstage/plugin-catalog-node": "2.2.0-next.2",
|
|
85
|
+
"@backstage/plugin-techdocs-node": "1.14.5-next.2",
|
|
86
86
|
"@backstage/types": "1.2.2",
|
|
87
87
|
"express": "^4.22.0",
|
|
88
88
|
"express-promise-router": "^4.1.0",
|
|
@@ -92,9 +92,9 @@
|
|
|
92
92
|
"winston": "^3.2.1"
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
|
-
"@backstage/backend-defaults": "0.16.1-next.
|
|
96
|
-
"@backstage/backend-test-utils": "1.11.2-next.
|
|
97
|
-
"@backstage/cli": "0.36.1-next.
|
|
95
|
+
"@backstage/backend-defaults": "0.16.1-next.2",
|
|
96
|
+
"@backstage/backend-test-utils": "1.11.2-next.2",
|
|
97
|
+
"@backstage/cli": "0.36.1-next.2",
|
|
98
98
|
"@types/express": "^4.17.6",
|
|
99
99
|
"msw": "^2.0.0",
|
|
100
100
|
"supertest": "^7.0.0"
|