@backstage/plugin-techdocs-backend 1.2.0 → 1.2.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,34 @@
1
1
  # @backstage/plugin-techdocs-backend
2
2
 
3
+ ## 1.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @backstage/backend-common@0.15.0
9
+ - @backstage/integration@1.3.0
10
+ - @backstage/plugin-techdocs-node@1.3.0
11
+ - @backstage/plugin-catalog-common@1.0.5
12
+
13
+ ## 1.2.1-next.1
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies
18
+ - @backstage/plugin-catalog-common@1.0.5-next.0
19
+ - @backstage/backend-common@0.15.0-next.1
20
+ - @backstage/integration@1.3.0-next.1
21
+ - @backstage/plugin-techdocs-node@1.3.0-next.1
22
+
23
+ ## 1.2.1-next.0
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies
28
+ - @backstage/backend-common@0.15.0-next.0
29
+ - @backstage/integration@1.3.0-next.0
30
+ - @backstage/plugin-techdocs-node@1.2.1-next.0
31
+
3
32
  ## 1.2.0
4
33
 
5
34
  ### Minor Changes
package/dist/index.cjs.js CHANGED
@@ -95,9 +95,15 @@ class DocsBuilder {
95
95
  async build() {
96
96
  var _a, _b;
97
97
  if (!this.entity.metadata.uid) {
98
- throw new Error("Trying to build documentation for entity not in software catalog");
99
- }
100
- this.logger.info(`Step 1 of 3: Preparing docs for entity ${catalogModel.stringifyEntityRef(this.entity)}`);
98
+ throw new Error(
99
+ "Trying to build documentation for entity not in software catalog"
100
+ );
101
+ }
102
+ this.logger.info(
103
+ `Step 1 of 3: Preparing docs for entity ${catalogModel.stringifyEntityRef(
104
+ this.entity
105
+ )}`
106
+ );
101
107
  let storedEtag;
102
108
  if (await this.publisher.hasDocsBeenGenerated(this.entity)) {
103
109
  try {
@@ -107,7 +113,9 @@ class DocsBuilder {
107
113
  name: this.entity.metadata.name
108
114
  })).etag;
109
115
  } catch (err) {
110
- this.logger.warn(`Unable to read techdocs_metadata.json, proceeding with fresh build, error ${err}.`);
116
+ this.logger.warn(
117
+ `Unable to read techdocs_metadata.json, proceeding with fresh build, error ${err}.`
118
+ );
111
119
  }
112
120
  }
113
121
  let preparedDir;
@@ -122,18 +130,37 @@ class DocsBuilder {
122
130
  } catch (err) {
123
131
  if (errors.isError(err) && err.name === "NotModifiedError") {
124
132
  new BuildMetadataStorage(this.entity.metadata.uid).setLastUpdated();
125
- this.logger.debug(`Docs for ${catalogModel.stringifyEntityRef(this.entity)} are unmodified. Using cache, skipping generate and prepare`);
133
+ this.logger.debug(
134
+ `Docs for ${catalogModel.stringifyEntityRef(
135
+ this.entity
136
+ )} are unmodified. Using cache, skipping generate and prepare`
137
+ );
126
138
  return false;
127
139
  }
128
140
  throw err;
129
141
  }
130
- this.logger.info(`Prepare step completed for entity ${catalogModel.stringifyEntityRef(this.entity)}, stored at ${preparedDir}`);
131
- this.logger.info(`Step 2 of 3: Generating docs for entity ${catalogModel.stringifyEntityRef(this.entity)}`);
132
- const workingDir = this.config.getOptionalString("backend.workingDirectory");
142
+ this.logger.info(
143
+ `Prepare step completed for entity ${catalogModel.stringifyEntityRef(
144
+ this.entity
145
+ )}, stored at ${preparedDir}`
146
+ );
147
+ this.logger.info(
148
+ `Step 2 of 3: Generating docs for entity ${catalogModel.stringifyEntityRef(
149
+ this.entity
150
+ )}`
151
+ );
152
+ const workingDir = this.config.getOptionalString(
153
+ "backend.workingDirectory"
154
+ );
133
155
  const tmpdirPath = workingDir || os__default["default"].tmpdir();
134
156
  const tmpdirResolvedPath = fs__default["default"].realpathSync(tmpdirPath);
135
- const outputDir = await fs__default["default"].mkdtemp(path__default["default"].join(tmpdirResolvedPath, "techdocs-tmp-"));
136
- const parsedLocationAnnotation = pluginTechdocsNode.getLocationForEntity(this.entity, this.scmIntegrations);
157
+ const outputDir = await fs__default["default"].mkdtemp(
158
+ path__default["default"].join(tmpdirResolvedPath, "techdocs-tmp-")
159
+ );
160
+ const parsedLocationAnnotation = pluginTechdocsNode.getLocationForEntity(
161
+ this.entity,
162
+ this.scmIntegrations
163
+ );
137
164
  await this.generator.run({
138
165
  inputDir: preparedDir,
139
166
  outputDir,
@@ -143,7 +170,9 @@ class DocsBuilder {
143
170
  logStream: this.logStream
144
171
  });
145
172
  if (this.preparer instanceof pluginTechdocsNode.UrlPreparer) {
146
- this.logger.debug(`Removing prepared directory ${preparedDir} since the site has been generated`);
173
+ this.logger.debug(
174
+ `Removing prepared directory ${preparedDir} since the site has been generated`
175
+ );
147
176
  try {
148
177
  fs__default["default"].remove(preparedDir);
149
178
  } catch (error) {
@@ -151,18 +180,26 @@ class DocsBuilder {
151
180
  this.logger.debug(`Error removing prepared directory ${error.message}`);
152
181
  }
153
182
  }
154
- this.logger.info(`Step 3 of 3: Publishing docs for entity ${catalogModel.stringifyEntityRef(this.entity)}`);
183
+ this.logger.info(
184
+ `Step 3 of 3: Publishing docs for entity ${catalogModel.stringifyEntityRef(
185
+ this.entity
186
+ )}`
187
+ );
155
188
  const published = await this.publisher.publish({
156
189
  entity: this.entity,
157
190
  directory: outputDir
158
191
  });
159
192
  if (this.cache && published && ((_b = published == null ? void 0 : published.objects) == null ? void 0 : _b.length)) {
160
- this.logger.debug(`Invalidating ${published.objects.length} cache objects`);
193
+ this.logger.debug(
194
+ `Invalidating ${published.objects.length} cache objects`
195
+ );
161
196
  await this.cache.invalidateMultiple(published.objects);
162
197
  }
163
198
  try {
164
199
  fs__default["default"].remove(outputDir);
165
- this.logger.debug(`Removing generated directory ${outputDir} since the site has been published`);
200
+ this.logger.debug(
201
+ `Removing generated directory ${outputDir} since the site has been published`
202
+ );
166
203
  } catch (error) {
167
204
  errors.assertError(error);
168
205
  this.logger.debug(`Error removing generated directory ${error.message}`);
@@ -197,7 +234,11 @@ class DocsSynchronizer {
197
234
  }) {
198
235
  const taskLogger = winston__namespace.createLogger({
199
236
  level: process.env.LOG_LEVEL || "info",
200
- format: winston__namespace.format.combine(winston__namespace.format.colorize(), winston__namespace.format.timestamp(), winston__namespace.format.simple()),
237
+ format: winston__namespace.format.combine(
238
+ winston__namespace.format.colorize(),
239
+ winston__namespace.format.timestamp(),
240
+ winston__namespace.format.simple()
241
+ ),
201
242
  defaultMeta: {}
202
243
  });
203
244
  const logStream = new stream.PassThrough();
@@ -244,8 +285,14 @@ class DocsSynchronizer {
244
285
  await new Promise((r) => setTimeout(r, 1e3));
245
286
  }
246
287
  if (!foundDocs) {
247
- this.logger.error("Published files are taking longer to show up in storage. Something went wrong.");
248
- error(new errors.NotFoundError("Sorry! It took too long for the generated docs to show up in storage. Check back later."));
288
+ this.logger.error(
289
+ "Published files are taking longer to show up in storage. Something went wrong."
290
+ );
291
+ error(
292
+ new errors.NotFoundError(
293
+ "Sorry! It took too long for the generated docs to show up in storage. Check back later."
294
+ )
295
+ );
249
296
  return;
250
297
  }
251
298
  finish({ updated: true });
@@ -265,15 +312,22 @@ class DocsSynchronizer {
265
312
  const namespace = ((_a = entity.metadata) == null ? void 0 : _a.namespace) || catalogModel.DEFAULT_NAMESPACE;
266
313
  const kind = entity.kind;
267
314
  const name = entity.metadata.name;
268
- const legacyPathCasing = this.config.getOptionalBoolean("techdocs.legacyUseCaseSensitiveTripletPaths") || false;
315
+ const legacyPathCasing = this.config.getOptionalBoolean(
316
+ "techdocs.legacyUseCaseSensitiveTripletPaths"
317
+ ) || false;
269
318
  const tripletPath = `${namespace}/${kind}/${name}`;
270
319
  const entityTripletPath = `${legacyPathCasing ? tripletPath : tripletPath.toLocaleLowerCase("en-US")}`;
271
320
  try {
272
321
  const [sourceMetadata, cachedMetadata] = await Promise.all([
273
322
  this.publisher.fetchTechDocsMetadata({ namespace, kind, name }),
274
- fetch__default["default"](`${baseUrl}/static/docs/${entityTripletPath}/techdocs_metadata.json`, {
275
- headers: token ? { Authorization: `Bearer ${token}` } : {}
276
- }).then((f) => f.json().catch(() => void 0))
323
+ fetch__default["default"](
324
+ `${baseUrl}/static/docs/${entityTripletPath}/techdocs_metadata.json`,
325
+ {
326
+ headers: token ? { Authorization: `Bearer ${token}` } : {}
327
+ }
328
+ ).then(
329
+ (f) => f.json().catch(() => void 0)
330
+ )
277
331
  ]);
278
332
  if (sourceMetadata.build_timestamp !== cachedMetadata.build_timestamp) {
279
333
  const files = [
@@ -289,7 +343,9 @@ class DocsSynchronizer {
289
343
  }
290
344
  } catch (e) {
291
345
  errors.assertError(e);
292
- this.logger.error(`Error syncing cache for ${entityTripletPath}: ${e.message}`);
346
+ this.logger.error(
347
+ `Error syncing cache for ${entityTripletPath}: ${e.message}`
348
+ );
293
349
  finish({ updated: false });
294
350
  } finally {
295
351
  new BuildMetadataStorage(entity.metadata.uid).setLastUpdated();
@@ -383,10 +439,17 @@ class TechDocsCache {
383
439
  return this.cache.delete(path);
384
440
  }
385
441
  async invalidateMultiple(paths) {
386
- const settled = await Promise.allSettled(paths.map((path) => this.cache.delete(path)));
387
- const rejected = settled.filter((s) => s.status === "rejected");
442
+ const settled = await Promise.allSettled(
443
+ paths.map((path) => this.cache.delete(path))
444
+ );
445
+ const rejected = settled.filter(
446
+ (s) => s.status === "rejected"
447
+ );
388
448
  if (rejected.length) {
389
- throw new CacheInvalidationError("TechDocs cache invalidation error", rejected);
449
+ throw new CacheInvalidationError(
450
+ "TechDocs cache invalidation error",
451
+ rejected
452
+ );
390
453
  }
391
454
  return settled;
392
455
  }
@@ -472,14 +535,25 @@ async function createRouter(options) {
472
535
  const token = getBearerToken(req.headers.authorization);
473
536
  const entity = await entityLoader.load(entityName, token);
474
537
  if (!entity) {
475
- throw new errors.NotFoundError(`Unable to get metadata for '${catalogModel.stringifyEntityRef(entityName)}'`);
538
+ throw new errors.NotFoundError(
539
+ `Unable to get metadata for '${catalogModel.stringifyEntityRef(entityName)}'`
540
+ );
476
541
  }
477
542
  try {
478
- const techdocsMetadata = await publisher.fetchTechDocsMetadata(entityName);
543
+ const techdocsMetadata = await publisher.fetchTechDocsMetadata(
544
+ entityName
545
+ );
479
546
  res.json(techdocsMetadata);
480
547
  } catch (err) {
481
- logger.info(`Unable to get metadata for '${catalogModel.stringifyEntityRef(entityName)}' with error ${err}`);
482
- throw new errors.NotFoundError(`Unable to get metadata for '${catalogModel.stringifyEntityRef(entityName)}'`, err);
548
+ logger.info(
549
+ `Unable to get metadata for '${catalogModel.stringifyEntityRef(
550
+ entityName
551
+ )}' with error ${err}`
552
+ );
553
+ throw new errors.NotFoundError(
554
+ `Unable to get metadata for '${catalogModel.stringifyEntityRef(entityName)}'`,
555
+ err
556
+ );
483
557
  }
484
558
  });
485
559
  router.get("/metadata/entity/:namespace/:kind/:name", async (req, res) => {
@@ -488,14 +562,23 @@ async function createRouter(options) {
488
562
  const token = getBearerToken(req.headers.authorization);
489
563
  const entity = await entityLoader.load(entityName, token);
490
564
  if (!entity) {
491
- throw new errors.NotFoundError(`Unable to get metadata for '${catalogModel.stringifyEntityRef(entityName)}'`);
565
+ throw new errors.NotFoundError(
566
+ `Unable to get metadata for '${catalogModel.stringifyEntityRef(entityName)}'`
567
+ );
492
568
  }
493
569
  try {
494
570
  const locationMetadata = pluginTechdocsNode.getLocationForEntity(entity, scmIntegrations);
495
571
  res.json({ ...entity, locationMetadata });
496
572
  } catch (err) {
497
- logger.info(`Unable to get metadata for '${catalogModel.stringifyEntityRef(entityName)}' with error ${err}`);
498
- throw new errors.NotFoundError(`Unable to get metadata for '${catalogModel.stringifyEntityRef(entityName)}'`, err);
573
+ logger.info(
574
+ `Unable to get metadata for '${catalogModel.stringifyEntityRef(
575
+ entityName
576
+ )}' with error ${err}`
577
+ );
578
+ throw new errors.NotFoundError(
579
+ `Unable to get metadata for '${catalogModel.stringifyEntityRef(entityName)}'`,
580
+ err
581
+ );
499
582
  }
500
583
  });
501
584
  router.get("/sync/:namespace/:kind/:name", async (req, res) => {
@@ -531,19 +614,28 @@ async function createRouter(options) {
531
614
  });
532
615
  return;
533
616
  }
534
- responseHandler.error(new Error("Invalid configuration. docsBuildStrategy.shouldBuild returned 'true', but no 'preparer' was provided to the router initialization."));
617
+ responseHandler.error(
618
+ new Error(
619
+ "Invalid configuration. docsBuildStrategy.shouldBuild returned 'true', but no 'preparer' was provided to the router initialization."
620
+ )
621
+ );
535
622
  });
536
623
  if (config.getOptionalBoolean("permission.enabled")) {
537
- router.use("/static/docs/:namespace/:kind/:name", async (req, _res, next) => {
538
- const { kind, namespace, name } = req.params;
539
- const entityName = { kind, namespace, name };
540
- const token = getBearerToken(req.headers.authorization);
541
- const entity = await entityLoader.load(entityName, token);
542
- if (!entity) {
543
- throw new errors.NotFoundError(`Entity not found for ${catalogModel.stringifyEntityRef(entityName)}`);
624
+ router.use(
625
+ "/static/docs/:namespace/:kind/:name",
626
+ async (req, _res, next) => {
627
+ const { kind, namespace, name } = req.params;
628
+ const entityName = { kind, namespace, name };
629
+ const token = getBearerToken(req.headers.authorization);
630
+ const entity = await entityLoader.load(entityName, token);
631
+ if (!entity) {
632
+ throw new errors.NotFoundError(
633
+ `Entity not found for ${catalogModel.stringifyEntityRef(entityName)}`
634
+ );
635
+ }
636
+ next();
544
637
  }
545
- next();
546
- });
638
+ );
547
639
  }
548
640
  if (cache) {
549
641
  router.use(createCacheMiddleware({ logger, cache }));
@@ -603,7 +695,9 @@ class DefaultTechDocsCollatorFactory {
603
695
  this.tokenManager = options.tokenManager;
604
696
  }
605
697
  static fromConfig(config, options) {
606
- const legacyPathCasing = config.getOptionalBoolean("techdocs.legacyUseCaseSensitiveTripletPaths") || false;
698
+ const legacyPathCasing = config.getOptionalBoolean(
699
+ "techdocs.legacyUseCaseSensitiveTripletPaths"
700
+ ) || false;
607
701
  return new DefaultTechDocsCollatorFactory({ ...options, legacyPathCasing });
608
702
  }
609
703
  async getCollator() {
@@ -617,74 +711,94 @@ class DefaultTechDocsCollatorFactory {
617
711
  let moreEntitiesToGet = true;
618
712
  const batchSize = this.parallelismLimit * 50;
619
713
  while (moreEntitiesToGet) {
620
- const entities = (await this.catalogClient.getEntities({
621
- filter: {
622
- "metadata.annotations.backstage.io/techdocs-ref": catalogClient.CATALOG_FILTER_EXISTS
714
+ const entities = (await this.catalogClient.getEntities(
715
+ {
716
+ filter: {
717
+ "metadata.annotations.backstage.io/techdocs-ref": catalogClient.CATALOG_FILTER_EXISTS
718
+ },
719
+ fields: [
720
+ "kind",
721
+ "namespace",
722
+ "metadata.annotations",
723
+ "metadata.name",
724
+ "metadata.title",
725
+ "metadata.namespace",
726
+ "spec.type",
727
+ "spec.lifecycle",
728
+ "relations"
729
+ ],
730
+ limit: batchSize,
731
+ offset: entitiesRetrieved
623
732
  },
624
- fields: [
625
- "kind",
626
- "namespace",
627
- "metadata.annotations",
628
- "metadata.name",
629
- "metadata.title",
630
- "metadata.namespace",
631
- "spec.type",
632
- "spec.lifecycle",
633
- "relations"
634
- ],
635
- limit: batchSize,
636
- offset: entitiesRetrieved
637
- }, { token })).items;
733
+ { token }
734
+ )).items;
638
735
  moreEntitiesToGet = entities.length === batchSize;
639
736
  entitiesRetrieved += entities.length;
640
737
  const docPromises = entities.filter((it) => {
641
738
  var _a, _b;
642
739
  return (_b = (_a = it.metadata) == null ? void 0 : _a.annotations) == null ? void 0 : _b["backstage.io/techdocs-ref"];
643
- }).map((entity) => limit(async () => {
644
- const entityInfo = DefaultTechDocsCollatorFactory.handleEntityInfoCasing(this.legacyPathCasing, {
645
- kind: entity.kind,
646
- namespace: entity.metadata.namespace || "default",
647
- name: entity.metadata.name
648
- });
649
- try {
650
- const searchIndexResponse = await fetch__default["default"](DefaultTechDocsCollatorFactory.constructDocsIndexUrl(techDocsBaseUrl, entityInfo), {
651
- headers: {
652
- Authorization: `Bearer ${token}`
740
+ }).map(
741
+ (entity) => limit(async () => {
742
+ const entityInfo = DefaultTechDocsCollatorFactory.handleEntityInfoCasing(
743
+ this.legacyPathCasing,
744
+ {
745
+ kind: entity.kind,
746
+ namespace: entity.metadata.namespace || "default",
747
+ name: entity.metadata.name
653
748
  }
654
- });
655
- const searchIndex = await Promise.race([
656
- searchIndexResponse.json(),
657
- new Promise((_resolve, reject) => {
658
- setTimeout(() => {
659
- reject("Could not parse JSON in 5 seconds.");
660
- }, 5e3);
661
- })
662
- ]);
663
- return searchIndex.docs.map((doc) => {
664
- var _a, _b, _c;
665
- return {
666
- title: unescape__default["default"](doc.title),
667
- text: unescape__default["default"](doc.text || ""),
668
- location: this.applyArgsToFormat(this.locationTemplate || "/docs/:namespace/:kind/:name/:path", {
669
- ...entityInfo,
670
- path: doc.location
671
- }),
672
- path: doc.location,
673
- ...entityInfo,
674
- entityTitle: entity.metadata.title,
675
- componentType: ((_b = (_a = entity.spec) == null ? void 0 : _a.type) == null ? void 0 : _b.toString()) || "other",
676
- lifecycle: ((_c = entity.spec) == null ? void 0 : _c.lifecycle) || "",
677
- owner: getSimpleEntityOwnerString$1(entity),
678
- authorization: {
679
- resourceRef: catalogModel.stringifyEntityRef(entity)
749
+ );
750
+ try {
751
+ const searchIndexResponse = await fetch__default["default"](
752
+ DefaultTechDocsCollatorFactory.constructDocsIndexUrl(
753
+ techDocsBaseUrl,
754
+ entityInfo
755
+ ),
756
+ {
757
+ headers: {
758
+ Authorization: `Bearer ${token}`
759
+ }
680
760
  }
681
- };
682
- });
683
- } catch (e) {
684
- this.logger.debug(`Failed to retrieve tech docs search index for entity ${entityInfo.namespace}/${entityInfo.kind}/${entityInfo.name}`, e);
685
- return [];
686
- }
687
- }));
761
+ );
762
+ const searchIndex = await Promise.race([
763
+ searchIndexResponse.json(),
764
+ new Promise((_resolve, reject) => {
765
+ setTimeout(() => {
766
+ reject("Could not parse JSON in 5 seconds.");
767
+ }, 5e3);
768
+ })
769
+ ]);
770
+ return searchIndex.docs.map((doc) => {
771
+ var _a, _b, _c;
772
+ return {
773
+ title: unescape__default["default"](doc.title),
774
+ text: unescape__default["default"](doc.text || ""),
775
+ location: this.applyArgsToFormat(
776
+ this.locationTemplate || "/docs/:namespace/:kind/:name/:path",
777
+ {
778
+ ...entityInfo,
779
+ path: doc.location
780
+ }
781
+ ),
782
+ path: doc.location,
783
+ ...entityInfo,
784
+ entityTitle: entity.metadata.title,
785
+ componentType: ((_b = (_a = entity.spec) == null ? void 0 : _a.type) == null ? void 0 : _b.toString()) || "other",
786
+ lifecycle: ((_c = entity.spec) == null ? void 0 : _c.lifecycle) || "",
787
+ owner: getSimpleEntityOwnerString$1(entity),
788
+ authorization: {
789
+ resourceRef: catalogModel.stringifyEntityRef(entity)
790
+ }
791
+ };
792
+ });
793
+ } catch (e) {
794
+ this.logger.debug(
795
+ `Failed to retrieve tech docs search index for entity ${entityInfo.namespace}/${entityInfo.kind}/${entityInfo.name}`,
796
+ e
797
+ );
798
+ return [];
799
+ }
800
+ })
801
+ );
688
802
  yield* (await Promise.all(docPromises)).flat();
689
803
  }
690
804
  }
@@ -723,7 +837,9 @@ class DefaultTechDocsCollator {
723
837
  this.visibilityPermission = pluginCatalogCommon.catalogEntityReadPermission;
724
838
  }
725
839
  static fromConfig(config, options) {
726
- const legacyPathCasing = config.getOptionalBoolean("techdocs.legacyUseCaseSensitiveTripletPaths") || false;
840
+ const legacyPathCasing = config.getOptionalBoolean(
841
+ "techdocs.legacyUseCaseSensitiveTripletPaths"
842
+ ) || false;
727
843
  return new DefaultTechDocsCollator(legacyPathCasing, options);
728
844
  }
729
845
  async execute() {
@@ -738,61 +854,81 @@ class DefaultTechDocsCollator {
738
854
  const limit = pLimit__default["default"](parallelismLimit != null ? parallelismLimit : 10);
739
855
  const techDocsBaseUrl = await discovery.getBaseUrl("techdocs");
740
856
  const { token } = await tokenManager.getToken();
741
- const entities = await (catalogClient$1 != null ? catalogClient$1 : new catalogClient.CatalogClient({ discoveryApi: discovery })).getEntities({
742
- filter: {
743
- "metadata.annotations.backstage.io/techdocs-ref": catalogClient.CATALOG_FILTER_EXISTS
857
+ const entities = await (catalogClient$1 != null ? catalogClient$1 : new catalogClient.CatalogClient({ discoveryApi: discovery })).getEntities(
858
+ {
859
+ filter: {
860
+ "metadata.annotations.backstage.io/techdocs-ref": catalogClient.CATALOG_FILTER_EXISTS
861
+ },
862
+ fields: [
863
+ "kind",
864
+ "namespace",
865
+ "metadata.annotations",
866
+ "metadata.name",
867
+ "metadata.title",
868
+ "metadata.namespace",
869
+ "spec.type",
870
+ "spec.lifecycle",
871
+ "relations"
872
+ ]
744
873
  },
745
- fields: [
746
- "kind",
747
- "namespace",
748
- "metadata.annotations",
749
- "metadata.name",
750
- "metadata.title",
751
- "metadata.namespace",
752
- "spec.type",
753
- "spec.lifecycle",
754
- "relations"
755
- ]
756
- }, { token });
757
- const docPromises = entities.items.map((entity) => limit(async () => {
758
- var _a;
759
- const entityInfo = DefaultTechDocsCollator.handleEntityInfoCasing((_a = this.legacyPathCasing) != null ? _a : false, {
760
- kind: entity.kind,
761
- namespace: entity.metadata.namespace || "default",
762
- name: entity.metadata.name
763
- });
764
- try {
765
- const searchIndexResponse = await fetch__default["default"](DefaultTechDocsCollator.constructDocsIndexUrl(techDocsBaseUrl, entityInfo), {
766
- headers: {
767
- Authorization: `Bearer ${token}`
874
+ { token }
875
+ );
876
+ const docPromises = entities.items.map(
877
+ (entity) => limit(async () => {
878
+ var _a;
879
+ const entityInfo = DefaultTechDocsCollator.handleEntityInfoCasing(
880
+ (_a = this.legacyPathCasing) != null ? _a : false,
881
+ {
882
+ kind: entity.kind,
883
+ namespace: entity.metadata.namespace || "default",
884
+ name: entity.metadata.name
768
885
  }
769
- });
770
- const searchIndex = await searchIndexResponse.json();
771
- return searchIndex.docs.map((doc) => {
772
- var _a2, _b, _c;
773
- return {
774
- title: unescape__default["default"](doc.title),
775
- text: unescape__default["default"](doc.text || ""),
776
- location: this.applyArgsToFormat(locationTemplate || "/docs/:namespace/:kind/:name/:path", {
777
- ...entityInfo,
778
- path: doc.location
779
- }),
780
- path: doc.location,
781
- ...entityInfo,
782
- entityTitle: entity.metadata.title,
783
- componentType: ((_b = (_a2 = entity.spec) == null ? void 0 : _a2.type) == null ? void 0 : _b.toString()) || "other",
784
- lifecycle: ((_c = entity.spec) == null ? void 0 : _c.lifecycle) || "",
785
- owner: getSimpleEntityOwnerString(entity),
786
- authorization: {
787
- resourceRef: catalogModel.stringifyEntityRef(entity)
886
+ );
887
+ try {
888
+ const searchIndexResponse = await fetch__default["default"](
889
+ DefaultTechDocsCollator.constructDocsIndexUrl(
890
+ techDocsBaseUrl,
891
+ entityInfo
892
+ ),
893
+ {
894
+ headers: {
895
+ Authorization: `Bearer ${token}`
896
+ }
788
897
  }
789
- };
790
- });
791
- } catch (e) {
792
- logger.debug(`Failed to retrieve tech docs search index for entity ${entityInfo.namespace}/${entityInfo.kind}/${entityInfo.name}`, e);
793
- return [];
794
- }
795
- }));
898
+ );
899
+ const searchIndex = await searchIndexResponse.json();
900
+ return searchIndex.docs.map((doc) => {
901
+ var _a2, _b, _c;
902
+ return {
903
+ title: unescape__default["default"](doc.title),
904
+ text: unescape__default["default"](doc.text || ""),
905
+ location: this.applyArgsToFormat(
906
+ locationTemplate || "/docs/:namespace/:kind/:name/:path",
907
+ {
908
+ ...entityInfo,
909
+ path: doc.location
910
+ }
911
+ ),
912
+ path: doc.location,
913
+ ...entityInfo,
914
+ entityTitle: entity.metadata.title,
915
+ componentType: ((_b = (_a2 = entity.spec) == null ? void 0 : _a2.type) == null ? void 0 : _b.toString()) || "other",
916
+ lifecycle: ((_c = entity.spec) == null ? void 0 : _c.lifecycle) || "",
917
+ owner: getSimpleEntityOwnerString(entity),
918
+ authorization: {
919
+ resourceRef: catalogModel.stringifyEntityRef(entity)
920
+ }
921
+ };
922
+ });
923
+ } catch (e) {
924
+ logger.debug(
925
+ `Failed to retrieve tech docs search index for entity ${entityInfo.namespace}/${entityInfo.kind}/${entityInfo.name}`,
926
+ e
927
+ );
928
+ return [];
929
+ }
930
+ })
931
+ );
796
932
  return (await Promise.all(docPromises)).flat();
797
933
  }
798
934
  applyArgsToFormat(format, args) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/DocsBuilder/BuildMetadataStorage.ts","../src/DocsBuilder/builder.ts","../src/service/DocsSynchronizer.ts","../src/cache/cacheMiddleware.ts","../src/cache/TechDocsCache.ts","../src/service/CachedEntityLoader.ts","../src/service/DocsBuildStrategy.ts","../src/service/router.ts","../src/search/DefaultTechDocsCollatorFactory.ts","../src/search/DefaultTechDocsCollator.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\n// Entity uid: unix timestamp\nconst lastUpdatedRecord = {} as Record<string, number>;\n\n/**\n * Store timestamps of the most recent TechDocs update of each Entity. This is\n * used to avoid checking for an update on each and every request to TechDocs.\n */\nexport class BuildMetadataStorage {\n private entityUid: string;\n private lastUpdatedRecord: Record<string, number>;\n\n constructor(entityUid: string) {\n this.entityUid = entityUid;\n this.lastUpdatedRecord = lastUpdatedRecord;\n }\n\n setLastUpdated(): void {\n this.lastUpdatedRecord[this.entityUid] = Date.now();\n }\n\n getLastUpdated(): number | undefined {\n return this.lastUpdatedRecord[this.entityUid];\n }\n}\n\n/**\n * Return false if a check for update has happened in last 60 seconds.\n */\nexport const shouldCheckForUpdate = (entityUid: string) => {\n const lastUpdated = new BuildMetadataStorage(entityUid).getLastUpdated();\n if (lastUpdated) {\n // The difference is in milliseconds\n if (Date.now() - lastUpdated < 60 * 1000) {\n return false;\n }\n }\n return true;\n};\n","/*\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 Entity,\n DEFAULT_NAMESPACE,\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 UrlPreparer,\n} from '@backstage/plugin-techdocs-node';\nimport fs from 'fs-extra';\nimport os from 'os';\nimport path from 'path';\nimport { Writable } from '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;\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 const 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 });\n\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 (this.preparer instanceof UrlPreparer) {\n this.logger.debug(\n `Removing prepared directory ${preparedDir} since the site has been generated`,\n );\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(`Error removing prepared directory ${error.message}`);\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\n try {\n // Not a blocker hence no need to await this.\n fs.remove(outputDir);\n this.logger.debug(\n `Removing generated directory ${outputDir} since the site has been published`,\n );\n } catch (error) {\n assertError(error);\n this.logger.debug(`Error removing generated directory ${error.message}`);\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","/*\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 { PluginEndpointDiscovery } from '@backstage/backend-common';\nimport { Entity, DEFAULT_NAMESPACE } 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 fetch from 'node-fetch';\nimport pLimit, { Limit } from 'p-limit';\nimport { PassThrough } from 'stream';\nimport * as winston from 'winston';\nimport { TechDocsCache } from '../cache';\nimport {\n BuildMetadataStorage,\n DocsBuilder,\n shouldCheckForUpdate,\n} from '../DocsBuilder';\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: winston.Logger;\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: winston.Logger;\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 taskLogger.add(this.buildLogTransport);\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 updated = await this.buildLimiter(() => docsBuilder.build());\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: ${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. 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: PluginEndpointDiscovery;\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","/*\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 { Router } from 'express';\nimport router from 'express-promise-router';\nimport { Logger } from 'winston';\nimport { TechDocsCache } from './TechDocsCache';\n\ntype CacheMiddlewareOptions = {\n cache: TechDocsCache;\n logger: Logger;\n};\n\ntype ErrorCallback = (err?: Error) => void;\n\nexport const createCacheMiddleware = ({\n cache,\n}: CacheMiddlewareOptions): Router => {\n const cacheMiddleware = router();\n\n // Middleware that, through socket monkey patching, captures responses as\n // they're sent over /static/docs/* and caches them. Subsequent requests are\n // loaded from cache. Cache key is the object's path (after `/static/docs/`).\n cacheMiddleware.use(async (req, res, next) => {\n const socket = res.socket;\n const isCacheable = req.path.startsWith('/static/docs/');\n const isGetRequest = req.method === 'GET';\n\n // Continue early if this is non-cacheable, or there's no socket.\n if (!isCacheable || !socket) {\n next();\n return;\n }\n\n // Make concrete references to these things.\n const reqPath = decodeURI(req.path.match(/\\/static\\/docs\\/(.*)$/)![1]);\n const realEnd = socket.end.bind(socket);\n const realWrite = socket.write.bind(socket);\n let writeToCache = true;\n const chunks: Buffer[] = [];\n\n // Monkey-patch the response's socket to keep track of chunks as they are\n // written over the wire.\n socket.write = (\n data: string | Uint8Array,\n encoding?: BufferEncoding | ErrorCallback,\n callback?: ErrorCallback,\n ) => {\n chunks.push(Buffer.from(data));\n if (typeof encoding === 'function') {\n return realWrite(data, encoding);\n }\n return realWrite(data, encoding, callback);\n };\n\n // When a socket is closed, if there were no errors and the data written\n // over the socket should be cached, cache it!\n socket.on('close', async hadError => {\n const content = Buffer.concat(chunks);\n const head = content.toString('utf8', 0, 12);\n if (\n isGetRequest &&\n writeToCache &&\n !hadError &&\n head.match(/HTTP\\/\\d\\.\\d 200/)\n ) {\n await cache.set(reqPath, content);\n }\n });\n\n // Attempt to retrieve data from the cache.\n const cached = await cache.get(reqPath);\n\n // If there is a cache hit, write it out on the socket, ensure we don't re-\n // cache the data, and prevent going back to canonical storage by never\n // calling next().\n if (cached) {\n writeToCache = false;\n realEnd(cached);\n return;\n }\n\n // No data retrieved from cache: allow retrieval from canonical storage.\n next();\n });\n\n return cacheMiddleware;\n};\n","/*\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 { CacheClient } from '@backstage/backend-common';\nimport { assertError, CustomErrorBase } from '@backstage/errors';\nimport { Config } from '@backstage/config';\nimport { Logger } from 'winston';\n\nexport class CacheInvalidationError extends CustomErrorBase {}\n\nexport class TechDocsCache {\n protected readonly cache: CacheClient;\n protected readonly logger: Logger;\n protected readonly readTimeout: number;\n\n private constructor({\n cache,\n logger,\n readTimeout,\n }: {\n cache: CacheClient;\n logger: Logger;\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: CacheClient; logger: Logger },\n ) {\n const timeout = config.getOptionalNumber('techdocs.cache.readTimeout');\n const readTimeout = timeout === undefined ? 1000 : timeout;\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 assertError(e);\n this.logger.warn(`Error getting cache entry ${path}: ${e.message}`);\n this.logger.debug(e.stack);\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","/*\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 */\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { CacheClient } from '@backstage/backend-common';\nimport {\n Entity,\n CompoundEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\n\nexport type CachedEntityLoaderOptions = {\n catalog: CatalogClient;\n cache: CacheClient;\n};\n\nexport class CachedEntityLoader {\n private readonly catalog: CatalogClient;\n private readonly cache: CacheClient;\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 entityRef: CompoundEntityRef,\n token: string | undefined,\n ): Promise<Entity | undefined> {\n const cacheKey = this.getCacheKey(entityRef, token);\n let result = await this.getFromCache(cacheKey);\n\n if (result) {\n return result;\n }\n\n result = await this.catalog.getEntityByRef(entityRef, { token });\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 token: string | undefined,\n ): string {\n const key = ['catalog', stringifyEntityRef(entityName)];\n\n if (token) {\n key.push(token);\n }\n\n return key.join(':');\n }\n}\n","/*\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 */\nimport { Entity } from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\n\n/**\n * Parameters passed to the shouldBuild method on the DocsBuildStrategy interface\n *\n * @public\n */\nexport type ShouldBuildParameters = {\n entity: Entity;\n};\n\n/**\n * A strategy for when to build TechDocs locally, and when to skip building TechDocs (allowing for an external build)\n *\n * @public\n */\nexport interface DocsBuildStrategy {\n shouldBuild(params: ShouldBuildParameters): Promise<boolean>;\n}\n\nexport class DefaultDocsBuildStrategy {\n private readonly config: Config;\n\n private constructor(config: Config) {\n this.config = config;\n }\n\n static fromConfig(config: Config): DefaultDocsBuildStrategy {\n return new DefaultDocsBuildStrategy(config);\n }\n\n async shouldBuild(_: ShouldBuildParameters): Promise<boolean> {\n return this.config.getString('techdocs.builder') === 'local';\n }\n}\n","/*\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 PluginEndpointDiscovery,\n PluginCacheManager,\n} from '@backstage/backend-common';\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { NotFoundError } from '@backstage/errors';\nimport {\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 {\n DefaultDocsBuildStrategy,\n DocsBuildStrategy,\n} from './DocsBuildStrategy';\nimport * as winston from 'winston';\nimport { PassThrough } from 'stream';\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 * @public\n */\nexport type OutOfTheBoxDeploymentOptions = {\n preparers: PreparerBuilder;\n generators: GeneratorBuilder;\n publisher: PublisherBase;\n logger: winston.Logger;\n discovery: PluginEndpointDiscovery;\n database?: Knex; // TODO: Make database required when we're implementing database stuff.\n config: Config;\n cache: PluginCacheManager;\n docsBuildStrategy?: DocsBuildStrategy;\n buildLogTransport?: winston.transport;\n};\n\n/**\n * Required dependencies for running TechDocs in the \"recommended\" deployment\n * configuration (prepare/generate handled externally in CI/CD).\n *\n * @public\n */\nexport type RecommendedDeploymentOptions = {\n publisher: PublisherBase;\n logger: winston.Logger;\n discovery: PluginEndpointDiscovery;\n config: Config;\n cache: PluginCacheManager;\n docsBuildStrategy?: DocsBuildStrategy;\n buildLogTransport?: winston.transport;\n};\n\n/**\n * One of the two deployment configurations must be provided.\n *\n * @public\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 * * @public\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 * @public\n */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const router = Router();\n const { publisher, config, logger, discovery } = options;\n const catalogClient = new CatalogClient({ discoveryApi: discovery });\n const docsBuildStrategy =\n options.docsBuildStrategy ?? DefaultDocsBuildStrategy.fromConfig(config);\n const buildLogTransport =\n options.buildLogTransport ??\n new winston.transports.Stream({ stream: new PassThrough() });\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: catalogClient,\n cache: options.cache.getClient(),\n });\n\n // Set up a cache client if configured.\n let cache: TechDocsCache | undefined;\n const defaultTtl = config.getOptionalNumber('techdocs.cache.ttl');\n if (defaultTtl) {\n const cacheClient = options.cache.getClient({ defaultTtl });\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 const token = getBearerToken(req.headers.authorization);\n\n // Verify that the related entity exists and the current user has permission to view it.\n const entity = await entityLoader.load(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 const token = getBearerToken(req.headers.authorization);\n\n const entity = await entityLoader.load(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 const token = getBearerToken(req.headers.authorization);\n\n const entity = await entityLoader.load({ kind, namespace, name }, token);\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 await docsSynchronizer.doCacheSync({\n responseHandler,\n discovery,\n token,\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 const token = getBearerToken(req.headers.authorization);\n\n const entity = await entityLoader.load(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\nfunction getBearerToken(header?: string): string | undefined {\n return header?.match(/(?:Bearer)\\s+(\\S+)/i)?.[1];\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","/*\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 PluginEndpointDiscovery,\n TokenManager,\n} from '@backstage/backend-common';\nimport {\n CatalogApi,\n CatalogClient,\n CATALOG_FILTER_EXISTS,\n} from '@backstage/catalog-client';\nimport {\n Entity,\n parseEntityRef,\n RELATION_OWNED_BY,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { catalogEntityReadPermission } from '@backstage/plugin-catalog-common';\nimport { Permission } from '@backstage/plugin-permission-common';\nimport { DocumentCollatorFactory } from '@backstage/plugin-search-common';\nimport { TechDocsDocument } from '@backstage/plugin-techdocs-node';\nimport unescape from 'lodash/unescape';\nimport fetch from 'node-fetch';\nimport pLimit from 'p-limit';\nimport { Readable } from 'stream';\nimport { Logger } from 'winston';\n\ninterface MkSearchIndexDoc {\n title: string;\n text: string;\n location: string;\n}\n\n/**\n * Options to configure the TechDocs collator factory\n *\n * @public\n */\nexport type TechDocsCollatorFactoryOptions = {\n discovery: PluginEndpointDiscovery;\n logger: Logger;\n tokenManager: TokenManager;\n locationTemplate?: string;\n catalogClient?: CatalogApi;\n parallelismLimit?: number;\n legacyPathCasing?: boolean;\n};\n\ntype EntityInfo = {\n name: string;\n namespace: string;\n kind: string;\n};\n\n/**\n * A search collator factory responsible for gathering and transforming\n * TechDocs documents.\n *\n * @public\n */\nexport class DefaultTechDocsCollatorFactory implements DocumentCollatorFactory {\n public readonly type: string = 'techdocs';\n public readonly visibilityPermission: Permission =\n catalogEntityReadPermission;\n\n private discovery: PluginEndpointDiscovery;\n private locationTemplate: string;\n private readonly logger: Logger;\n private readonly catalogClient: CatalogApi;\n private readonly tokenManager: TokenManager;\n private readonly parallelismLimit: number;\n private readonly legacyPathCasing: boolean;\n\n private constructor(options: TechDocsCollatorFactoryOptions) {\n this.discovery = options.discovery;\n this.locationTemplate =\n options.locationTemplate || '/docs/:namespace/:kind/:name/:path';\n this.logger = options.logger;\n this.catalogClient =\n options.catalogClient ||\n new CatalogClient({ discoveryApi: options.discovery });\n this.parallelismLimit = options.parallelismLimit ?? 10;\n this.legacyPathCasing = options.legacyPathCasing ?? false;\n this.tokenManager = options.tokenManager;\n }\n\n static fromConfig(config: Config, options: TechDocsCollatorFactoryOptions) {\n const legacyPathCasing =\n config.getOptionalBoolean(\n 'techdocs.legacyUseCaseSensitiveTripletPaths',\n ) || false;\n return new DefaultTechDocsCollatorFactory({ ...options, legacyPathCasing });\n }\n\n async getCollator(): Promise<Readable> {\n return Readable.from(this.execute());\n }\n\n private async *execute(): AsyncGenerator<TechDocsDocument, void, undefined> {\n const limit = pLimit(this.parallelismLimit);\n const techDocsBaseUrl = await this.discovery.getBaseUrl('techdocs');\n const { token } = await this.tokenManager.getToken();\n let entitiesRetrieved = 0;\n let moreEntitiesToGet = true;\n\n // Offset/limit pagination is used on the Catalog Client in order to\n // limit (and allow some control over) memory used by the search backend\n // at index-time. The batchSize is calculated as a factor of the given\n // parallelism limit to simplify configuration.\n const batchSize = this.parallelismLimit * 50;\n while (moreEntitiesToGet) {\n const entities = (\n await this.catalogClient.getEntities(\n {\n filter: {\n 'metadata.annotations.backstage.io/techdocs-ref':\n CATALOG_FILTER_EXISTS,\n },\n fields: [\n 'kind',\n 'namespace',\n 'metadata.annotations',\n 'metadata.name',\n 'metadata.title',\n 'metadata.namespace',\n 'spec.type',\n 'spec.lifecycle',\n 'relations',\n ],\n limit: batchSize,\n offset: entitiesRetrieved,\n },\n { token },\n )\n ).items;\n\n // Control looping through entity batches.\n moreEntitiesToGet = entities.length === batchSize;\n entitiesRetrieved += entities.length;\n\n const docPromises = entities\n .filter(it => it.metadata?.annotations?.['backstage.io/techdocs-ref'])\n .map((entity: Entity) =>\n limit(async (): Promise<TechDocsDocument[]> => {\n const entityInfo =\n DefaultTechDocsCollatorFactory.handleEntityInfoCasing(\n this.legacyPathCasing,\n {\n kind: entity.kind,\n namespace: entity.metadata.namespace || 'default',\n name: entity.metadata.name,\n },\n );\n\n try {\n const searchIndexResponse = await fetch(\n DefaultTechDocsCollatorFactory.constructDocsIndexUrl(\n techDocsBaseUrl,\n entityInfo,\n ),\n {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n },\n );\n\n // todo(@backstage/techdocs-core): remove Promise.race() when node-fetch is 3.x+\n // workaround for fetch().json() hanging in node-fetch@2.x.x, fixed in 3.x.x\n // https://github.com/node-fetch/node-fetch/issues/665\n const searchIndex = await Promise.race([\n searchIndexResponse.json(),\n new Promise((_resolve, reject) => {\n setTimeout(() => {\n reject('Could not parse JSON in 5 seconds.');\n }, 5000);\n }),\n ]);\n\n return searchIndex.docs.map((doc: MkSearchIndexDoc) => ({\n title: unescape(doc.title),\n text: unescape(doc.text || ''),\n location: this.applyArgsToFormat(\n this.locationTemplate || '/docs/:namespace/:kind/:name/:path',\n {\n ...entityInfo,\n path: doc.location,\n },\n ),\n path: doc.location,\n ...entityInfo,\n entityTitle: entity.metadata.title,\n componentType: entity.spec?.type?.toString() || 'other',\n lifecycle: (entity.spec?.lifecycle as string) || '',\n owner: getSimpleEntityOwnerString(entity),\n authorization: {\n resourceRef: stringifyEntityRef(entity),\n },\n }));\n } catch (e) {\n this.logger.debug(\n `Failed to retrieve tech docs search index for entity ${entityInfo.namespace}/${entityInfo.kind}/${entityInfo.name}`,\n e,\n );\n return [];\n }\n }),\n );\n yield* (await Promise.all(docPromises)).flat();\n }\n }\n\n private applyArgsToFormat(\n format: string,\n args: Record<string, string>,\n ): string {\n let formatted = format;\n for (const [key, value] of Object.entries(args)) {\n formatted = formatted.replace(`:${key}`, value);\n }\n return formatted;\n }\n\n private static constructDocsIndexUrl(\n techDocsBaseUrl: string,\n entityInfo: { kind: string; namespace: string; name: string },\n ) {\n return `${techDocsBaseUrl}/static/docs/${entityInfo.namespace}/${entityInfo.kind}/${entityInfo.name}/search/search_index.json`;\n }\n\n private static handleEntityInfoCasing(\n legacyPaths: boolean,\n entityInfo: EntityInfo,\n ): EntityInfo {\n return legacyPaths\n ? entityInfo\n : Object.entries(entityInfo).reduce((acc, [key, value]) => {\n return { ...acc, [key]: value.toLocaleLowerCase('en-US') };\n }, {} as EntityInfo);\n }\n}\n\nfunction getSimpleEntityOwnerString(entity: Entity): string {\n if (entity.relations) {\n const owner = entity.relations.find(r => r.type === RELATION_OWNED_BY);\n if (owner) {\n const { name } = parseEntityRef(owner.targetRef);\n return name;\n }\n }\n return '';\n}\n","/*\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 PluginEndpointDiscovery,\n TokenManager,\n} from '@backstage/backend-common';\nimport {\n Entity,\n parseEntityRef,\n RELATION_OWNED_BY,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport fetch from 'node-fetch';\nimport unescape from 'lodash/unescape';\nimport { Logger } from 'winston';\nimport pLimit from 'p-limit';\nimport { Config } from '@backstage/config';\nimport { catalogEntityReadPermission } from '@backstage/plugin-catalog-common';\nimport { Permission } from '@backstage/plugin-permission-common';\nimport {\n CatalogApi,\n CatalogClient,\n CATALOG_FILTER_EXISTS,\n} from '@backstage/catalog-client';\nimport { TechDocsDocument } from '@backstage/plugin-techdocs-node';\n\ninterface MkSearchIndexDoc {\n title: string;\n text: string;\n location: string;\n}\n\n/**\n * Options to configure the TechDocs collator\n *\n * @public\n */\nexport type TechDocsCollatorOptions = {\n discovery: PluginEndpointDiscovery;\n logger: Logger;\n tokenManager: TokenManager;\n locationTemplate?: string;\n catalogClient?: CatalogApi;\n parallelismLimit?: number;\n legacyPathCasing?: boolean;\n};\n\ntype EntityInfo = {\n name: string;\n namespace: string;\n kind: string;\n};\n\n/**\n * A search collator responsible for gathering and transforming TechDocs documents.\n *\n * @public\n * @deprecated Upgrade to a more recent `@backstage/plugin-search-backend-node` and\n * use `DefaultTechDocsCollatorFactory` instead.\n */\nexport class DefaultTechDocsCollator {\n public readonly type: string = 'techdocs';\n public readonly visibilityPermission: Permission =\n catalogEntityReadPermission;\n\n private constructor(\n private readonly legacyPathCasing: boolean,\n private readonly options: TechDocsCollatorOptions,\n ) {}\n\n static fromConfig(config: Config, options: TechDocsCollatorOptions) {\n const legacyPathCasing =\n config.getOptionalBoolean(\n 'techdocs.legacyUseCaseSensitiveTripletPaths',\n ) || false;\n return new DefaultTechDocsCollator(legacyPathCasing, options);\n }\n\n async execute() {\n const {\n parallelismLimit,\n discovery,\n tokenManager,\n catalogClient,\n locationTemplate,\n logger,\n } = this.options;\n const limit = pLimit(parallelismLimit ?? 10);\n const techDocsBaseUrl = await discovery.getBaseUrl('techdocs');\n const { token } = await tokenManager.getToken();\n const entities = await (\n catalogClient ?? new CatalogClient({ discoveryApi: discovery })\n ).getEntities(\n {\n filter: {\n 'metadata.annotations.backstage.io/techdocs-ref':\n CATALOG_FILTER_EXISTS,\n },\n fields: [\n 'kind',\n 'namespace',\n 'metadata.annotations',\n 'metadata.name',\n 'metadata.title',\n 'metadata.namespace',\n 'spec.type',\n 'spec.lifecycle',\n 'relations',\n ],\n },\n { token },\n );\n const docPromises = entities.items.map((entity: Entity) =>\n limit(async (): Promise<TechDocsDocument[]> => {\n const entityInfo = DefaultTechDocsCollator.handleEntityInfoCasing(\n this.legacyPathCasing ?? false,\n {\n kind: entity.kind,\n namespace: entity.metadata.namespace || 'default',\n name: entity.metadata.name,\n },\n );\n\n try {\n const searchIndexResponse = await fetch(\n DefaultTechDocsCollator.constructDocsIndexUrl(\n techDocsBaseUrl,\n entityInfo,\n ),\n {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n },\n );\n const searchIndex = await searchIndexResponse.json();\n\n return searchIndex.docs.map((doc: MkSearchIndexDoc) => ({\n title: unescape(doc.title),\n text: unescape(doc.text || ''),\n location: this.applyArgsToFormat(\n locationTemplate || '/docs/:namespace/:kind/:name/:path',\n {\n ...entityInfo,\n path: doc.location,\n },\n ),\n path: doc.location,\n ...entityInfo,\n entityTitle: entity.metadata.title,\n componentType: entity.spec?.type?.toString() || 'other',\n lifecycle: (entity.spec?.lifecycle as string) || '',\n owner: getSimpleEntityOwnerString(entity),\n authorization: {\n resourceRef: stringifyEntityRef(entity),\n },\n }));\n } catch (e) {\n logger.debug(\n `Failed to retrieve tech docs search index for entity ${entityInfo.namespace}/${entityInfo.kind}/${entityInfo.name}`,\n e,\n );\n return [];\n }\n }),\n );\n return (await Promise.all(docPromises)).flat();\n }\n\n protected applyArgsToFormat(\n format: string,\n args: Record<string, string>,\n ): string {\n let formatted = format;\n for (const [key, value] of Object.entries(args)) {\n formatted = formatted.replace(`:${key}`, value);\n }\n return formatted;\n }\n\n private static constructDocsIndexUrl(\n techDocsBaseUrl: string,\n entityInfo: { kind: string; namespace: string; name: string },\n ) {\n return `${techDocsBaseUrl}/static/docs/${entityInfo.namespace}/${entityInfo.kind}/${entityInfo.name}/search/search_index.json`;\n }\n\n private static handleEntityInfoCasing(\n legacyPaths: boolean,\n entityInfo: EntityInfo,\n ): EntityInfo {\n return legacyPaths\n ? entityInfo\n : Object.entries(entityInfo).reduce((acc, [key, value]) => {\n return { ...acc, [key]: value.toLocaleLowerCase('en-US') };\n }, {} as EntityInfo);\n }\n}\n\nfunction getSimpleEntityOwnerString(entity: Entity): string {\n if (entity.relations) {\n const owner = entity.relations.find(r => r.type === RELATION_OWNED_BY);\n if (owner) {\n const { name } = parseEntityRef(owner.targetRef);\n return name;\n }\n }\n return '';\n}\n"],"names":["stringifyEntityRef","DEFAULT_NAMESPACE","isError","os","fs","path","getLocationForEntity","UrlPreparer","assertError","pLimit","winston","PassThrough","NotFoundError","fetch","router","CustomErrorBase","Router","catalogClient","CatalogClient","ScmIntegrations","catalogEntityReadPermission","Readable","CATALOG_FILTER_EXISTS","unescape","getSimpleEntityOwnerString","RELATION_OWNED_BY","parseEntityRef"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/DocsBuilder/BuildMetadataStorage.ts","../src/DocsBuilder/builder.ts","../src/service/DocsSynchronizer.ts","../src/cache/cacheMiddleware.ts","../src/cache/TechDocsCache.ts","../src/service/CachedEntityLoader.ts","../src/service/DocsBuildStrategy.ts","../src/service/router.ts","../src/search/DefaultTechDocsCollatorFactory.ts","../src/search/DefaultTechDocsCollator.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\n// Entity uid: unix timestamp\nconst lastUpdatedRecord = {} as Record<string, number>;\n\n/**\n * Store timestamps of the most recent TechDocs update of each Entity. This is\n * used to avoid checking for an update on each and every request to TechDocs.\n */\nexport class BuildMetadataStorage {\n private entityUid: string;\n private lastUpdatedRecord: Record<string, number>;\n\n constructor(entityUid: string) {\n this.entityUid = entityUid;\n this.lastUpdatedRecord = lastUpdatedRecord;\n }\n\n setLastUpdated(): void {\n this.lastUpdatedRecord[this.entityUid] = Date.now();\n }\n\n getLastUpdated(): number | undefined {\n return this.lastUpdatedRecord[this.entityUid];\n }\n}\n\n/**\n * Return false if a check for update has happened in last 60 seconds.\n */\nexport const shouldCheckForUpdate = (entityUid: string) => {\n const lastUpdated = new BuildMetadataStorage(entityUid).getLastUpdated();\n if (lastUpdated) {\n // The difference is in milliseconds\n if (Date.now() - lastUpdated < 60 * 1000) {\n return false;\n }\n }\n return true;\n};\n","/*\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 Entity,\n DEFAULT_NAMESPACE,\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 UrlPreparer,\n} from '@backstage/plugin-techdocs-node';\nimport fs from 'fs-extra';\nimport os from 'os';\nimport path from 'path';\nimport { Writable } from '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;\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 const 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 });\n\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 (this.preparer instanceof UrlPreparer) {\n this.logger.debug(\n `Removing prepared directory ${preparedDir} since the site has been generated`,\n );\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(`Error removing prepared directory ${error.message}`);\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\n try {\n // Not a blocker hence no need to await this.\n fs.remove(outputDir);\n this.logger.debug(\n `Removing generated directory ${outputDir} since the site has been published`,\n );\n } catch (error) {\n assertError(error);\n this.logger.debug(`Error removing generated directory ${error.message}`);\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","/*\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 { PluginEndpointDiscovery } from '@backstage/backend-common';\nimport { Entity, DEFAULT_NAMESPACE } 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 fetch from 'node-fetch';\nimport pLimit, { Limit } from 'p-limit';\nimport { PassThrough } from 'stream';\nimport * as winston from 'winston';\nimport { TechDocsCache } from '../cache';\nimport {\n BuildMetadataStorage,\n DocsBuilder,\n shouldCheckForUpdate,\n} from '../DocsBuilder';\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: winston.Logger;\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: winston.Logger;\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 taskLogger.add(this.buildLogTransport);\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 updated = await this.buildLimiter(() => docsBuilder.build());\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: ${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. 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: PluginEndpointDiscovery;\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","/*\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 { Router } from 'express';\nimport router from 'express-promise-router';\nimport { Logger } from 'winston';\nimport { TechDocsCache } from './TechDocsCache';\n\ntype CacheMiddlewareOptions = {\n cache: TechDocsCache;\n logger: Logger;\n};\n\ntype ErrorCallback = (err?: Error) => void;\n\nexport const createCacheMiddleware = ({\n cache,\n}: CacheMiddlewareOptions): Router => {\n const cacheMiddleware = router();\n\n // Middleware that, through socket monkey patching, captures responses as\n // they're sent over /static/docs/* and caches them. Subsequent requests are\n // loaded from cache. Cache key is the object's path (after `/static/docs/`).\n cacheMiddleware.use(async (req, res, next) => {\n const socket = res.socket;\n const isCacheable = req.path.startsWith('/static/docs/');\n const isGetRequest = req.method === 'GET';\n\n // Continue early if this is non-cacheable, or there's no socket.\n if (!isCacheable || !socket) {\n next();\n return;\n }\n\n // Make concrete references to these things.\n const reqPath = decodeURI(req.path.match(/\\/static\\/docs\\/(.*)$/)![1]);\n const realEnd = socket.end.bind(socket);\n const realWrite = socket.write.bind(socket);\n let writeToCache = true;\n const chunks: Buffer[] = [];\n\n // Monkey-patch the response's socket to keep track of chunks as they are\n // written over the wire.\n socket.write = (\n data: string | Uint8Array,\n encoding?: BufferEncoding | ErrorCallback,\n callback?: ErrorCallback,\n ) => {\n chunks.push(Buffer.from(data));\n if (typeof encoding === 'function') {\n return realWrite(data, encoding);\n }\n return realWrite(data, encoding, callback);\n };\n\n // When a socket is closed, if there were no errors and the data written\n // over the socket should be cached, cache it!\n socket.on('close', async hadError => {\n const content = Buffer.concat(chunks);\n const head = content.toString('utf8', 0, 12);\n if (\n isGetRequest &&\n writeToCache &&\n !hadError &&\n head.match(/HTTP\\/\\d\\.\\d 200/)\n ) {\n await cache.set(reqPath, content);\n }\n });\n\n // Attempt to retrieve data from the cache.\n const cached = await cache.get(reqPath);\n\n // If there is a cache hit, write it out on the socket, ensure we don't re-\n // cache the data, and prevent going back to canonical storage by never\n // calling next().\n if (cached) {\n writeToCache = false;\n realEnd(cached);\n return;\n }\n\n // No data retrieved from cache: allow retrieval from canonical storage.\n next();\n });\n\n return cacheMiddleware;\n};\n","/*\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 { CacheClient } from '@backstage/backend-common';\nimport { assertError, CustomErrorBase } from '@backstage/errors';\nimport { Config } from '@backstage/config';\nimport { Logger } from 'winston';\n\nexport class CacheInvalidationError extends CustomErrorBase {}\n\nexport class TechDocsCache {\n protected readonly cache: CacheClient;\n protected readonly logger: Logger;\n protected readonly readTimeout: number;\n\n private constructor({\n cache,\n logger,\n readTimeout,\n }: {\n cache: CacheClient;\n logger: Logger;\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: CacheClient; logger: Logger },\n ) {\n const timeout = config.getOptionalNumber('techdocs.cache.readTimeout');\n const readTimeout = timeout === undefined ? 1000 : timeout;\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 assertError(e);\n this.logger.warn(`Error getting cache entry ${path}: ${e.message}`);\n this.logger.debug(e.stack);\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","/*\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 */\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { CacheClient } from '@backstage/backend-common';\nimport {\n Entity,\n CompoundEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\n\nexport type CachedEntityLoaderOptions = {\n catalog: CatalogClient;\n cache: CacheClient;\n};\n\nexport class CachedEntityLoader {\n private readonly catalog: CatalogClient;\n private readonly cache: CacheClient;\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 entityRef: CompoundEntityRef,\n token: string | undefined,\n ): Promise<Entity | undefined> {\n const cacheKey = this.getCacheKey(entityRef, token);\n let result = await this.getFromCache(cacheKey);\n\n if (result) {\n return result;\n }\n\n result = await this.catalog.getEntityByRef(entityRef, { token });\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 token: string | undefined,\n ): string {\n const key = ['catalog', stringifyEntityRef(entityName)];\n\n if (token) {\n key.push(token);\n }\n\n return key.join(':');\n }\n}\n","/*\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 */\nimport { Entity } from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\n\n/**\n * Parameters passed to the shouldBuild method on the DocsBuildStrategy interface\n *\n * @public\n */\nexport type ShouldBuildParameters = {\n entity: Entity;\n};\n\n/**\n * A strategy for when to build TechDocs locally, and when to skip building TechDocs (allowing for an external build)\n *\n * @public\n */\nexport interface DocsBuildStrategy {\n shouldBuild(params: ShouldBuildParameters): Promise<boolean>;\n}\n\nexport class DefaultDocsBuildStrategy {\n private readonly config: Config;\n\n private constructor(config: Config) {\n this.config = config;\n }\n\n static fromConfig(config: Config): DefaultDocsBuildStrategy {\n return new DefaultDocsBuildStrategy(config);\n }\n\n async shouldBuild(_: ShouldBuildParameters): Promise<boolean> {\n return this.config.getString('techdocs.builder') === 'local';\n }\n}\n","/*\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 PluginEndpointDiscovery,\n PluginCacheManager,\n} from '@backstage/backend-common';\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { NotFoundError } from '@backstage/errors';\nimport {\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 {\n DefaultDocsBuildStrategy,\n DocsBuildStrategy,\n} from './DocsBuildStrategy';\nimport * as winston from 'winston';\nimport { PassThrough } from 'stream';\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 * @public\n */\nexport type OutOfTheBoxDeploymentOptions = {\n preparers: PreparerBuilder;\n generators: GeneratorBuilder;\n publisher: PublisherBase;\n logger: winston.Logger;\n discovery: PluginEndpointDiscovery;\n database?: Knex; // TODO: Make database required when we're implementing database stuff.\n config: Config;\n cache: PluginCacheManager;\n docsBuildStrategy?: DocsBuildStrategy;\n buildLogTransport?: winston.transport;\n};\n\n/**\n * Required dependencies for running TechDocs in the \"recommended\" deployment\n * configuration (prepare/generate handled externally in CI/CD).\n *\n * @public\n */\nexport type RecommendedDeploymentOptions = {\n publisher: PublisherBase;\n logger: winston.Logger;\n discovery: PluginEndpointDiscovery;\n config: Config;\n cache: PluginCacheManager;\n docsBuildStrategy?: DocsBuildStrategy;\n buildLogTransport?: winston.transport;\n};\n\n/**\n * One of the two deployment configurations must be provided.\n *\n * @public\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 * * @public\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 * @public\n */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const router = Router();\n const { publisher, config, logger, discovery } = options;\n const catalogClient = new CatalogClient({ discoveryApi: discovery });\n const docsBuildStrategy =\n options.docsBuildStrategy ?? DefaultDocsBuildStrategy.fromConfig(config);\n const buildLogTransport =\n options.buildLogTransport ??\n new winston.transports.Stream({ stream: new PassThrough() });\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: catalogClient,\n cache: options.cache.getClient(),\n });\n\n // Set up a cache client if configured.\n let cache: TechDocsCache | undefined;\n const defaultTtl = config.getOptionalNumber('techdocs.cache.ttl');\n if (defaultTtl) {\n const cacheClient = options.cache.getClient({ defaultTtl });\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 const token = getBearerToken(req.headers.authorization);\n\n // Verify that the related entity exists and the current user has permission to view it.\n const entity = await entityLoader.load(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 const token = getBearerToken(req.headers.authorization);\n\n const entity = await entityLoader.load(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 const token = getBearerToken(req.headers.authorization);\n\n const entity = await entityLoader.load({ kind, namespace, name }, token);\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 await docsSynchronizer.doCacheSync({\n responseHandler,\n discovery,\n token,\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 const token = getBearerToken(req.headers.authorization);\n\n const entity = await entityLoader.load(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\nfunction getBearerToken(header?: string): string | undefined {\n return header?.match(/(?:Bearer)\\s+(\\S+)/i)?.[1];\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","/*\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 PluginEndpointDiscovery,\n TokenManager,\n} from '@backstage/backend-common';\nimport {\n CatalogApi,\n CatalogClient,\n CATALOG_FILTER_EXISTS,\n} from '@backstage/catalog-client';\nimport {\n Entity,\n parseEntityRef,\n RELATION_OWNED_BY,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { catalogEntityReadPermission } from '@backstage/plugin-catalog-common';\nimport { Permission } from '@backstage/plugin-permission-common';\nimport { DocumentCollatorFactory } from '@backstage/plugin-search-common';\nimport { TechDocsDocument } from '@backstage/plugin-techdocs-node';\nimport unescape from 'lodash/unescape';\nimport fetch from 'node-fetch';\nimport pLimit from 'p-limit';\nimport { Readable } from 'stream';\nimport { Logger } from 'winston';\n\ninterface MkSearchIndexDoc {\n title: string;\n text: string;\n location: string;\n}\n\n/**\n * Options to configure the TechDocs collator factory\n *\n * @public\n */\nexport type TechDocsCollatorFactoryOptions = {\n discovery: PluginEndpointDiscovery;\n logger: Logger;\n tokenManager: TokenManager;\n locationTemplate?: string;\n catalogClient?: CatalogApi;\n parallelismLimit?: number;\n legacyPathCasing?: boolean;\n};\n\ntype EntityInfo = {\n name: string;\n namespace: string;\n kind: string;\n};\n\n/**\n * A search collator factory responsible for gathering and transforming\n * TechDocs documents.\n *\n * @public\n */\nexport class DefaultTechDocsCollatorFactory implements DocumentCollatorFactory {\n public readonly type: string = 'techdocs';\n public readonly visibilityPermission: Permission =\n catalogEntityReadPermission;\n\n private discovery: PluginEndpointDiscovery;\n private locationTemplate: string;\n private readonly logger: Logger;\n private readonly catalogClient: CatalogApi;\n private readonly tokenManager: TokenManager;\n private readonly parallelismLimit: number;\n private readonly legacyPathCasing: boolean;\n\n private constructor(options: TechDocsCollatorFactoryOptions) {\n this.discovery = options.discovery;\n this.locationTemplate =\n options.locationTemplate || '/docs/:namespace/:kind/:name/:path';\n this.logger = options.logger;\n this.catalogClient =\n options.catalogClient ||\n new CatalogClient({ discoveryApi: options.discovery });\n this.parallelismLimit = options.parallelismLimit ?? 10;\n this.legacyPathCasing = options.legacyPathCasing ?? false;\n this.tokenManager = options.tokenManager;\n }\n\n static fromConfig(config: Config, options: TechDocsCollatorFactoryOptions) {\n const legacyPathCasing =\n config.getOptionalBoolean(\n 'techdocs.legacyUseCaseSensitiveTripletPaths',\n ) || false;\n return new DefaultTechDocsCollatorFactory({ ...options, legacyPathCasing });\n }\n\n async getCollator(): Promise<Readable> {\n return Readable.from(this.execute());\n }\n\n private async *execute(): AsyncGenerator<TechDocsDocument, void, undefined> {\n const limit = pLimit(this.parallelismLimit);\n const techDocsBaseUrl = await this.discovery.getBaseUrl('techdocs');\n const { token } = await this.tokenManager.getToken();\n let entitiesRetrieved = 0;\n let moreEntitiesToGet = true;\n\n // Offset/limit pagination is used on the Catalog Client in order to\n // limit (and allow some control over) memory used by the search backend\n // at index-time. The batchSize is calculated as a factor of the given\n // parallelism limit to simplify configuration.\n const batchSize = this.parallelismLimit * 50;\n while (moreEntitiesToGet) {\n const entities = (\n await this.catalogClient.getEntities(\n {\n filter: {\n 'metadata.annotations.backstage.io/techdocs-ref':\n CATALOG_FILTER_EXISTS,\n },\n fields: [\n 'kind',\n 'namespace',\n 'metadata.annotations',\n 'metadata.name',\n 'metadata.title',\n 'metadata.namespace',\n 'spec.type',\n 'spec.lifecycle',\n 'relations',\n ],\n limit: batchSize,\n offset: entitiesRetrieved,\n },\n { token },\n )\n ).items;\n\n // Control looping through entity batches.\n moreEntitiesToGet = entities.length === batchSize;\n entitiesRetrieved += entities.length;\n\n const docPromises = entities\n .filter(it => it.metadata?.annotations?.['backstage.io/techdocs-ref'])\n .map((entity: Entity) =>\n limit(async (): Promise<TechDocsDocument[]> => {\n const entityInfo =\n DefaultTechDocsCollatorFactory.handleEntityInfoCasing(\n this.legacyPathCasing,\n {\n kind: entity.kind,\n namespace: entity.metadata.namespace || 'default',\n name: entity.metadata.name,\n },\n );\n\n try {\n const searchIndexResponse = await fetch(\n DefaultTechDocsCollatorFactory.constructDocsIndexUrl(\n techDocsBaseUrl,\n entityInfo,\n ),\n {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n },\n );\n\n // todo(@backstage/techdocs-core): remove Promise.race() when node-fetch is 3.x+\n // workaround for fetch().json() hanging in node-fetch@2.x.x, fixed in 3.x.x\n // https://github.com/node-fetch/node-fetch/issues/665\n const searchIndex = await Promise.race([\n searchIndexResponse.json(),\n new Promise((_resolve, reject) => {\n setTimeout(() => {\n reject('Could not parse JSON in 5 seconds.');\n }, 5000);\n }),\n ]);\n\n return searchIndex.docs.map((doc: MkSearchIndexDoc) => ({\n title: unescape(doc.title),\n text: unescape(doc.text || ''),\n location: this.applyArgsToFormat(\n this.locationTemplate || '/docs/:namespace/:kind/:name/:path',\n {\n ...entityInfo,\n path: doc.location,\n },\n ),\n path: doc.location,\n ...entityInfo,\n entityTitle: entity.metadata.title,\n componentType: entity.spec?.type?.toString() || 'other',\n lifecycle: (entity.spec?.lifecycle as string) || '',\n owner: getSimpleEntityOwnerString(entity),\n authorization: {\n resourceRef: stringifyEntityRef(entity),\n },\n }));\n } catch (e) {\n this.logger.debug(\n `Failed to retrieve tech docs search index for entity ${entityInfo.namespace}/${entityInfo.kind}/${entityInfo.name}`,\n e,\n );\n return [];\n }\n }),\n );\n yield* (await Promise.all(docPromises)).flat();\n }\n }\n\n private applyArgsToFormat(\n format: string,\n args: Record<string, string>,\n ): string {\n let formatted = format;\n for (const [key, value] of Object.entries(args)) {\n formatted = formatted.replace(`:${key}`, value);\n }\n return formatted;\n }\n\n private static constructDocsIndexUrl(\n techDocsBaseUrl: string,\n entityInfo: { kind: string; namespace: string; name: string },\n ) {\n return `${techDocsBaseUrl}/static/docs/${entityInfo.namespace}/${entityInfo.kind}/${entityInfo.name}/search/search_index.json`;\n }\n\n private static handleEntityInfoCasing(\n legacyPaths: boolean,\n entityInfo: EntityInfo,\n ): EntityInfo {\n return legacyPaths\n ? entityInfo\n : Object.entries(entityInfo).reduce((acc, [key, value]) => {\n return { ...acc, [key]: value.toLocaleLowerCase('en-US') };\n }, {} as EntityInfo);\n }\n}\n\nfunction getSimpleEntityOwnerString(entity: Entity): string {\n if (entity.relations) {\n const owner = entity.relations.find(r => r.type === RELATION_OWNED_BY);\n if (owner) {\n const { name } = parseEntityRef(owner.targetRef);\n return name;\n }\n }\n return '';\n}\n","/*\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 PluginEndpointDiscovery,\n TokenManager,\n} from '@backstage/backend-common';\nimport {\n Entity,\n parseEntityRef,\n RELATION_OWNED_BY,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport fetch from 'node-fetch';\nimport unescape from 'lodash/unescape';\nimport { Logger } from 'winston';\nimport pLimit from 'p-limit';\nimport { Config } from '@backstage/config';\nimport { catalogEntityReadPermission } from '@backstage/plugin-catalog-common';\nimport { Permission } from '@backstage/plugin-permission-common';\nimport {\n CatalogApi,\n CatalogClient,\n CATALOG_FILTER_EXISTS,\n} from '@backstage/catalog-client';\nimport { TechDocsDocument } from '@backstage/plugin-techdocs-node';\n\ninterface MkSearchIndexDoc {\n title: string;\n text: string;\n location: string;\n}\n\n/**\n * Options to configure the TechDocs collator\n *\n * @public\n */\nexport type TechDocsCollatorOptions = {\n discovery: PluginEndpointDiscovery;\n logger: Logger;\n tokenManager: TokenManager;\n locationTemplate?: string;\n catalogClient?: CatalogApi;\n parallelismLimit?: number;\n legacyPathCasing?: boolean;\n};\n\ntype EntityInfo = {\n name: string;\n namespace: string;\n kind: string;\n};\n\n/**\n * A search collator responsible for gathering and transforming TechDocs documents.\n *\n * @public\n * @deprecated Upgrade to a more recent `@backstage/plugin-search-backend-node` and\n * use `DefaultTechDocsCollatorFactory` instead.\n */\nexport class DefaultTechDocsCollator {\n public readonly type: string = 'techdocs';\n public readonly visibilityPermission: Permission =\n catalogEntityReadPermission;\n\n private constructor(\n private readonly legacyPathCasing: boolean,\n private readonly options: TechDocsCollatorOptions,\n ) {}\n\n static fromConfig(config: Config, options: TechDocsCollatorOptions) {\n const legacyPathCasing =\n config.getOptionalBoolean(\n 'techdocs.legacyUseCaseSensitiveTripletPaths',\n ) || false;\n return new DefaultTechDocsCollator(legacyPathCasing, options);\n }\n\n async execute() {\n const {\n parallelismLimit,\n discovery,\n tokenManager,\n catalogClient,\n locationTemplate,\n logger,\n } = this.options;\n const limit = pLimit(parallelismLimit ?? 10);\n const techDocsBaseUrl = await discovery.getBaseUrl('techdocs');\n const { token } = await tokenManager.getToken();\n const entities = await (\n catalogClient ?? new CatalogClient({ discoveryApi: discovery })\n ).getEntities(\n {\n filter: {\n 'metadata.annotations.backstage.io/techdocs-ref':\n CATALOG_FILTER_EXISTS,\n },\n fields: [\n 'kind',\n 'namespace',\n 'metadata.annotations',\n 'metadata.name',\n 'metadata.title',\n 'metadata.namespace',\n 'spec.type',\n 'spec.lifecycle',\n 'relations',\n ],\n },\n { token },\n );\n const docPromises = entities.items.map((entity: Entity) =>\n limit(async (): Promise<TechDocsDocument[]> => {\n const entityInfo = DefaultTechDocsCollator.handleEntityInfoCasing(\n this.legacyPathCasing ?? false,\n {\n kind: entity.kind,\n namespace: entity.metadata.namespace || 'default',\n name: entity.metadata.name,\n },\n );\n\n try {\n const searchIndexResponse = await fetch(\n DefaultTechDocsCollator.constructDocsIndexUrl(\n techDocsBaseUrl,\n entityInfo,\n ),\n {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n },\n );\n const searchIndex = await searchIndexResponse.json();\n\n return searchIndex.docs.map((doc: MkSearchIndexDoc) => ({\n title: unescape(doc.title),\n text: unescape(doc.text || ''),\n location: this.applyArgsToFormat(\n locationTemplate || '/docs/:namespace/:kind/:name/:path',\n {\n ...entityInfo,\n path: doc.location,\n },\n ),\n path: doc.location,\n ...entityInfo,\n entityTitle: entity.metadata.title,\n componentType: entity.spec?.type?.toString() || 'other',\n lifecycle: (entity.spec?.lifecycle as string) || '',\n owner: getSimpleEntityOwnerString(entity),\n authorization: {\n resourceRef: stringifyEntityRef(entity),\n },\n }));\n } catch (e) {\n logger.debug(\n `Failed to retrieve tech docs search index for entity ${entityInfo.namespace}/${entityInfo.kind}/${entityInfo.name}`,\n e,\n );\n return [];\n }\n }),\n );\n return (await Promise.all(docPromises)).flat();\n }\n\n protected applyArgsToFormat(\n format: string,\n args: Record<string, string>,\n ): string {\n let formatted = format;\n for (const [key, value] of Object.entries(args)) {\n formatted = formatted.replace(`:${key}`, value);\n }\n return formatted;\n }\n\n private static constructDocsIndexUrl(\n techDocsBaseUrl: string,\n entityInfo: { kind: string; namespace: string; name: string },\n ) {\n return `${techDocsBaseUrl}/static/docs/${entityInfo.namespace}/${entityInfo.kind}/${entityInfo.name}/search/search_index.json`;\n }\n\n private static handleEntityInfoCasing(\n legacyPaths: boolean,\n entityInfo: EntityInfo,\n ): EntityInfo {\n return legacyPaths\n ? entityInfo\n : Object.entries(entityInfo).reduce((acc, [key, value]) => {\n return { ...acc, [key]: value.toLocaleLowerCase('en-US') };\n }, {} as EntityInfo);\n }\n}\n\nfunction getSimpleEntityOwnerString(entity: Entity): string {\n if (entity.relations) {\n const owner = entity.relations.find(r => r.type === RELATION_OWNED_BY);\n if (owner) {\n const { name } = parseEntityRef(owner.targetRef);\n return name;\n }\n }\n return '';\n}\n"],"names":["stringifyEntityRef","DEFAULT_NAMESPACE","isError","os","fs","path","getLocationForEntity","UrlPreparer","assertError","pLimit","winston","PassThrough","NotFoundError","fetch","router","CustomErrorBase","Router","catalogClient","CatalogClient","ScmIntegrations","catalogEntityReadPermission","Readable","CATALOG_FILTER_EXISTS","unescape","getSimpleEntityOwnerString","RELATION_OWNED_BY","parseEntityRef"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/plugin-techdocs-backend",
3
3
  "description": "The Backstage backend plugin that renders technical documentation for your components",
4
- "version": "1.2.0",
4
+ "version": "1.2.1",
5
5
  "main": "dist/index.cjs.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "Apache-2.0",
@@ -34,16 +34,16 @@
34
34
  "clean": "backstage-cli package clean"
35
35
  },
36
36
  "dependencies": {
37
- "@backstage/backend-common": "^0.14.1",
37
+ "@backstage/backend-common": "^0.15.0",
38
38
  "@backstage/catalog-client": "^1.0.4",
39
39
  "@backstage/catalog-model": "^1.1.0",
40
40
  "@backstage/config": "^1.0.1",
41
41
  "@backstage/errors": "^1.1.0",
42
- "@backstage/integration": "^1.2.2",
43
- "@backstage/plugin-catalog-common": "^1.0.4",
42
+ "@backstage/integration": "^1.3.0",
43
+ "@backstage/plugin-catalog-common": "^1.0.5",
44
44
  "@backstage/plugin-permission-common": "^0.6.3",
45
45
  "@backstage/plugin-search-common": "^1.0.0",
46
- "@backstage/plugin-techdocs-node": "^1.2.0",
46
+ "@backstage/plugin-techdocs-node": "^1.3.0",
47
47
  "@types/express": "^4.17.6",
48
48
  "dockerode": "^3.3.1",
49
49
  "express": "^4.17.1",
@@ -56,9 +56,9 @@
56
56
  "winston": "^3.2.1"
57
57
  },
58
58
  "devDependencies": {
59
- "@backstage/backend-test-utils": "^0.1.26",
60
- "@backstage/cli": "^0.18.0",
61
- "@backstage/plugin-search-backend-node": "1.0.0",
59
+ "@backstage/backend-test-utils": "^0.1.27",
60
+ "@backstage/cli": "^0.18.1",
61
+ "@backstage/plugin-search-backend-node": "1.0.1",
62
62
  "@types/dockerode": "^3.3.0",
63
63
  "msw": "^0.44.0",
64
64
  "supertest": "^6.1.3"
@@ -68,5 +68,5 @@
68
68
  "config.d.ts"
69
69
  ],
70
70
  "configSchema": "config.d.ts",
71
- "gitHead": "999878d8f1ae30f6a15925816af2016cb9d717a1"
71
+ "gitHead": "a12f6269e3bf224aa7f52475be9152bc52addeed"
72
72
  }