@backstage/plugin-catalog-backend 3.5.1-next.0 → 3.5.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,26 +1,10 @@
1
1
  # @backstage/plugin-catalog-backend
2
2
 
3
- ## 3.5.1-next.0
3
+ ## 3.5.1
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - 375b546: Fixed a deadlock in the catalog processing loop that occurred when running multiple replicas. The `getProcessableEntities` method used `SELECT ... FOR UPDATE SKIP LOCKED` to prevent concurrent processors from claiming the same rows, but the call was not wrapped in a transaction, so the row locks were released before the subsequent `UPDATE` executed. This allowed multiple replicas to select and update overlapping rows, causing PostgreSQL deadlock errors (code 40P01).
8
- - 79453c0: Updated dependency `wait-for-expect` to `^4.0.0`.
9
- - Updated dependencies
10
- - @backstage/backend-plugin-api@1.8.1-next.0
11
- - @backstage/plugin-permission-node@0.10.12-next.0
12
- - @backstage/backend-openapi-utils@0.6.8-next.0
13
- - @backstage/plugin-catalog-node@2.1.1-next.0
14
- - @backstage/plugin-events-node@0.4.21-next.0
15
- - @backstage/catalog-client@1.14.0
16
- - @backstage/catalog-model@1.7.7
17
- - @backstage/config@1.3.6
18
- - @backstage/errors@1.2.7
19
- - @backstage/filter-predicates@0.1.1
20
- - @backstage/integration@2.0.0
21
- - @backstage/types@1.2.2
22
- - @backstage/plugin-catalog-common@1.1.8
23
- - @backstage/plugin-permission-common@0.9.7
7
+ - 99729e9: Improved path validation for catalog entity placeholders.
24
8
 
25
9
  ## 3.5.0
26
10
 
@@ -76,13 +76,9 @@ class DefaultCatalogProcessingEngine {
76
76
  pollingIntervalMs: this.pollingIntervalMs,
77
77
  loadTasks: async (count) => {
78
78
  try {
79
- const { items } = await this.processingDatabase.transaction(
80
- async (tx) => {
81
- return this.processingDatabase.getProcessableEntities(tx, {
82
- processBatchSize: count
83
- });
84
- }
85
- );
79
+ const { items } = await this.processingDatabase.getProcessableEntities(this.knex, {
80
+ processBatchSize: count
81
+ });
86
82
  return items;
87
83
  } catch (error) {
88
84
  this.logger.warn("Failed to load processing items", error);
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultCatalogProcessingEngine.cjs.js","sources":["../../src/processing/DefaultCatalogProcessingEngine.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n ANNOTATION_LOCATION,\n Entity,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { assertError, serializeError, stringifyError } from '@backstage/errors';\nimport { Hash } from 'node:crypto';\nimport stableStringify from 'fast-json-stable-stringify';\nimport { Knex } from 'knex';\nimport { trace } from '@opentelemetry/api';\nimport { ProcessingDatabase, RefreshStateItem } from '../database/types';\nimport { createCounterMetric, createSummaryMetric } from '../util/metrics';\nimport { CatalogProcessingOrchestrator, EntityProcessingResult } from './types';\nimport { Stitcher, stitchingStrategyFromConfig } from '../stitching/types';\nimport { startTaskPipeline } from './TaskPipeline';\nimport { Config } from '@backstage/config';\nimport {\n addEntityAttributes,\n TRACER_ID,\n withActiveSpan,\n} from '../util/opentelemetry';\nimport { deleteOrphanedEntities } from '../database/operations/util/deleteOrphanedEntities';\nimport { EventsService } from '@backstage/plugin-events-node';\nimport { CATALOG_ERRORS_TOPIC } from '../constants';\nimport { LoggerService, SchedulerService } from '@backstage/backend-plugin-api';\nimport { MetricsService } from '@backstage/backend-plugin-api/alpha';\n\nconst CACHE_TTL = 5;\n\nconst tracer = trace.getTracer(TRACER_ID);\n\nexport type ProgressTracker = ReturnType<typeof progressTracker>;\n\nconst stableStringifyArray = (arr: any[]) => {\n const sorted = arr.map(stableStringify).sort();\n return `[${sorted.join(',')}]`;\n};\n\n// NOTE(freben): Perhaps surprisingly, this class does not implement the\n// CatalogProcessingEngine type. That type is externally visible and its name is\n// the way it is for historic reasons. This class has no particular reason to\n// implement that precise interface; nowadays there are several different\n// engines \"hiding\" behind the CatalogProcessingEngine interface, of which this\n// is just one.\nexport class DefaultCatalogProcessingEngine {\n private readonly config: Config;\n private readonly scheduler: SchedulerService;\n private readonly logger: LoggerService;\n private readonly knex: Knex;\n private readonly processingDatabase: ProcessingDatabase;\n private readonly orchestrator: CatalogProcessingOrchestrator;\n private readonly stitcher: Stitcher;\n private readonly createHash: () => Hash;\n private readonly pollingIntervalMs: number;\n private readonly orphanCleanupIntervalMs: number;\n private readonly onProcessingError?: (event: {\n unprocessedEntity: Entity;\n errors: Error[];\n }) => Promise<void> | void;\n private readonly tracker: ProgressTracker;\n private readonly events: EventsService;\n\n private stopFunc?: () => void;\n\n constructor(options: {\n config: Config;\n scheduler: SchedulerService;\n logger: LoggerService;\n knex: Knex;\n processingDatabase: ProcessingDatabase;\n orchestrator: CatalogProcessingOrchestrator;\n stitcher: Stitcher;\n createHash: () => Hash;\n pollingIntervalMs?: number;\n orphanCleanupIntervalMs?: number;\n onProcessingError?: (event: {\n unprocessedEntity: Entity;\n errors: Error[];\n }) => Promise<void> | void;\n tracker?: ProgressTracker;\n events: EventsService;\n metrics: MetricsService;\n }) {\n this.config = options.config;\n this.scheduler = options.scheduler;\n this.logger = options.logger;\n this.knex = options.knex;\n this.processingDatabase = options.processingDatabase;\n this.orchestrator = options.orchestrator;\n this.stitcher = options.stitcher;\n this.createHash = options.createHash;\n this.pollingIntervalMs = options.pollingIntervalMs ?? 1_000;\n this.orphanCleanupIntervalMs = options.orphanCleanupIntervalMs ?? 30_000;\n this.onProcessingError = options.onProcessingError;\n this.tracker = options.tracker ?? progressTracker(options.metrics);\n this.events = options.events;\n\n this.stopFunc = undefined;\n }\n\n async start() {\n if (this.stopFunc) {\n throw new Error('Processing engine is already started');\n }\n\n const stopPipeline = this.startPipeline();\n const stopCleanup = this.startOrphanCleanup();\n\n this.stopFunc = () => {\n stopPipeline();\n stopCleanup();\n };\n }\n\n async stop() {\n if (this.stopFunc) {\n this.stopFunc();\n this.stopFunc = undefined;\n }\n }\n\n private startPipeline(): () => void {\n return startTaskPipeline<RefreshStateItem>({\n lowWatermark: 5,\n highWatermark: 10,\n pollingIntervalMs: this.pollingIntervalMs,\n loadTasks: async count => {\n try {\n const { items } = await this.processingDatabase.transaction(\n async tx => {\n return this.processingDatabase.getProcessableEntities(tx, {\n processBatchSize: count,\n });\n },\n );\n return items;\n } catch (error) {\n this.logger.warn('Failed to load processing items', error);\n return [];\n }\n },\n processTask: async item => {\n await withActiveSpan(tracer, 'ProcessingRun', async span => {\n const track = this.tracker.processStart(item, this.logger);\n addEntityAttributes(span, item.unprocessedEntity);\n\n try {\n const {\n id,\n state,\n unprocessedEntity,\n entityRef,\n locationKey,\n resultHash: previousResultHash,\n } = item;\n const result = await this.orchestrator.process({\n entity: unprocessedEntity,\n state,\n });\n\n track.markProcessorsCompleted(result);\n\n if (result.ok) {\n const { ttl: _, ...stateWithoutTtl } = state ?? {};\n if (\n stableStringify(stateWithoutTtl) !==\n stableStringify(result.state)\n ) {\n await this.processingDatabase.transaction(async tx => {\n await this.processingDatabase.updateEntityCache(tx, {\n id,\n state: {\n ttl: CACHE_TTL,\n ...result.state,\n },\n });\n });\n }\n } else {\n const maybeTtl = state?.ttl;\n const ttl = Number.isInteger(maybeTtl) ? (maybeTtl as number) : 0;\n await this.processingDatabase.transaction(async tx => {\n await this.processingDatabase.updateEntityCache(tx, {\n id,\n state: ttl > 0 ? { ...state, ttl: ttl - 1 } : {},\n });\n });\n }\n\n const location =\n unprocessedEntity?.metadata?.annotations?.[ANNOTATION_LOCATION];\n if (result.errors.length) {\n this.events.publish({\n topic: CATALOG_ERRORS_TOPIC,\n eventPayload: {\n entity: entityRef,\n location,\n errors: result.errors,\n },\n });\n }\n const errorsString = JSON.stringify(\n result.errors.map(e => serializeError(e)),\n );\n\n let hashBuilder = this.createHash().update(errorsString);\n\n if (result.ok) {\n const { entityRefs: parents } =\n await this.processingDatabase.transaction(tx =>\n this.processingDatabase.listParents(tx, {\n entityRefs: [\n entityRef,\n ...result.deferredEntities.map(e =>\n stringifyEntityRef(e.entity),\n ),\n ],\n }),\n );\n\n hashBuilder = hashBuilder\n .update(stableStringify({ ...result.completedEntity }))\n .update(stableStringifyArray([...result.deferredEntities]))\n .update(stableStringifyArray([...result.relations]))\n .update(stableStringifyArray([...result.refreshKeys]))\n .update(stableStringifyArray([...parents]));\n }\n\n const resultHash = hashBuilder.digest('hex');\n if (resultHash === previousResultHash) {\n // If nothing changed in our produced outputs, we cannot have any\n // significant effect on our surroundings; therefore, we just abort\n // without any updates / stitching.\n track.markSuccessfulWithNoChanges();\n return;\n }\n\n // If the result was marked as not OK, it signals that some part of the\n // processing pipeline threw an exception. This can happen both as part of\n // non-catastrophic things such as due to validation errors, as well as if\n // something fatal happens inside the processing for other reasons. In any\n // case, this means we can't trust that anything in the output is okay. So\n // just store the errors and trigger a stich so that they become visible to\n // the outside.\n if (!result.ok) {\n // notify the error listener if the entity can not be processed.\n Promise.resolve(undefined)\n .then(() =>\n this.onProcessingError?.({\n unprocessedEntity,\n errors: result.errors,\n }),\n )\n .catch(error => {\n this.logger.debug(\n `Processing error listener threw an exception, ${stringifyError(\n error,\n )}`,\n );\n });\n\n await this.processingDatabase.transaction(async tx => {\n await this.processingDatabase.updateProcessedEntityErrors(tx, {\n id,\n errors: errorsString,\n resultHash,\n });\n });\n\n await this.stitcher.stitch({\n entityRefs: [stringifyEntityRef(unprocessedEntity)],\n });\n\n track.markSuccessfulWithErrors();\n return;\n }\n\n result.completedEntity.metadata.uid = id;\n let oldRelationSources: Map<string, string>;\n await this.processingDatabase.transaction(async tx => {\n const { previous } =\n await this.processingDatabase.updateProcessedEntity(tx, {\n id,\n processedEntity: result.completedEntity,\n resultHash,\n errors: errorsString,\n relations: result.relations,\n deferredEntities: result.deferredEntities,\n locationKey,\n refreshKeys: result.refreshKeys,\n });\n oldRelationSources = new Map(\n previous.relations.map(r => [\n `${r.source_entity_ref}:${r.type}->${r.target_entity_ref}`,\n r.source_entity_ref,\n ]),\n );\n });\n\n const newRelationSources = new Map<string, string>(\n result.relations.map(relation => {\n const sourceEntityRef = stringifyEntityRef(relation.source);\n const targetEntityRef = stringifyEntityRef(relation.target);\n return [\n `${sourceEntityRef}:${relation.type}->${targetEntityRef}`,\n sourceEntityRef,\n ];\n }),\n );\n\n const setOfThingsToStitch = new Set<string>([\n stringifyEntityRef(result.completedEntity),\n ]);\n newRelationSources.forEach((sourceEntityRef, uniqueKey) => {\n if (!oldRelationSources.has(uniqueKey)) {\n setOfThingsToStitch.add(sourceEntityRef);\n }\n });\n oldRelationSources!.forEach((sourceEntityRef, uniqueKey) => {\n if (!newRelationSources.has(uniqueKey)) {\n setOfThingsToStitch.add(sourceEntityRef);\n }\n });\n\n await this.stitcher.stitch({\n entityRefs: setOfThingsToStitch,\n });\n\n track.markSuccessfulWithChanges();\n } catch (error) {\n assertError(error);\n track.markFailed(error);\n }\n });\n },\n });\n }\n\n private startOrphanCleanup(): () => void {\n const orphanStrategy =\n this.config.getOptionalString('catalog.orphanStrategy') ?? 'delete';\n if (orphanStrategy !== 'delete') {\n return () => {};\n }\n\n const stitchingStrategy = stitchingStrategyFromConfig(this.config);\n\n const runOnce = async () => {\n try {\n const n = await deleteOrphanedEntities({\n knex: this.knex,\n strategy: stitchingStrategy,\n });\n if (n > 0) {\n this.logger.info(`Deleted ${n} orphaned entities`);\n }\n } catch (error) {\n this.logger.warn(`Failed to delete orphaned entities`, error);\n }\n };\n\n const abortController = new AbortController();\n this.scheduler.scheduleTask({\n id: 'catalog_orphan_cleanup',\n frequency: { milliseconds: this.orphanCleanupIntervalMs },\n timeout: { milliseconds: this.orphanCleanupIntervalMs * 0.8 },\n fn: runOnce,\n signal: abortController.signal,\n });\n\n return () => {\n abortController.abort();\n };\n }\n}\n\n// Helps wrap the timing and logging behaviors\nfunction progressTracker(metrics: MetricsService) {\n // prom-client metrics are deprecated in favour of OpenTelemetry metrics.\n const promProcessedEntities = createCounterMetric({\n name: 'catalog_processed_entities_count',\n help: 'Amount of entities processed, DEPRECATED, use OpenTelemetry metrics instead',\n labelNames: ['result'],\n });\n const promProcessingDuration = createSummaryMetric({\n name: 'catalog_processing_duration_seconds',\n help: 'Time spent executing the full processing flow, DEPRECATED, use OpenTelemetry metrics instead',\n labelNames: ['result'],\n });\n const promProcessorsDuration = createSummaryMetric({\n name: 'catalog_processors_duration_seconds',\n help: 'Time spent executing catalog processors, DEPRECATED, use OpenTelemetry metrics instead',\n labelNames: ['result'],\n });\n const promProcessingQueueDelay = createSummaryMetric({\n name: 'catalog_processing_queue_delay_seconds',\n help: 'The amount of delay between being scheduled for processing, and the start of actually being processed, DEPRECATED, use OpenTelemetry metrics instead',\n });\n\n const processedEntities = metrics.createCounter(\n 'catalog.processed.entities.count',\n { description: 'Amount of entities processed' },\n );\n\n const processingDuration = metrics.createHistogram(\n 'catalog.processing.duration',\n {\n description: 'Time spent executing the full processing flow',\n unit: 'seconds',\n },\n );\n\n const processorsDuration = metrics.createHistogram(\n 'catalog.processors.duration',\n {\n description: 'Time spent executing catalog processors',\n unit: 'seconds',\n },\n );\n\n const processingQueueDelay = metrics.createHistogram(\n 'catalog.processing.queue.delay',\n {\n description:\n 'The amount of delay between being scheduled for processing, and the start of actually being processed',\n unit: 'seconds',\n },\n );\n\n function processStart(item: RefreshStateItem, logger: LoggerService) {\n const startTime = process.hrtime();\n const endOverallTimer = promProcessingDuration.startTimer();\n const endProcessorsTimer = promProcessorsDuration.startTimer();\n\n logger.debug(`Processing ${item.entityRef}`);\n\n if (item.nextUpdateAt) {\n const seconds = -item.nextUpdateAt.diffNow().as('seconds');\n promProcessingQueueDelay.observe(seconds);\n processingQueueDelay.record(seconds);\n }\n\n function endTime() {\n const delta = process.hrtime(startTime);\n return delta[0] + delta[1] / 1e9;\n }\n\n function markProcessorsCompleted(result: EntityProcessingResult) {\n endProcessorsTimer({ result: result.ok ? 'ok' : 'failed' });\n processorsDuration.record(endTime(), {\n result: result.ok ? 'ok' : 'failed',\n });\n }\n\n function markSuccessfulWithNoChanges() {\n endOverallTimer({ result: 'unchanged' });\n promProcessedEntities.inc({ result: 'unchanged' }, 1);\n\n processingDuration.record(endTime(), { result: 'unchanged' });\n processedEntities.add(1, { result: 'unchanged' });\n }\n\n function markSuccessfulWithErrors() {\n endOverallTimer({ result: 'errors' });\n promProcessedEntities.inc({ result: 'errors' }, 1);\n\n processingDuration.record(endTime(), { result: 'errors' });\n processedEntities.add(1, { result: 'errors' });\n }\n\n function markSuccessfulWithChanges() {\n endOverallTimer({ result: 'changed' });\n promProcessedEntities.inc({ result: 'changed' }, 1);\n\n processingDuration.record(endTime(), { result: 'changed' });\n processedEntities.add(1, { result: 'changed' });\n }\n\n function markFailed(error: Error) {\n promProcessedEntities.inc({ result: 'failed' }, 1);\n processedEntities.add(1, { result: 'failed' });\n logger.warn(`Processing of ${item.entityRef} failed`, error);\n }\n\n return {\n markProcessorsCompleted,\n markSuccessfulWithNoChanges,\n markSuccessfulWithErrors,\n markSuccessfulWithChanges,\n markFailed,\n };\n }\n\n return { processStart };\n}\n"],"names":["trace","TRACER_ID","stableStringify","startTaskPipeline","withActiveSpan","addEntityAttributes","ANNOTATION_LOCATION","CATALOG_ERRORS_TOPIC","serializeError","stringifyEntityRef","stringifyError","assertError","stitchingStrategyFromConfig","deleteOrphanedEntities","metrics","createCounterMetric","createSummaryMetric"],"mappings":"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"}
1
+ {"version":3,"file":"DefaultCatalogProcessingEngine.cjs.js","sources":["../../src/processing/DefaultCatalogProcessingEngine.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n ANNOTATION_LOCATION,\n Entity,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { assertError, serializeError, stringifyError } from '@backstage/errors';\nimport { Hash } from 'node:crypto';\nimport stableStringify from 'fast-json-stable-stringify';\nimport { Knex } from 'knex';\nimport { trace } from '@opentelemetry/api';\nimport { ProcessingDatabase, RefreshStateItem } from '../database/types';\nimport { createCounterMetric, createSummaryMetric } from '../util/metrics';\nimport { CatalogProcessingOrchestrator, EntityProcessingResult } from './types';\nimport { Stitcher, stitchingStrategyFromConfig } from '../stitching/types';\nimport { startTaskPipeline } from './TaskPipeline';\nimport { Config } from '@backstage/config';\nimport {\n addEntityAttributes,\n TRACER_ID,\n withActiveSpan,\n} from '../util/opentelemetry';\nimport { deleteOrphanedEntities } from '../database/operations/util/deleteOrphanedEntities';\nimport { EventsService } from '@backstage/plugin-events-node';\nimport { CATALOG_ERRORS_TOPIC } from '../constants';\nimport { LoggerService, SchedulerService } from '@backstage/backend-plugin-api';\nimport { MetricsService } from '@backstage/backend-plugin-api/alpha';\n\nconst CACHE_TTL = 5;\n\nconst tracer = trace.getTracer(TRACER_ID);\n\nexport type ProgressTracker = ReturnType<typeof progressTracker>;\n\nconst stableStringifyArray = (arr: any[]) => {\n const sorted = arr.map(stableStringify).sort();\n return `[${sorted.join(',')}]`;\n};\n\n// NOTE(freben): Perhaps surprisingly, this class does not implement the\n// CatalogProcessingEngine type. That type is externally visible and its name is\n// the way it is for historic reasons. This class has no particular reason to\n// implement that precise interface; nowadays there are several different\n// engines \"hiding\" behind the CatalogProcessingEngine interface, of which this\n// is just one.\nexport class DefaultCatalogProcessingEngine {\n private readonly config: Config;\n private readonly scheduler: SchedulerService;\n private readonly logger: LoggerService;\n private readonly knex: Knex;\n private readonly processingDatabase: ProcessingDatabase;\n private readonly orchestrator: CatalogProcessingOrchestrator;\n private readonly stitcher: Stitcher;\n private readonly createHash: () => Hash;\n private readonly pollingIntervalMs: number;\n private readonly orphanCleanupIntervalMs: number;\n private readonly onProcessingError?: (event: {\n unprocessedEntity: Entity;\n errors: Error[];\n }) => Promise<void> | void;\n private readonly tracker: ProgressTracker;\n private readonly events: EventsService;\n\n private stopFunc?: () => void;\n\n constructor(options: {\n config: Config;\n scheduler: SchedulerService;\n logger: LoggerService;\n knex: Knex;\n processingDatabase: ProcessingDatabase;\n orchestrator: CatalogProcessingOrchestrator;\n stitcher: Stitcher;\n createHash: () => Hash;\n pollingIntervalMs?: number;\n orphanCleanupIntervalMs?: number;\n onProcessingError?: (event: {\n unprocessedEntity: Entity;\n errors: Error[];\n }) => Promise<void> | void;\n tracker?: ProgressTracker;\n events: EventsService;\n metrics: MetricsService;\n }) {\n this.config = options.config;\n this.scheduler = options.scheduler;\n this.logger = options.logger;\n this.knex = options.knex;\n this.processingDatabase = options.processingDatabase;\n this.orchestrator = options.orchestrator;\n this.stitcher = options.stitcher;\n this.createHash = options.createHash;\n this.pollingIntervalMs = options.pollingIntervalMs ?? 1_000;\n this.orphanCleanupIntervalMs = options.orphanCleanupIntervalMs ?? 30_000;\n this.onProcessingError = options.onProcessingError;\n this.tracker = options.tracker ?? progressTracker(options.metrics);\n this.events = options.events;\n\n this.stopFunc = undefined;\n }\n\n async start() {\n if (this.stopFunc) {\n throw new Error('Processing engine is already started');\n }\n\n const stopPipeline = this.startPipeline();\n const stopCleanup = this.startOrphanCleanup();\n\n this.stopFunc = () => {\n stopPipeline();\n stopCleanup();\n };\n }\n\n async stop() {\n if (this.stopFunc) {\n this.stopFunc();\n this.stopFunc = undefined;\n }\n }\n\n private startPipeline(): () => void {\n return startTaskPipeline<RefreshStateItem>({\n lowWatermark: 5,\n highWatermark: 10,\n pollingIntervalMs: this.pollingIntervalMs,\n loadTasks: async count => {\n try {\n const { items } =\n await this.processingDatabase.getProcessableEntities(this.knex, {\n processBatchSize: count,\n });\n return items;\n } catch (error) {\n this.logger.warn('Failed to load processing items', error);\n return [];\n }\n },\n processTask: async item => {\n await withActiveSpan(tracer, 'ProcessingRun', async span => {\n const track = this.tracker.processStart(item, this.logger);\n addEntityAttributes(span, item.unprocessedEntity);\n\n try {\n const {\n id,\n state,\n unprocessedEntity,\n entityRef,\n locationKey,\n resultHash: previousResultHash,\n } = item;\n const result = await this.orchestrator.process({\n entity: unprocessedEntity,\n state,\n });\n\n track.markProcessorsCompleted(result);\n\n if (result.ok) {\n const { ttl: _, ...stateWithoutTtl } = state ?? {};\n if (\n stableStringify(stateWithoutTtl) !==\n stableStringify(result.state)\n ) {\n await this.processingDatabase.transaction(async tx => {\n await this.processingDatabase.updateEntityCache(tx, {\n id,\n state: {\n ttl: CACHE_TTL,\n ...result.state,\n },\n });\n });\n }\n } else {\n const maybeTtl = state?.ttl;\n const ttl = Number.isInteger(maybeTtl) ? (maybeTtl as number) : 0;\n await this.processingDatabase.transaction(async tx => {\n await this.processingDatabase.updateEntityCache(tx, {\n id,\n state: ttl > 0 ? { ...state, ttl: ttl - 1 } : {},\n });\n });\n }\n\n const location =\n unprocessedEntity?.metadata?.annotations?.[ANNOTATION_LOCATION];\n if (result.errors.length) {\n this.events.publish({\n topic: CATALOG_ERRORS_TOPIC,\n eventPayload: {\n entity: entityRef,\n location,\n errors: result.errors,\n },\n });\n }\n const errorsString = JSON.stringify(\n result.errors.map(e => serializeError(e)),\n );\n\n let hashBuilder = this.createHash().update(errorsString);\n\n if (result.ok) {\n const { entityRefs: parents } =\n await this.processingDatabase.transaction(tx =>\n this.processingDatabase.listParents(tx, {\n entityRefs: [\n entityRef,\n ...result.deferredEntities.map(e =>\n stringifyEntityRef(e.entity),\n ),\n ],\n }),\n );\n\n hashBuilder = hashBuilder\n .update(stableStringify({ ...result.completedEntity }))\n .update(stableStringifyArray([...result.deferredEntities]))\n .update(stableStringifyArray([...result.relations]))\n .update(stableStringifyArray([...result.refreshKeys]))\n .update(stableStringifyArray([...parents]));\n }\n\n const resultHash = hashBuilder.digest('hex');\n if (resultHash === previousResultHash) {\n // If nothing changed in our produced outputs, we cannot have any\n // significant effect on our surroundings; therefore, we just abort\n // without any updates / stitching.\n track.markSuccessfulWithNoChanges();\n return;\n }\n\n // If the result was marked as not OK, it signals that some part of the\n // processing pipeline threw an exception. This can happen both as part of\n // non-catastrophic things such as due to validation errors, as well as if\n // something fatal happens inside the processing for other reasons. In any\n // case, this means we can't trust that anything in the output is okay. So\n // just store the errors and trigger a stich so that they become visible to\n // the outside.\n if (!result.ok) {\n // notify the error listener if the entity can not be processed.\n Promise.resolve(undefined)\n .then(() =>\n this.onProcessingError?.({\n unprocessedEntity,\n errors: result.errors,\n }),\n )\n .catch(error => {\n this.logger.debug(\n `Processing error listener threw an exception, ${stringifyError(\n error,\n )}`,\n );\n });\n\n await this.processingDatabase.transaction(async tx => {\n await this.processingDatabase.updateProcessedEntityErrors(tx, {\n id,\n errors: errorsString,\n resultHash,\n });\n });\n\n await this.stitcher.stitch({\n entityRefs: [stringifyEntityRef(unprocessedEntity)],\n });\n\n track.markSuccessfulWithErrors();\n return;\n }\n\n result.completedEntity.metadata.uid = id;\n let oldRelationSources: Map<string, string>;\n await this.processingDatabase.transaction(async tx => {\n const { previous } =\n await this.processingDatabase.updateProcessedEntity(tx, {\n id,\n processedEntity: result.completedEntity,\n resultHash,\n errors: errorsString,\n relations: result.relations,\n deferredEntities: result.deferredEntities,\n locationKey,\n refreshKeys: result.refreshKeys,\n });\n oldRelationSources = new Map(\n previous.relations.map(r => [\n `${r.source_entity_ref}:${r.type}->${r.target_entity_ref}`,\n r.source_entity_ref,\n ]),\n );\n });\n\n const newRelationSources = new Map<string, string>(\n result.relations.map(relation => {\n const sourceEntityRef = stringifyEntityRef(relation.source);\n const targetEntityRef = stringifyEntityRef(relation.target);\n return [\n `${sourceEntityRef}:${relation.type}->${targetEntityRef}`,\n sourceEntityRef,\n ];\n }),\n );\n\n const setOfThingsToStitch = new Set<string>([\n stringifyEntityRef(result.completedEntity),\n ]);\n newRelationSources.forEach((sourceEntityRef, uniqueKey) => {\n if (!oldRelationSources.has(uniqueKey)) {\n setOfThingsToStitch.add(sourceEntityRef);\n }\n });\n oldRelationSources!.forEach((sourceEntityRef, uniqueKey) => {\n if (!newRelationSources.has(uniqueKey)) {\n setOfThingsToStitch.add(sourceEntityRef);\n }\n });\n\n await this.stitcher.stitch({\n entityRefs: setOfThingsToStitch,\n });\n\n track.markSuccessfulWithChanges();\n } catch (error) {\n assertError(error);\n track.markFailed(error);\n }\n });\n },\n });\n }\n\n private startOrphanCleanup(): () => void {\n const orphanStrategy =\n this.config.getOptionalString('catalog.orphanStrategy') ?? 'delete';\n if (orphanStrategy !== 'delete') {\n return () => {};\n }\n\n const stitchingStrategy = stitchingStrategyFromConfig(this.config);\n\n const runOnce = async () => {\n try {\n const n = await deleteOrphanedEntities({\n knex: this.knex,\n strategy: stitchingStrategy,\n });\n if (n > 0) {\n this.logger.info(`Deleted ${n} orphaned entities`);\n }\n } catch (error) {\n this.logger.warn(`Failed to delete orphaned entities`, error);\n }\n };\n\n const abortController = new AbortController();\n this.scheduler.scheduleTask({\n id: 'catalog_orphan_cleanup',\n frequency: { milliseconds: this.orphanCleanupIntervalMs },\n timeout: { milliseconds: this.orphanCleanupIntervalMs * 0.8 },\n fn: runOnce,\n signal: abortController.signal,\n });\n\n return () => {\n abortController.abort();\n };\n }\n}\n\n// Helps wrap the timing and logging behaviors\nfunction progressTracker(metrics: MetricsService) {\n // prom-client metrics are deprecated in favour of OpenTelemetry metrics.\n const promProcessedEntities = createCounterMetric({\n name: 'catalog_processed_entities_count',\n help: 'Amount of entities processed, DEPRECATED, use OpenTelemetry metrics instead',\n labelNames: ['result'],\n });\n const promProcessingDuration = createSummaryMetric({\n name: 'catalog_processing_duration_seconds',\n help: 'Time spent executing the full processing flow, DEPRECATED, use OpenTelemetry metrics instead',\n labelNames: ['result'],\n });\n const promProcessorsDuration = createSummaryMetric({\n name: 'catalog_processors_duration_seconds',\n help: 'Time spent executing catalog processors, DEPRECATED, use OpenTelemetry metrics instead',\n labelNames: ['result'],\n });\n const promProcessingQueueDelay = createSummaryMetric({\n name: 'catalog_processing_queue_delay_seconds',\n help: 'The amount of delay between being scheduled for processing, and the start of actually being processed, DEPRECATED, use OpenTelemetry metrics instead',\n });\n\n const processedEntities = metrics.createCounter(\n 'catalog.processed.entities.count',\n { description: 'Amount of entities processed' },\n );\n\n const processingDuration = metrics.createHistogram(\n 'catalog.processing.duration',\n {\n description: 'Time spent executing the full processing flow',\n unit: 'seconds',\n },\n );\n\n const processorsDuration = metrics.createHistogram(\n 'catalog.processors.duration',\n {\n description: 'Time spent executing catalog processors',\n unit: 'seconds',\n },\n );\n\n const processingQueueDelay = metrics.createHistogram(\n 'catalog.processing.queue.delay',\n {\n description:\n 'The amount of delay between being scheduled for processing, and the start of actually being processed',\n unit: 'seconds',\n },\n );\n\n function processStart(item: RefreshStateItem, logger: LoggerService) {\n const startTime = process.hrtime();\n const endOverallTimer = promProcessingDuration.startTimer();\n const endProcessorsTimer = promProcessorsDuration.startTimer();\n\n logger.debug(`Processing ${item.entityRef}`);\n\n if (item.nextUpdateAt) {\n const seconds = -item.nextUpdateAt.diffNow().as('seconds');\n promProcessingQueueDelay.observe(seconds);\n processingQueueDelay.record(seconds);\n }\n\n function endTime() {\n const delta = process.hrtime(startTime);\n return delta[0] + delta[1] / 1e9;\n }\n\n function markProcessorsCompleted(result: EntityProcessingResult) {\n endProcessorsTimer({ result: result.ok ? 'ok' : 'failed' });\n processorsDuration.record(endTime(), {\n result: result.ok ? 'ok' : 'failed',\n });\n }\n\n function markSuccessfulWithNoChanges() {\n endOverallTimer({ result: 'unchanged' });\n promProcessedEntities.inc({ result: 'unchanged' }, 1);\n\n processingDuration.record(endTime(), { result: 'unchanged' });\n processedEntities.add(1, { result: 'unchanged' });\n }\n\n function markSuccessfulWithErrors() {\n endOverallTimer({ result: 'errors' });\n promProcessedEntities.inc({ result: 'errors' }, 1);\n\n processingDuration.record(endTime(), { result: 'errors' });\n processedEntities.add(1, { result: 'errors' });\n }\n\n function markSuccessfulWithChanges() {\n endOverallTimer({ result: 'changed' });\n promProcessedEntities.inc({ result: 'changed' }, 1);\n\n processingDuration.record(endTime(), { result: 'changed' });\n processedEntities.add(1, { result: 'changed' });\n }\n\n function markFailed(error: Error) {\n promProcessedEntities.inc({ result: 'failed' }, 1);\n processedEntities.add(1, { result: 'failed' });\n logger.warn(`Processing of ${item.entityRef} failed`, error);\n }\n\n return {\n markProcessorsCompleted,\n markSuccessfulWithNoChanges,\n markSuccessfulWithErrors,\n markSuccessfulWithChanges,\n markFailed,\n };\n }\n\n return { processStart };\n}\n"],"names":["trace","TRACER_ID","stableStringify","startTaskPipeline","withActiveSpan","addEntityAttributes","ANNOTATION_LOCATION","CATALOG_ERRORS_TOPIC","serializeError","stringifyEntityRef","stringifyError","assertError","stitchingStrategyFromConfig","deleteOrphanedEntities","metrics","createCounterMetric","createSummaryMetric"],"mappings":"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"}
@@ -1,10 +1,12 @@
1
1
  'use strict';
2
2
 
3
+ var parseGitUrl = require('git-url-parse');
3
4
  var yaml = require('yaml');
4
5
  var pluginCatalogNode = require('@backstage/plugin-catalog-node');
5
6
 
6
7
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
7
8
 
9
+ var parseGitUrl__default = /*#__PURE__*/_interopDefaultCompat(parseGitUrl);
8
10
  var yaml__default = /*#__PURE__*/_interopDefaultCompat(yaml);
9
11
 
10
12
  class PlaceholderProcessor {
@@ -119,6 +121,24 @@ async function readTextLocation(params) {
119
121
  );
120
122
  }
121
123
  }
124
+ function assertSameRepository(resolvedUrl, baseUrl) {
125
+ let resolved;
126
+ let base;
127
+ try {
128
+ resolved = parseGitUrl__default.default(resolvedUrl);
129
+ base = parseGitUrl__default.default(baseUrl);
130
+ } catch {
131
+ return;
132
+ }
133
+ if (!resolved.owner || !resolved.name || !resolved.filepath || !base.owner || !base.name || !base.filepath) {
134
+ return;
135
+ }
136
+ if (resolved.resource !== base.resource || resolved.full_name !== base.full_name) {
137
+ throw new Error(
138
+ `Placeholder may only reference files from the same repository as the entity`
139
+ );
140
+ }
141
+ }
122
142
  function relativeUrl({
123
143
  key,
124
144
  value,
@@ -131,7 +151,9 @@ function relativeUrl({
131
151
  );
132
152
  }
133
153
  try {
134
- return resolveUrl(value, baseUrl);
154
+ const resolved = resolveUrl(value, baseUrl);
155
+ assertSameRepository(resolved, baseUrl);
156
+ return resolved;
135
157
  } catch (e) {
136
158
  throw new Error(
137
159
  `Placeholder $${key} could not form a URL out of ${baseUrl} and ${value}, ${e}`
@@ -1 +1 @@
1
- {"version":3,"file":"PlaceholderProcessor.cjs.js","sources":["../../src/processors/PlaceholderProcessor.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { JsonValue } from '@backstage/types';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport yaml from 'yaml';\nimport { LocationSpec } from '@backstage/plugin-catalog-common';\nimport {\n CatalogProcessor,\n CatalogProcessorEmit,\n PlaceholderResolver,\n PlaceholderResolverParams,\n processingResult,\n} from '@backstage/plugin-catalog-node';\nimport { UrlReaderService } from '@backstage/backend-plugin-api';\n\n/** @public */\nexport type PlaceholderProcessorOptions = {\n resolvers: Record<string, PlaceholderResolver>;\n reader: UrlReaderService;\n integrations: ScmIntegrationRegistry;\n};\n\n/**\n * Traverses raw entity JSON looking for occurrences of $-prefixed placeholders\n * that it then fills in with actual data.\n * @public\n */\nexport class PlaceholderProcessor implements CatalogProcessor {\n private readonly options: PlaceholderProcessorOptions;\n\n constructor(options: PlaceholderProcessorOptions) {\n this.options = options;\n }\n\n getProcessorName(): string {\n return 'PlaceholderProcessor';\n }\n\n async preProcessEntity(\n entity: Entity,\n location: LocationSpec,\n emit: CatalogProcessorEmit,\n ): Promise<Entity> {\n const process = async (data: any): Promise<[any, boolean]> => {\n if (!data || !(data instanceof Object)) {\n // Scalars can't have placeholders\n return [data, false];\n }\n\n if (Array.isArray(data)) {\n // We're an array - process all entries recursively\n const items = await Promise.all(data.map(item => process(item)));\n return items.every(([, changed]) => !changed)\n ? [data, false]\n : [items.map(([item]) => item), true];\n }\n\n const keys = Object.keys(data);\n if (!keys.some(k => k.startsWith('$'))) {\n // We're an object but no placeholders at this level - process all\n // entries recursively\n const entries = await Promise.all(\n Object.entries(data).map(([k, v]) =>\n process(v).then(vp => [k, vp] as const),\n ),\n );\n return entries.every(([, [, changed]]) => !changed)\n ? [data, false]\n : [Object.fromEntries(entries.map(([k, [v]]) => [k, v])), true];\n } else if (keys.length !== 1) {\n // This was an object that had more than one key, some of which were\n // dollar prefixed. We only handle the case where there is exactly one\n // such key; anything else is left alone.\n return [data, false];\n }\n\n const resolverKey = keys[0].substring(1);\n const resolverValue = data[keys[0]];\n\n const resolver = this.options.resolvers[resolverKey];\n if (!resolver) {\n // If there was no such placeholder resolver, we err on the side of safety\n // and assume that this is something that's best left alone. For example, if\n // the input contains JSONSchema, there may be \"$ref\": \"#/definitions/node\"\n // nodes in the document.\n return [data, false];\n }\n\n const read = async (url: string): Promise<Buffer> => {\n const response = await this.options.reader.readUrl(url);\n const buffer = await response.buffer();\n return buffer;\n };\n\n const resolveUrl = (url: string, base: string): string =>\n this.options.integrations.resolveUrl({\n url,\n base,\n });\n\n return [\n await resolver({\n key: resolverKey,\n value: resolverValue,\n baseUrl: location.target,\n read,\n resolveUrl,\n emit,\n }),\n true,\n ];\n };\n\n const [result] = await process(entity);\n return result;\n }\n}\n\n/*\n * Resolvers\n */\n\nexport async function yamlPlaceholderResolver(\n params: PlaceholderResolverParams,\n): Promise<JsonValue> {\n const { content, url } = await readTextLocation(params);\n\n params.emit(processingResult.refresh(`url:${url}`));\n\n // YAML merge support enabled by default\n const enableYamlMerge = false;\n const parseOptions = { merge: enableYamlMerge };\n\n let documents: yaml.Document.Parsed[];\n try {\n documents = yaml.parseAllDocuments(content, parseOptions).filter(d => d);\n } catch (e) {\n throw new Error(\n `Placeholder \\$${params.key} failed to parse YAML data at ${params.value}, ${e}`,\n );\n }\n\n if (documents.length !== 1) {\n throw new Error(\n `Placeholder \\$${params.key} expected to find exactly one document of data at ${params.value}, found ${documents.length}`,\n );\n }\n\n const document = documents[0];\n\n if (document.errors?.length) {\n throw new Error(\n `Placeholder \\$${params.key} found an error in the data at ${params.value}, ${document.errors[0]}`,\n );\n }\n\n return document.toJSON();\n}\n\nexport async function jsonPlaceholderResolver(\n params: PlaceholderResolverParams,\n): Promise<JsonValue> {\n const { content, url } = await readTextLocation(params);\n\n params.emit(processingResult.refresh(`url:${url}`));\n\n try {\n return JSON.parse(content);\n } catch (e) {\n throw new Error(\n `Placeholder \\$${params.key} failed to parse JSON data at ${params.value}, ${e}`,\n );\n }\n}\n\nexport async function textPlaceholderResolver(\n params: PlaceholderResolverParams,\n): Promise<JsonValue> {\n const { content, url } = await readTextLocation(params);\n\n params.emit(processingResult.refresh(`url:${url}`));\n\n return content;\n}\n\n/*\n * Helpers\n */\n\nasync function readTextLocation(\n params: PlaceholderResolverParams,\n): Promise<{ content: string; url: string }> {\n const newUrl = relativeUrl(params);\n\n try {\n const data = await params.read(newUrl);\n return { content: data.toString('utf-8'), url: newUrl };\n } catch (e) {\n throw new Error(\n `Placeholder \\$${params.key} could not read location ${params.value}, ${e}`,\n );\n }\n}\n\nfunction relativeUrl({\n key,\n value,\n baseUrl,\n resolveUrl,\n}: PlaceholderResolverParams): string {\n if (typeof value !== 'string') {\n throw new Error(\n `Placeholder \\$${key} expected a string value parameter, in the form of an absolute URL or a relative path`,\n );\n }\n\n try {\n return resolveUrl(value, baseUrl);\n } catch (e) {\n // The only remaining case that isn't support is a relative file path that should be\n // resolved using a relative file location. Accessing local file paths can lead to\n // path traversal attacks and access to any file on the host system. Implementing this\n // would require additional security measures.\n throw new Error(\n `Placeholder \\$${key} could not form a URL out of ${baseUrl} and ${value}, ${e}`,\n );\n }\n}\n"],"names":["processingResult","yaml"],"mappings":"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"}
1
+ {"version":3,"file":"PlaceholderProcessor.cjs.js","sources":["../../src/processors/PlaceholderProcessor.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { JsonValue } from '@backstage/types';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport parseGitUrl from 'git-url-parse';\nimport yaml from 'yaml';\nimport { LocationSpec } from '@backstage/plugin-catalog-common';\nimport {\n CatalogProcessor,\n CatalogProcessorEmit,\n PlaceholderResolver,\n PlaceholderResolverParams,\n processingResult,\n} from '@backstage/plugin-catalog-node';\nimport { UrlReaderService } from '@backstage/backend-plugin-api';\n\n/** @public */\nexport type PlaceholderProcessorOptions = {\n resolvers: Record<string, PlaceholderResolver>;\n reader: UrlReaderService;\n integrations: ScmIntegrationRegistry;\n};\n\n/**\n * Traverses raw entity JSON looking for occurrences of $-prefixed placeholders\n * that it then fills in with actual data.\n * @public\n */\nexport class PlaceholderProcessor implements CatalogProcessor {\n private readonly options: PlaceholderProcessorOptions;\n\n constructor(options: PlaceholderProcessorOptions) {\n this.options = options;\n }\n\n getProcessorName(): string {\n return 'PlaceholderProcessor';\n }\n\n async preProcessEntity(\n entity: Entity,\n location: LocationSpec,\n emit: CatalogProcessorEmit,\n ): Promise<Entity> {\n const process = async (data: any): Promise<[any, boolean]> => {\n if (!data || !(data instanceof Object)) {\n // Scalars can't have placeholders\n return [data, false];\n }\n\n if (Array.isArray(data)) {\n // We're an array - process all entries recursively\n const items = await Promise.all(data.map(item => process(item)));\n return items.every(([, changed]) => !changed)\n ? [data, false]\n : [items.map(([item]) => item), true];\n }\n\n const keys = Object.keys(data);\n if (!keys.some(k => k.startsWith('$'))) {\n // We're an object but no placeholders at this level - process all\n // entries recursively\n const entries = await Promise.all(\n Object.entries(data).map(([k, v]) =>\n process(v).then(vp => [k, vp] as const),\n ),\n );\n return entries.every(([, [, changed]]) => !changed)\n ? [data, false]\n : [Object.fromEntries(entries.map(([k, [v]]) => [k, v])), true];\n } else if (keys.length !== 1) {\n // This was an object that had more than one key, some of which were\n // dollar prefixed. We only handle the case where there is exactly one\n // such key; anything else is left alone.\n return [data, false];\n }\n\n const resolverKey = keys[0].substring(1);\n const resolverValue = data[keys[0]];\n\n const resolver = this.options.resolvers[resolverKey];\n if (!resolver) {\n // If there was no such placeholder resolver, we err on the side of safety\n // and assume that this is something that's best left alone. For example, if\n // the input contains JSONSchema, there may be \"$ref\": \"#/definitions/node\"\n // nodes in the document.\n return [data, false];\n }\n\n const read = async (url: string): Promise<Buffer> => {\n const response = await this.options.reader.readUrl(url);\n const buffer = await response.buffer();\n return buffer;\n };\n\n const resolveUrl = (url: string, base: string): string =>\n this.options.integrations.resolveUrl({\n url,\n base,\n });\n\n return [\n await resolver({\n key: resolverKey,\n value: resolverValue,\n baseUrl: location.target,\n read,\n resolveUrl,\n emit,\n }),\n true,\n ];\n };\n\n const [result] = await process(entity);\n return result;\n }\n}\n\n/*\n * Resolvers\n */\n\nexport async function yamlPlaceholderResolver(\n params: PlaceholderResolverParams,\n): Promise<JsonValue> {\n const { content, url } = await readTextLocation(params);\n\n params.emit(processingResult.refresh(`url:${url}`));\n\n // YAML merge support enabled by default\n const enableYamlMerge = false;\n const parseOptions = { merge: enableYamlMerge };\n\n let documents: yaml.Document.Parsed[];\n try {\n documents = yaml.parseAllDocuments(content, parseOptions).filter(d => d);\n } catch (e) {\n throw new Error(\n `Placeholder \\$${params.key} failed to parse YAML data at ${params.value}, ${e}`,\n );\n }\n\n if (documents.length !== 1) {\n throw new Error(\n `Placeholder \\$${params.key} expected to find exactly one document of data at ${params.value}, found ${documents.length}`,\n );\n }\n\n const document = documents[0];\n\n if (document.errors?.length) {\n throw new Error(\n `Placeholder \\$${params.key} found an error in the data at ${params.value}, ${document.errors[0]}`,\n );\n }\n\n return document.toJSON();\n}\n\nexport async function jsonPlaceholderResolver(\n params: PlaceholderResolverParams,\n): Promise<JsonValue> {\n const { content, url } = await readTextLocation(params);\n\n params.emit(processingResult.refresh(`url:${url}`));\n\n try {\n return JSON.parse(content);\n } catch (e) {\n throw new Error(\n `Placeholder \\$${params.key} failed to parse JSON data at ${params.value}, ${e}`,\n );\n }\n}\n\nexport async function textPlaceholderResolver(\n params: PlaceholderResolverParams,\n): Promise<JsonValue> {\n const { content, url } = await readTextLocation(params);\n\n params.emit(processingResult.refresh(`url:${url}`));\n\n return content;\n}\n\n/*\n * Helpers\n */\n\nasync function readTextLocation(\n params: PlaceholderResolverParams,\n): Promise<{ content: string; url: string }> {\n const newUrl = relativeUrl(params);\n\n try {\n const data = await params.read(newUrl);\n return { content: data.toString('utf-8'), url: newUrl };\n } catch (e) {\n throw new Error(\n `Placeholder \\$${params.key} could not read location ${params.value}, ${e}`,\n );\n }\n}\n\nfunction assertSameRepository(resolvedUrl: string, baseUrl: string): void {\n let resolved;\n let base;\n try {\n resolved = parseGitUrl(resolvedUrl);\n base = parseGitUrl(baseUrl);\n } catch {\n return;\n }\n\n if (\n !resolved.owner ||\n !resolved.name ||\n !resolved.filepath ||\n !base.owner ||\n !base.name ||\n !base.filepath\n ) {\n return;\n }\n\n if (\n resolved.resource !== base.resource ||\n resolved.full_name !== base.full_name\n ) {\n throw new Error(\n `Placeholder may only reference files from the same repository as the entity`,\n );\n }\n}\n\nfunction relativeUrl({\n key,\n value,\n baseUrl,\n resolveUrl,\n}: PlaceholderResolverParams): string {\n if (typeof value !== 'string') {\n throw new Error(\n `Placeholder \\$${key} expected a string value parameter, in the form of an absolute URL or a relative path`,\n );\n }\n\n try {\n const resolved = resolveUrl(value, baseUrl);\n assertSameRepository(resolved, baseUrl);\n return resolved;\n } catch (e) {\n // The only remaining case that isn't support is a relative file path that should be\n // resolved using a relative file location. Accessing local file paths can lead to\n // path traversal attacks and access to any file on the host system. Implementing this\n // would require additional security measures.\n throw new Error(\n `Placeholder \\$${key} could not form a URL out of ${baseUrl} and ${value}, ${e}`,\n );\n }\n}\n"],"names":["processingResult","yaml","parseGitUrl"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-backend",
3
- "version": "3.5.1-next.0",
3
+ "version": "3.5.1",
4
4
  "description": "The Backstage backend plugin that provides the Backstage catalog",
5
5
  "backstage": {
6
6
  "role": "backend-plugin",
@@ -76,20 +76,20 @@
76
76
  "test": "backstage-cli package test"
77
77
  },
78
78
  "dependencies": {
79
- "@backstage/backend-openapi-utils": "0.6.8-next.0",
80
- "@backstage/backend-plugin-api": "1.8.1-next.0",
81
- "@backstage/catalog-client": "1.14.0",
82
- "@backstage/catalog-model": "1.7.7",
83
- "@backstage/config": "1.3.6",
84
- "@backstage/errors": "1.2.7",
85
- "@backstage/filter-predicates": "0.1.1",
86
- "@backstage/integration": "2.0.0",
87
- "@backstage/plugin-catalog-common": "1.1.8",
88
- "@backstage/plugin-catalog-node": "2.1.1-next.0",
89
- "@backstage/plugin-events-node": "0.4.21-next.0",
90
- "@backstage/plugin-permission-common": "0.9.7",
91
- "@backstage/plugin-permission-node": "0.10.12-next.0",
92
- "@backstage/types": "1.2.2",
79
+ "@backstage/backend-openapi-utils": "^0.6.7",
80
+ "@backstage/backend-plugin-api": "^1.8.0",
81
+ "@backstage/catalog-client": "^1.14.0",
82
+ "@backstage/catalog-model": "^1.7.7",
83
+ "@backstage/config": "^1.3.6",
84
+ "@backstage/errors": "^1.2.7",
85
+ "@backstage/filter-predicates": "^0.1.1",
86
+ "@backstage/integration": "^2.0.0",
87
+ "@backstage/plugin-catalog-common": "^1.1.8",
88
+ "@backstage/plugin-catalog-node": "^2.1.0",
89
+ "@backstage/plugin-events-node": "^0.4.20",
90
+ "@backstage/plugin-permission-common": "^0.9.7",
91
+ "@backstage/plugin-permission-node": "^0.10.11",
92
+ "@backstage/types": "^1.2.2",
93
93
  "@opentelemetry/api": "^1.9.0",
94
94
  "codeowners-utils": "^1.0.2",
95
95
  "core-js": "^3.6.5",
@@ -111,11 +111,11 @@
111
111
  "zod-validation-error": "^4.0.2"
112
112
  },
113
113
  "devDependencies": {
114
- "@backstage/backend-defaults": "0.16.1-next.0",
115
- "@backstage/backend-test-utils": "1.11.2-next.0",
116
- "@backstage/cli": "0.36.1-next.0",
117
- "@backstage/plugin-permission-common": "0.9.7",
118
- "@backstage/repo-tools": "0.17.1-next.0",
114
+ "@backstage/backend-defaults": "^0.16.1",
115
+ "@backstage/backend-test-utils": "^1.11.1",
116
+ "@backstage/cli": "^0.36.0",
117
+ "@backstage/plugin-permission-common": "^0.9.7",
118
+ "@backstage/repo-tools": "^0.17.0",
119
119
  "@types/core-js": "^2.5.4",
120
120
  "@types/express": "^4.17.6",
121
121
  "@types/git-url-parse": "^9.0.0",
@@ -126,7 +126,7 @@
126
126
  "luxon": "^3.0.0",
127
127
  "msw": "^2.0.0",
128
128
  "supertest": "^7.0.0",
129
- "wait-for-expect": "^4.0.0",
129
+ "wait-for-expect": "^3.0.2",
130
130
  "winston": "^3.13.0"
131
131
  },
132
132
  "configSchema": "config.d.ts"