@backstage/plugin-catalog-backend 3.0.1-next.0 → 3.0.1-next.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,24 @@
1
1
  # @backstage/plugin-catalog-backend
2
2
 
3
+ ## 3.0.1-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 1752be6: Attempt to circumvent event listener memory leak in compression middleware
8
+ - 9dd213c: Make the processing hash calculation not care about the order of the processors.
9
+
10
+ This change does not affect the behavior of the catalog, but it will make the processing
11
+ hash calculation more robust against changes in the order of processors. This should lead to
12
+ more stable processing hashes, which in turn should lead to fewer unnecessary reprocessing
13
+ of entities.
14
+
15
+ After deploying this fix, you may see a period of increased processing and stitching, but
16
+ this should stabilize over time as the processing hashes become more consistent.
17
+
18
+ - fa6fa60: Fixed getLocationByEntity to use `original_value` instead of `value` when querying search table
19
+ - Updated dependencies
20
+ - @backstage/backend-openapi-utils@0.6.0-next.1
21
+
3
22
  ## 3.0.1-next.0
4
23
 
5
24
  ### Patch Changes
@@ -17,6 +17,10 @@ var stableStringify__default = /*#__PURE__*/_interopDefaultCompat(stableStringif
17
17
 
18
18
  const CACHE_TTL = 5;
19
19
  const tracer = api.trace.getTracer(opentelemetry.TRACER_ID);
20
+ const stableStringifyArray = (arr) => {
21
+ const sorted = arr.map(stableStringify__default.default).sort();
22
+ return `[${sorted.join(",")}]`;
23
+ };
20
24
  class DefaultCatalogProcessingEngine {
21
25
  config;
22
26
  scheduler;
@@ -148,7 +152,7 @@ class DefaultCatalogProcessingEngine {
148
152
  ]
149
153
  })
150
154
  );
151
- hashBuilder = hashBuilder.update(stableStringify__default.default({ ...result.completedEntity })).update(stableStringify__default.default([...result.deferredEntities])).update(stableStringify__default.default([...result.relations])).update(stableStringify__default.default([...result.refreshKeys])).update(stableStringify__default.default([...parents]));
155
+ hashBuilder = hashBuilder.update(stableStringify__default.default({ ...result.completedEntity })).update(stableStringifyArray([...result.deferredEntities])).update(stableStringifyArray([...result.relations])).update(stableStringifyArray([...result.refreshKeys])).update(stableStringifyArray([...parents]));
152
156
  }
153
157
  const resultHash = hashBuilder.digest("hex");
154
158
  if (resultHash === previousResultHash) {
@@ -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 'crypto';\nimport stableStringify from 'fast-json-stable-stringify';\nimport { Knex } from 'knex';\nimport { metrics, 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 { EventBroker, EventsService } from '@backstage/plugin-events-node';\nimport { CATALOG_ERRORS_TOPIC } from '../constants';\nimport { LoggerService, SchedulerService } from '@backstage/backend-plugin-api';\n\nconst CACHE_TTL = 5;\n\nconst tracer = trace.getTracer(TRACER_ID);\n\nexport type ProgressTracker = ReturnType<typeof progressTracker>;\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 eventBroker?: EventBroker | 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 eventBroker?: EventBroker | EventsService;\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();\n this.eventBroker = options.eventBroker;\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.eventBroker?.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(stableStringify([...result.deferredEntities]))\n .update(stableStringify([...result.relations]))\n .update(stableStringify([...result.refreshKeys]))\n .update(stableStringify([...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 if (this.scheduler) {\n const abortController = new AbortController();\n\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 const intervalKey = setInterval(runOnce, this.orphanCleanupIntervalMs);\n return () => {\n clearInterval(intervalKey);\n };\n }\n}\n\n// Helps wrap the timing and logging behaviors\nfunction progressTracker() {\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 meter = metrics.getMeter('default');\n const processedEntities = meter.createCounter(\n 'catalog.processed.entities.count',\n { description: 'Amount of entities processed' },\n );\n\n const processingDuration = meter.createHistogram(\n 'catalog.processing.duration',\n {\n description: 'Time spent executing the full processing flow',\n unit: 'seconds',\n },\n );\n\n const processorsDuration = meter.createHistogram(\n 'catalog.processors.duration',\n {\n description: 'Time spent executing catalog processors',\n unit: 'seconds',\n },\n );\n\n const processingQueueDelay = meter.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","startTaskPipeline","withActiveSpan","addEntityAttributes","stableStringify","ANNOTATION_LOCATION","CATALOG_ERRORS_TOPIC","serializeError","stringifyEntityRef","stringifyError","assertError","stitchingStrategyFromConfig","deleteOrphanedEntities","createCounterMetric","createSummaryMetric","metrics"],"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 'crypto';\nimport stableStringify from 'fast-json-stable-stringify';\nimport { Knex } from 'knex';\nimport { metrics, 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 { EventBroker, EventsService } from '@backstage/plugin-events-node';\nimport { CATALOG_ERRORS_TOPIC } from '../constants';\nimport { LoggerService, SchedulerService } from '@backstage/backend-plugin-api';\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 eventBroker?: EventBroker | 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 eventBroker?: EventBroker | EventsService;\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();\n this.eventBroker = options.eventBroker;\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.eventBroker?.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 if (this.scheduler) {\n const abortController = new AbortController();\n\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 const intervalKey = setInterval(runOnce, this.orphanCleanupIntervalMs);\n return () => {\n clearInterval(intervalKey);\n };\n }\n}\n\n// Helps wrap the timing and logging behaviors\nfunction progressTracker() {\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 meter = metrics.getMeter('default');\n const processedEntities = meter.createCounter(\n 'catalog.processed.entities.count',\n { description: 'Amount of entities processed' },\n );\n\n const processingDuration = meter.createHistogram(\n 'catalog.processing.duration',\n {\n description: 'Time spent executing the full processing flow',\n unit: 'seconds',\n },\n );\n\n const processorsDuration = meter.createHistogram(\n 'catalog.processors.duration',\n {\n description: 'Time spent executing catalog processors',\n unit: 'seconds',\n },\n );\n\n const processingQueueDelay = meter.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","createCounterMetric","createSummaryMetric","metrics"],"mappings":"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"}
@@ -79,13 +79,13 @@ class DefaultLocationStore {
79
79
  const [searchRow] = await this.db("search").where({
80
80
  entity_id: entityRow.entity_id,
81
81
  key: `metadata.annotations.${catalogModel.ANNOTATION_ORIGIN_LOCATION}`
82
- }).select("value").limit(1);
83
- if (!searchRow?.value) {
82
+ }).select("original_value").limit(1);
83
+ if (!searchRow?.original_value) {
84
84
  throw new errors.NotFoundError(
85
85
  `found no origin annotation for ref ${entityRefString}`
86
86
  );
87
87
  }
88
- const { type, target } = catalogModel.parseLocationRef(searchRow.value);
88
+ const { type, target } = catalogModel.parseLocationRef(searchRow.original_value);
89
89
  const [locationRow] = await this.db("locations").where({ type, target }).select().limit(1);
90
90
  if (!locationRow) {
91
91
  throw new errors.NotFoundError(
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultLocationStore.cjs.js","sources":["../../src/providers/DefaultLocationStore.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 { Location } from '@backstage/catalog-client';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport { v4 as uuid } from 'uuid';\nimport {\n DbLocationsRow,\n DbRefreshStateRow,\n DbSearchRow,\n} from '../database/tables';\nimport { getEntityLocationRef } from '../processing/util';\nimport {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-node';\nimport { locationSpecToLocationEntity } from '../util/conversion';\nimport { LocationInput, LocationStore } from '../service/types';\nimport {\n ANNOTATION_ORIGIN_LOCATION,\n CompoundEntityRef,\n parseLocationRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\n\nexport class DefaultLocationStore implements LocationStore, EntityProvider {\n private _connection: EntityProviderConnection | undefined;\n\n constructor(private readonly db: Knex) {}\n\n getProviderName(): string {\n return 'DefaultLocationStore';\n }\n\n async createLocation(input: LocationInput): Promise<Location> {\n const location = await this.db.transaction(async tx => {\n // Attempt to find a previous location matching the input\n const previousLocations = await this.locations(tx);\n // TODO: when location id's are a compilation of input target we can remove this full\n // lookup of locations first and just grab the by that instead.\n const previousLocation = previousLocations.some(\n l => input.type === l.type && input.target === l.target,\n );\n if (previousLocation) {\n throw new ConflictError(\n `Location ${input.type}:${input.target} already exists`,\n );\n }\n\n const inner: DbLocationsRow = {\n id: uuid(),\n type: input.type,\n target: input.target,\n };\n\n await tx<DbLocationsRow>('locations').insert(inner);\n\n return inner;\n });\n const entity = locationSpecToLocationEntity({ location });\n await this.connection.applyMutation({\n type: 'delta',\n added: [{ entity, locationKey: getEntityLocationRef(entity) }],\n removed: [],\n });\n\n return location;\n }\n\n async listLocations(): Promise<Location[]> {\n return await this.locations();\n }\n\n async getLocation(id: string): Promise<Location> {\n const items = await this.db<DbLocationsRow>('locations')\n .where({ id })\n .select();\n\n if (!items.length) {\n throw new NotFoundError(`Found no location with ID ${id}`);\n }\n return items[0];\n }\n\n async deleteLocation(id: string): Promise<void> {\n if (!this.connection) {\n throw new Error('location store is not initialized');\n }\n\n const deleted = await this.db.transaction(async tx => {\n const [location] = await tx<DbLocationsRow>('locations')\n .where({ id })\n .select();\n\n if (!location) {\n throw new NotFoundError(`Found no location with ID ${id}`);\n }\n\n await tx<DbLocationsRow>('locations').where({ id }).del();\n return location;\n });\n const entity = locationSpecToLocationEntity({ location: deleted });\n await this.connection.applyMutation({\n type: 'delta',\n added: [],\n removed: [{ entity, locationKey: getEntityLocationRef(entity) }],\n });\n }\n\n async getLocationByEntity(entityRef: CompoundEntityRef): Promise<Location> {\n const entityRefString = stringifyEntityRef(entityRef);\n\n const [entityRow] = await this.db<DbRefreshStateRow>('refresh_state')\n .where({ entity_ref: entityRefString })\n .select('entity_id')\n .limit(1);\n if (!entityRow) {\n throw new NotFoundError(`found no entity for ref ${entityRefString}`);\n }\n\n const [searchRow] = await this.db<DbSearchRow>('search')\n .where({\n entity_id: entityRow.entity_id,\n key: `metadata.annotations.${ANNOTATION_ORIGIN_LOCATION}`,\n })\n .select('value')\n .limit(1);\n if (!searchRow?.value) {\n throw new NotFoundError(\n `found no origin annotation for ref ${entityRefString}`,\n );\n }\n\n const { type, target } = parseLocationRef(searchRow.value);\n const [locationRow] = await this.db<DbLocationsRow>('locations')\n .where({ type, target })\n .select()\n .limit(1);\n\n if (!locationRow) {\n throw new NotFoundError(\n `Found no location with type ${type} and target ${target}`,\n );\n }\n\n return locationRow;\n }\n\n private get connection(): EntityProviderConnection {\n if (!this._connection) {\n throw new Error('location store is not initialized');\n }\n\n return this._connection;\n }\n\n async connect(connection: EntityProviderConnection): Promise<void> {\n this._connection = connection;\n\n const locations = await this.locations();\n\n const entities = locations.map(location => {\n const entity = locationSpecToLocationEntity({ location });\n return { entity, locationKey: getEntityLocationRef(entity) };\n });\n\n await this.connection.applyMutation({\n type: 'full',\n entities,\n });\n }\n\n private async locations(dbOrTx: Knex.Transaction | Knex = this.db) {\n const locations = await dbOrTx<DbLocationsRow>('locations').select();\n return (\n locations\n // TODO(blam): We should create a mutation to remove this location for everyone\n // eventually when it's all done and dusted\n .filter(({ type }) => type !== 'bootstrap')\n .map(item => ({\n id: item.id,\n target: item.target,\n type: item.type,\n }))\n );\n }\n}\n"],"names":["ConflictError","uuid","locationSpecToLocationEntity","getEntityLocationRef","NotFoundError","stringifyEntityRef","ANNOTATION_ORIGIN_LOCATION","parseLocationRef"],"mappings":"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"}
1
+ {"version":3,"file":"DefaultLocationStore.cjs.js","sources":["../../src/providers/DefaultLocationStore.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 { Location } from '@backstage/catalog-client';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport { v4 as uuid } from 'uuid';\nimport {\n DbLocationsRow,\n DbRefreshStateRow,\n DbSearchRow,\n} from '../database/tables';\nimport { getEntityLocationRef } from '../processing/util';\nimport {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-node';\nimport { locationSpecToLocationEntity } from '../util/conversion';\nimport { LocationInput, LocationStore } from '../service/types';\nimport {\n ANNOTATION_ORIGIN_LOCATION,\n CompoundEntityRef,\n parseLocationRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\n\nexport class DefaultLocationStore implements LocationStore, EntityProvider {\n private _connection: EntityProviderConnection | undefined;\n\n constructor(private readonly db: Knex) {}\n\n getProviderName(): string {\n return 'DefaultLocationStore';\n }\n\n async createLocation(input: LocationInput): Promise<Location> {\n const location = await this.db.transaction(async tx => {\n // Attempt to find a previous location matching the input\n const previousLocations = await this.locations(tx);\n // TODO: when location id's are a compilation of input target we can remove this full\n // lookup of locations first and just grab the by that instead.\n const previousLocation = previousLocations.some(\n l => input.type === l.type && input.target === l.target,\n );\n if (previousLocation) {\n throw new ConflictError(\n `Location ${input.type}:${input.target} already exists`,\n );\n }\n\n const inner: DbLocationsRow = {\n id: uuid(),\n type: input.type,\n target: input.target,\n };\n\n await tx<DbLocationsRow>('locations').insert(inner);\n\n return inner;\n });\n const entity = locationSpecToLocationEntity({ location });\n await this.connection.applyMutation({\n type: 'delta',\n added: [{ entity, locationKey: getEntityLocationRef(entity) }],\n removed: [],\n });\n\n return location;\n }\n\n async listLocations(): Promise<Location[]> {\n return await this.locations();\n }\n\n async getLocation(id: string): Promise<Location> {\n const items = await this.db<DbLocationsRow>('locations')\n .where({ id })\n .select();\n\n if (!items.length) {\n throw new NotFoundError(`Found no location with ID ${id}`);\n }\n return items[0];\n }\n\n async deleteLocation(id: string): Promise<void> {\n if (!this.connection) {\n throw new Error('location store is not initialized');\n }\n\n const deleted = await this.db.transaction(async tx => {\n const [location] = await tx<DbLocationsRow>('locations')\n .where({ id })\n .select();\n\n if (!location) {\n throw new NotFoundError(`Found no location with ID ${id}`);\n }\n\n await tx<DbLocationsRow>('locations').where({ id }).del();\n return location;\n });\n const entity = locationSpecToLocationEntity({ location: deleted });\n await this.connection.applyMutation({\n type: 'delta',\n added: [],\n removed: [{ entity, locationKey: getEntityLocationRef(entity) }],\n });\n }\n\n async getLocationByEntity(entityRef: CompoundEntityRef): Promise<Location> {\n const entityRefString = stringifyEntityRef(entityRef);\n\n const [entityRow] = await this.db<DbRefreshStateRow>('refresh_state')\n .where({ entity_ref: entityRefString })\n .select('entity_id')\n .limit(1);\n if (!entityRow) {\n throw new NotFoundError(`found no entity for ref ${entityRefString}`);\n }\n\n const [searchRow] = await this.db<DbSearchRow>('search')\n .where({\n entity_id: entityRow.entity_id,\n key: `metadata.annotations.${ANNOTATION_ORIGIN_LOCATION}`,\n })\n .select('original_value')\n .limit(1);\n if (!searchRow?.original_value) {\n throw new NotFoundError(\n `found no origin annotation for ref ${entityRefString}`,\n );\n }\n\n const { type, target } = parseLocationRef(searchRow.original_value);\n const [locationRow] = await this.db<DbLocationsRow>('locations')\n .where({ type, target })\n .select()\n .limit(1);\n\n if (!locationRow) {\n throw new NotFoundError(\n `Found no location with type ${type} and target ${target}`,\n );\n }\n\n return locationRow;\n }\n\n private get connection(): EntityProviderConnection {\n if (!this._connection) {\n throw new Error('location store is not initialized');\n }\n\n return this._connection;\n }\n\n async connect(connection: EntityProviderConnection): Promise<void> {\n this._connection = connection;\n\n const locations = await this.locations();\n\n const entities = locations.map(location => {\n const entity = locationSpecToLocationEntity({ location });\n return { entity, locationKey: getEntityLocationRef(entity) };\n });\n\n await this.connection.applyMutation({\n type: 'full',\n entities,\n });\n }\n\n private async locations(dbOrTx: Knex.Transaction | Knex = this.db) {\n const locations = await dbOrTx<DbLocationsRow>('locations').select();\n return (\n locations\n // TODO(blam): We should create a mutation to remove this location for everyone\n // eventually when it's all done and dusted\n .filter(({ type }) => type !== 'bootstrap')\n .map(item => ({\n id: item.id,\n target: item.target,\n type: item.type,\n }))\n );\n }\n}\n"],"names":["ConflictError","uuid","locationSpecToLocationEntity","getEntityLocationRef","NotFoundError","stringifyEntityRef","ANNOTATION_ORIGIN_LOCATION","parseLocationRef"],"mappings":"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"}
@@ -7,6 +7,7 @@ var zod = require('zod');
7
7
  var util = require('./util.cjs.js');
8
8
  var applyEntityFilterToQuery = require('./request/applyEntityFilterToQuery.cjs.js');
9
9
  var process = require('./response/process.cjs.js');
10
+ require('@backstage/types');
10
11
 
11
12
  const DEFAULT_LIMIT = 200;
12
13
  function parsePagination(input) {
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultEntitiesCatalog.cjs.js","sources":["../../src/service/DefaultEntitiesCatalog.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, stringifyEntityRef } from '@backstage/catalog-model';\nimport { InputError, NotFoundError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport { chunk as lodashChunk, isEqual } from 'lodash';\nimport { z } from 'zod';\nimport {\n Cursor,\n EntitiesBatchRequest,\n EntitiesBatchResponse,\n EntitiesCatalog,\n EntitiesRequest,\n EntitiesResponse,\n EntityAncestryResponse,\n EntityFacetsRequest,\n EntityFacetsResponse,\n EntityOrder,\n EntityPagination,\n QueryEntitiesRequest,\n QueryEntitiesResponse,\n} from '../catalog/types';\nimport {\n DbFinalEntitiesRow,\n DbPageInfo,\n DbRefreshStateReferencesRow,\n DbRefreshStateRow,\n DbRelationsRow,\n DbSearchRow,\n} from '../database/tables';\nimport { Stitcher } from '../stitching/types';\n\nimport {\n expandLegacyCompoundRelationsInEntity,\n isQueryEntitiesCursorRequest,\n isQueryEntitiesInitialRequest,\n} from './util';\nimport { EntityFilter } from '@backstage/plugin-catalog-node';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { applyEntityFilterToQuery } from './request/applyEntityFilterToQuery';\nimport { processRawEntitiesResult } from './response';\n\nconst DEFAULT_LIMIT = 200;\n\nfunction parsePagination(input?: EntityPagination): EntityPagination {\n if (!input) {\n return {};\n }\n\n let { limit, offset } = input;\n\n if (input.after === undefined) {\n return { limit, offset };\n }\n\n let cursor;\n try {\n const json = Buffer.from(input.after, 'base64').toString('utf8');\n cursor = JSON.parse(json);\n } catch {\n throw new InputError('Malformed after cursor, could not be parsed');\n }\n\n if (cursor.limit !== undefined) {\n if (!Number.isInteger(cursor.limit)) {\n throw new InputError('Malformed after cursor, limit was not an number');\n }\n limit = cursor.limit;\n }\n\n if (cursor.offset !== undefined) {\n if (!Number.isInteger(cursor.offset)) {\n throw new InputError('Malformed after cursor, offset was not a number');\n }\n offset = cursor.offset;\n }\n\n return { limit, offset };\n}\n\nfunction stringifyPagination(\n input: Required<Omit<EntityPagination, 'after'>>,\n): string {\n const { limit, offset } = input;\n const json = JSON.stringify({ limit, offset });\n const base64 = Buffer.from(json, 'utf8').toString('base64');\n return base64;\n}\n\nexport class DefaultEntitiesCatalog implements EntitiesCatalog {\n private readonly database: Knex;\n private readonly logger: LoggerService;\n private readonly stitcher: Stitcher;\n private readonly enableRelationsCompatibility: boolean;\n\n constructor(options: {\n database: Knex;\n logger: LoggerService;\n stitcher: Stitcher;\n enableRelationsCompatibility?: boolean;\n }) {\n this.database = options.database;\n this.logger = options.logger;\n this.stitcher = options.stitcher;\n this.enableRelationsCompatibility = Boolean(\n options.enableRelationsCompatibility,\n );\n }\n\n async entities(request?: EntitiesRequest): Promise<EntitiesResponse> {\n const db = this.database;\n const { limit, offset } = parsePagination(request?.pagination);\n\n let entitiesQuery =\n db<DbFinalEntitiesRow>('final_entities').select('final_entities.*');\n\n request?.order?.forEach(({ field }, index) => {\n const alias = `order_${index}`;\n entitiesQuery = entitiesQuery.leftOuterJoin(\n { [alias]: 'search' },\n function search(inner) {\n inner\n .on(`${alias}.entity_id`, 'final_entities.entity_id')\n .andOn(`${alias}.key`, db.raw('?', [field]));\n },\n );\n });\n\n entitiesQuery = entitiesQuery.whereNotNull('final_entities.final_entity');\n\n if (request?.filter) {\n entitiesQuery = applyEntityFilterToQuery({\n filter: request.filter,\n targetQuery: entitiesQuery,\n onEntityIdField: 'final_entities.entity_id',\n knex: db,\n });\n }\n\n request?.order?.forEach(({ order }, index) => {\n if (db.client.config.client === 'pg') {\n // pg correctly orders by the column value and handling nulls in one go\n entitiesQuery = entitiesQuery.orderBy([\n { column: `order_${index}.value`, order, nulls: 'last' },\n ]);\n } else {\n // sqlite and mysql translate the above statement ONLY into \"order by (value is null) asc\"\n // no matter what the order is, for some reason, so we have to manually add back the statement\n // that translates to \"order by value <order>\" while avoiding to give an order\n entitiesQuery = entitiesQuery.orderBy([\n { column: `order_${index}.value`, order: undefined, nulls: 'last' },\n { column: `order_${index}.value`, order },\n ]);\n }\n });\n\n if (!request?.order) {\n entitiesQuery = entitiesQuery.orderBy('final_entities.entity_ref', 'asc'); // default sort\n } else {\n entitiesQuery.orderBy('final_entities.entity_id', 'asc'); // stable sort\n }\n\n if (limit !== undefined) {\n entitiesQuery = entitiesQuery.limit(limit + 1);\n }\n if (offset !== undefined) {\n entitiesQuery = entitiesQuery.offset(offset);\n }\n\n let rows = await entitiesQuery;\n let pageInfo: DbPageInfo;\n if (limit === undefined || rows.length <= limit) {\n pageInfo = { hasNextPage: false };\n } else {\n rows = rows.slice(0, -1);\n pageInfo = {\n hasNextPage: true,\n endCursor: stringifyPagination({\n limit,\n offset: (offset ?? 0) + limit,\n }),\n };\n }\n\n return {\n entities: processRawEntitiesResult(\n rows.map(r => r.final_entity!),\n this.enableRelationsCompatibility\n ? e => {\n expandLegacyCompoundRelationsInEntity(e);\n if (request?.fields) {\n return request.fields(e);\n }\n return e;\n }\n : request?.fields,\n ),\n pageInfo,\n };\n }\n\n async entitiesBatch(\n request: EntitiesBatchRequest,\n ): Promise<EntitiesBatchResponse> {\n const lookup = new Map<string, string>();\n\n for (const chunk of lodashChunk(request.entityRefs, 200)) {\n let query = this.database<DbFinalEntitiesRow>('final_entities')\n .select({\n entityRef: 'final_entities.entity_ref',\n entity: 'final_entities.final_entity',\n })\n .whereIn('final_entities.entity_ref', chunk);\n\n if (request?.filter) {\n query = applyEntityFilterToQuery({\n filter: request.filter,\n targetQuery: query,\n onEntityIdField: 'final_entities.entity_id',\n knex: this.database,\n });\n }\n\n for (const row of await query) {\n lookup.set(row.entityRef, row.entity ? row.entity : null);\n }\n }\n\n const items = request.entityRefs.map(ref => lookup.get(ref) ?? null);\n\n return { items: processRawEntitiesResult(items, request.fields) };\n }\n\n async queryEntities(\n request: QueryEntitiesRequest,\n ): Promise<QueryEntitiesResponse> {\n const limit = request.limit ?? DEFAULT_LIMIT;\n\n const cursor: Omit<Cursor, 'orderFieldValues'> & {\n orderFieldValues?: (string | null)[];\n skipTotalItems: boolean;\n } = {\n orderFields: [],\n isPrevious: false,\n ...parseCursorFromRequest(request),\n };\n\n // For performance reasons we invoke the count query only on the first\n // request. The result is then embedded into the cursor for subsequent\n // requests. Threfore this can be undefined here, but will then get\n // populated further down.\n const shouldComputeTotalItems =\n cursor.totalItems === undefined && !cursor.skipTotalItems;\n const isFetchingBackwards = cursor.isPrevious;\n\n if (cursor.orderFields.length > 1) {\n this.logger.warn(`Only one sort field is supported, ignoring the rest`);\n }\n\n const sortField = cursor.orderFields.at(0);\n\n // The first part of the query builder is a subquery that applies all of the\n // filtering.\n const dbQuery = this.database.with(\n 'filtered',\n ['entity_id', 'final_entity', ...(sortField ? ['value'] : [])],\n inner => {\n inner\n .from<DbFinalEntitiesRow>('final_entities')\n .whereNotNull('final_entity');\n\n if (sortField) {\n inner\n .distinct()\n .leftOuterJoin('search', qb =>\n qb\n .on('search.entity_id', 'final_entities.entity_id')\n .andOnVal('search.key', sortField.field),\n )\n .select({\n entity_id: 'final_entities.entity_id',\n final_entity: 'final_entities.final_entity',\n value: 'search.value',\n });\n } else {\n inner.select({\n entity_id: 'final_entities.entity_id',\n final_entity: 'final_entities.final_entity',\n });\n }\n\n // Add regular filters, if given\n if (cursor.filter) {\n applyEntityFilterToQuery({\n filter: cursor.filter,\n targetQuery: inner,\n onEntityIdField: 'final_entities.entity_id',\n knex: this.database,\n });\n }\n\n // Add full text search filters, if given\n const normalizedFullTextFilterTerm =\n cursor.fullTextFilter?.term?.trim();\n const textFilterFields = cursor.fullTextFilter?.fields ?? [\n sortField?.field || 'metadata.uid',\n ];\n if (normalizedFullTextFilterTerm) {\n if (\n textFilterFields.length === 1 &&\n textFilterFields[0] === sortField?.field\n ) {\n // If there is one item, apply the like query to the top level query which is already\n // filtered based on the singular sortField.\n inner.andWhereRaw(\n 'search.value like ?',\n `%${normalizedFullTextFilterTerm.toLocaleLowerCase('en-US')}%`,\n );\n } else {\n const matchQuery = this.database<DbSearchRow>('search')\n .select('search.entity_id')\n // textFilterFields must be lowercased to match searchable keys in database, i.e. spec.profile.displayName -> spec.profile.displayname\n .whereIn(\n 'search.key',\n textFilterFields.map(field => field.toLocaleLowerCase('en-US')),\n )\n .andWhere(function keyFilter() {\n this.andWhereRaw(\n 'search.value like ?',\n `%${normalizedFullTextFilterTerm.toLocaleLowerCase(\n 'en-US',\n )}%`,\n );\n });\n inner.andWhere('final_entities.entity_id', 'in', matchQuery);\n }\n }\n },\n );\n\n // Only pay the cost of counting the number of items if needed\n if (shouldComputeTotalItems) {\n // Note the intentional cross join here. The filtered_count dataset is\n // always exactly one row, so it won't grow the result unnecessarily. But\n // it's also important that there IS at least one row, because even if the\n // filtered dataset is empty, we still want to know the total number of\n // items.\n dbQuery\n .with('filtered_count', ['count'], inner =>\n inner.from('filtered').count('*', { as: 'count' }),\n )\n .fromRaw('filtered_count, filtered')\n .select('count', 'filtered.*');\n } else {\n dbQuery.from('filtered').select('*');\n }\n\n const isOrderingDescending = sortField?.order === 'desc';\n\n // Move forward (or backward) in the set to the correct cursor position\n if (cursor.orderFieldValues) {\n if (cursor.orderFieldValues.length === 2) {\n // The first will be the sortField value, the second the entity_id\n const [first, second] = cursor.orderFieldValues;\n dbQuery.andWhere(function nested() {\n this.where(\n 'filtered.value',\n isFetchingBackwards !== isOrderingDescending ? '<' : '>',\n first,\n )\n .orWhere('filtered.value', '=', first)\n .andWhere(\n 'filtered.entity_id',\n isFetchingBackwards !== isOrderingDescending ? '<' : '>',\n second,\n );\n });\n } else if (cursor.orderFieldValues.length === 1) {\n // This will be the entity_id\n const [first] = cursor.orderFieldValues;\n dbQuery.andWhere('entity_id', isFetchingBackwards ? '<' : '>', first);\n }\n }\n\n // Add the ordering\n let order = sortField?.order ?? 'asc';\n if (isFetchingBackwards) {\n order = invertOrder(order);\n }\n if (this.database.client.config.client === 'pg') {\n // pg correctly orders by the column value and handling nulls in one go\n dbQuery.orderBy([\n ...(sortField\n ? [\n {\n column: 'filtered.value',\n order,\n nulls: 'last',\n },\n ]\n : []),\n {\n column: 'filtered.entity_id',\n order,\n },\n ]);\n } else {\n // sqlite and mysql translate the above statement ONLY into \"order by (value is null) asc\"\n // no matter what the order is, for some reason, so we have to manually add back the statement\n // that translates to \"order by value <order>\" while avoiding to give an order\n dbQuery.orderBy([\n ...(sortField\n ? [\n {\n column: 'filtered.value',\n order: undefined,\n nulls: 'last',\n },\n {\n column: 'filtered.value',\n order,\n },\n ]\n : []),\n {\n column: 'filtered.entity_id',\n order,\n },\n ]);\n }\n\n // Apply a manually set initial offset\n if (\n isQueryEntitiesInitialRequest(request) &&\n request.offset !== undefined\n ) {\n dbQuery.offset(request.offset);\n }\n // fetch an extra item to check if there are more items.\n dbQuery.limit(isFetchingBackwards ? limit : limit + 1);\n\n const rows = shouldComputeTotalItems || limit > 0 ? await dbQuery : [];\n\n let totalItems: number;\n if (cursor.totalItems !== undefined) {\n totalItems = cursor.totalItems;\n } else if (cursor.skipTotalItems) {\n totalItems = 0;\n } else if (rows.length) {\n totalItems = Number(rows[0].count);\n } else {\n totalItems = 0;\n }\n\n if (isFetchingBackwards) {\n rows.reverse();\n }\n const hasMoreResults =\n limit > 0 && (isFetchingBackwards || rows.length > limit);\n\n // discard the extra item only when fetching forward.\n if (rows.length > limit) {\n rows.length -= 1;\n }\n\n const isInitialRequest = cursor.firstSortFieldValues === undefined;\n\n const firstRow = rows[0];\n const lastRow = rows[rows.length - 1];\n\n const firstSortFieldValues =\n cursor.firstSortFieldValues || sortFieldsFromRow(firstRow, sortField);\n\n const nextCursor: Cursor | undefined = hasMoreResults\n ? {\n ...cursor,\n orderFieldValues: sortFieldsFromRow(lastRow, sortField),\n firstSortFieldValues,\n isPrevious: false,\n totalItems,\n }\n : undefined;\n\n const prevCursor: Cursor | undefined =\n !isInitialRequest &&\n rows.length > 0 &&\n !isEqual(\n sortFieldsFromRow(firstRow, sortField),\n cursor.firstSortFieldValues,\n )\n ? {\n ...cursor,\n orderFieldValues: sortFieldsFromRow(firstRow, sortField),\n firstSortFieldValues: cursor.firstSortFieldValues,\n isPrevious: true,\n totalItems,\n }\n : undefined;\n\n return {\n items: processRawEntitiesResult(\n rows.map(r => r.final_entity!),\n request.fields,\n ),\n pageInfo: {\n ...(!!prevCursor && { prevCursor }),\n ...(!!nextCursor && { nextCursor }),\n },\n totalItems,\n };\n }\n\n async removeEntityByUid(uid: string): Promise<void> {\n const dbConfig = this.database.client.config;\n\n // Clear the hashed state of the immediate parents of the deleted entity.\n // This makes sure that when they get reprocessed, their output is written\n // down again. The reason for wanting to do this, is that if the user\n // deletes entities that ARE still emitted by the parent, the parent\n // processing will still generate the same output hash as always, which\n // means it'll never try to write down the children again (it assumes that\n // they already exist). This means that without the code below, the database\n // never \"heals\" from accidental deletes.\n if (dbConfig.client.includes('mysql')) {\n // MySQL doesn't support the syntax we need to do this in a single query,\n // http://dev.mysql.com/doc/refman/5.6/en/update.html\n const results = await this.database<DbRefreshStateRow>('refresh_state')\n .select('entity_id')\n .whereIn('entity_ref', function parents(builder) {\n return builder\n .from<DbRefreshStateRow>('refresh_state')\n .innerJoin<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n {\n 'refresh_state_references.target_entity_ref':\n 'refresh_state.entity_ref',\n },\n )\n .where('refresh_state.entity_id', '=', uid)\n .select('refresh_state_references.source_entity_ref');\n });\n await this.database<DbRefreshStateRow>('refresh_state')\n .update({\n result_hash: 'child-was-deleted',\n next_update_at: this.database.fn.now(),\n })\n .whereIn(\n 'entity_id',\n results.map(key => key.entity_id),\n );\n } else {\n await this.database<DbRefreshStateRow>('refresh_state')\n .update({\n result_hash: 'child-was-deleted',\n next_update_at: this.database.fn.now(),\n })\n .whereIn('entity_ref', function parents(builder) {\n return builder\n .from<DbRefreshStateRow>('refresh_state')\n .innerJoin<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n {\n 'refresh_state_references.target_entity_ref':\n 'refresh_state.entity_ref',\n },\n )\n .where('refresh_state.entity_id', '=', uid)\n .select('refresh_state_references.source_entity_ref');\n });\n }\n\n // Stitch the entities that the deleted one had relations to. If we do not\n // do this, the entities in the other end of the relations will still look\n // like they have a relation to the entity that was deleted, despite not\n // having any corresponding rows in the relations table.\n const relationPeers = await this.database\n .from<DbRelationsRow>('relations')\n .innerJoin<DbRefreshStateReferencesRow>('refresh_state', {\n 'refresh_state.entity_ref': 'relations.target_entity_ref',\n })\n .where('relations.originating_entity_id', '=', uid)\n .andWhere('refresh_state.entity_id', '!=', uid)\n .select({ ref: 'relations.target_entity_ref' })\n .union(other =>\n other\n .from<DbRelationsRow>('relations')\n .innerJoin<DbRefreshStateReferencesRow>('refresh_state', {\n 'refresh_state.entity_ref': 'relations.source_entity_ref',\n })\n .where('relations.originating_entity_id', '=', uid)\n .andWhere('refresh_state.entity_id', '!=', uid)\n .select({ ref: 'relations.source_entity_ref' }),\n );\n\n await this.database<DbRefreshStateRow>('refresh_state')\n .where('entity_id', uid)\n .delete();\n\n await this.stitcher.stitch({\n entityRefs: new Set(relationPeers.map(p => p.ref)),\n });\n }\n\n async entityAncestry(rootRef: string): Promise<EntityAncestryResponse> {\n const [rootRow] = await this.database<DbFinalEntitiesRow>('final_entities')\n .where('final_entities.entity_ref', '=', rootRef)\n .select({\n entityJson: 'final_entities.final_entity',\n });\n\n if (!rootRow) {\n throw new NotFoundError(`No such entity ${rootRef}`);\n }\n\n const rootEntity = JSON.parse(rootRow.entityJson) as Entity;\n const seenEntityRefs = new Set<string>();\n const todo = new Array<Entity>();\n const items = new Array<{ entity: Entity; parentEntityRefs: string[] }>();\n\n for (\n let current: Entity | undefined = rootEntity;\n current;\n current = todo.pop()\n ) {\n const currentRef = stringifyEntityRef(current);\n seenEntityRefs.add(currentRef);\n\n const parentRows = await this.database<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n )\n .innerJoin<DbFinalEntitiesRow>('final_entities', {\n 'refresh_state_references.source_entity_ref':\n 'final_entities.entity_ref',\n })\n .where('refresh_state_references.target_entity_ref', '=', currentRef)\n .select({\n parentEntityRef: 'final_entities.entity_ref',\n parentEntityJson: 'final_entities.final_entity',\n });\n\n const parentRefs: string[] = [];\n for (const { parentEntityRef, parentEntityJson } of parentRows) {\n parentRefs.push(parentEntityRef);\n if (!seenEntityRefs.has(parentEntityRef)) {\n seenEntityRefs.add(parentEntityRef);\n todo.push(JSON.parse(parentEntityJson));\n }\n }\n\n items.push({\n entity: current,\n parentEntityRefs: parentRefs,\n });\n }\n\n return {\n rootEntityRef: stringifyEntityRef(rootEntity),\n items,\n };\n }\n\n async facets(request: EntityFacetsRequest): Promise<EntityFacetsResponse> {\n const query = this.database<DbSearchRow>('search')\n .whereIn(\n 'search.key',\n request.facets.map(f => f.toLocaleLowerCase('en-US')),\n )\n .whereNotNull('search.original_value')\n .select({\n facet: 'search.key',\n value: 'search.original_value',\n count: this.database.raw('count(*)'),\n })\n .groupBy(['search.key', 'search.original_value']);\n\n if (request.filter) {\n applyEntityFilterToQuery({\n filter: request.filter,\n targetQuery: query,\n onEntityIdField: 'search.entity_id',\n knex: this.database,\n });\n }\n\n const rows = await query;\n\n const facets: EntityFacetsResponse['facets'] = {};\n for (const facet of request.facets) {\n const facetLowercase = facet.toLocaleLowerCase('en-US');\n facets[facet] = rows\n .filter(row => row.facet === facetLowercase)\n .map(row => ({\n value: String(row.value),\n count: Number(row.count),\n }));\n }\n\n return { facets };\n }\n}\n\nconst entityFilterParser: z.ZodSchema<EntityFilter> = z.lazy(() =>\n z\n .object({\n key: z.string(),\n values: z.array(z.string()).optional(),\n })\n .or(z.object({ not: entityFilterParser }))\n .or(z.object({ anyOf: z.array(entityFilterParser) }))\n .or(z.object({ allOf: z.array(entityFilterParser) })),\n);\n\nexport const cursorParser: z.ZodSchema<Cursor> = z.object({\n orderFields: z.array(\n z.object({ field: z.string(), order: z.enum(['asc', 'desc']) }),\n ),\n orderFieldValues: z.array(z.string().or(z.null())),\n filter: entityFilterParser.optional(),\n isPrevious: z.boolean(),\n query: z.string().optional(),\n firstSortFieldValues: z.array(z.string().or(z.null())).optional(),\n totalItems: z.number().optional(),\n});\n\nfunction parseCursorFromRequest(\n request?: QueryEntitiesRequest,\n): Partial<Cursor> & { skipTotalItems: boolean } {\n if (isQueryEntitiesInitialRequest(request)) {\n const {\n filter,\n orderFields: sortFields = [],\n fullTextFilter,\n skipTotalItems = false,\n } = request;\n return { filter, orderFields: sortFields, fullTextFilter, skipTotalItems };\n }\n if (isQueryEntitiesCursorRequest(request)) {\n return {\n ...request.cursor,\n // Doesn't matter here\n skipTotalItems: false,\n };\n }\n return {\n skipTotalItems: false,\n };\n}\n\nfunction invertOrder(order: EntityOrder['order']) {\n return order === 'asc' ? 'desc' : 'asc';\n}\n\nfunction sortFieldsFromRow(\n row: DbSearchRow & DbFinalEntitiesRow,\n sortField?: EntityOrder | undefined,\n) {\n return sortField ? [row?.value, row?.entity_id] : [row?.entity_id];\n}\n"],"names":["InputError","applyEntityFilterToQuery","processRawEntitiesResult","expandLegacyCompoundRelationsInEntity","lodashChunk","isQueryEntitiesInitialRequest","isEqual","NotFoundError","stringifyEntityRef","z","isQueryEntitiesCursorRequest"],"mappings":"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"}
1
+ {"version":3,"file":"DefaultEntitiesCatalog.cjs.js","sources":["../../src/service/DefaultEntitiesCatalog.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, stringifyEntityRef } from '@backstage/catalog-model';\nimport { InputError, NotFoundError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport { chunk as lodashChunk, isEqual } from 'lodash';\nimport { z } from 'zod';\nimport {\n Cursor,\n EntitiesBatchRequest,\n EntitiesBatchResponse,\n EntitiesCatalog,\n EntitiesRequest,\n EntitiesResponse,\n EntityAncestryResponse,\n EntityFacetsRequest,\n EntityFacetsResponse,\n EntityOrder,\n EntityPagination,\n QueryEntitiesRequest,\n QueryEntitiesResponse,\n} from '../catalog/types';\nimport {\n DbFinalEntitiesRow,\n DbPageInfo,\n DbRefreshStateReferencesRow,\n DbRefreshStateRow,\n DbRelationsRow,\n DbSearchRow,\n} from '../database/tables';\nimport { Stitcher } from '../stitching/types';\n\nimport {\n expandLegacyCompoundRelationsInEntity,\n isQueryEntitiesCursorRequest,\n isQueryEntitiesInitialRequest,\n} from './util';\nimport { EntityFilter } from '@backstage/plugin-catalog-node';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { applyEntityFilterToQuery } from './request/applyEntityFilterToQuery';\nimport { processRawEntitiesResult } from './response';\n\nconst DEFAULT_LIMIT = 200;\n\nfunction parsePagination(input?: EntityPagination): EntityPagination {\n if (!input) {\n return {};\n }\n\n let { limit, offset } = input;\n\n if (input.after === undefined) {\n return { limit, offset };\n }\n\n let cursor;\n try {\n const json = Buffer.from(input.after, 'base64').toString('utf8');\n cursor = JSON.parse(json);\n } catch {\n throw new InputError('Malformed after cursor, could not be parsed');\n }\n\n if (cursor.limit !== undefined) {\n if (!Number.isInteger(cursor.limit)) {\n throw new InputError('Malformed after cursor, limit was not an number');\n }\n limit = cursor.limit;\n }\n\n if (cursor.offset !== undefined) {\n if (!Number.isInteger(cursor.offset)) {\n throw new InputError('Malformed after cursor, offset was not a number');\n }\n offset = cursor.offset;\n }\n\n return { limit, offset };\n}\n\nfunction stringifyPagination(\n input: Required<Omit<EntityPagination, 'after'>>,\n): string {\n const { limit, offset } = input;\n const json = JSON.stringify({ limit, offset });\n const base64 = Buffer.from(json, 'utf8').toString('base64');\n return base64;\n}\n\nexport class DefaultEntitiesCatalog implements EntitiesCatalog {\n private readonly database: Knex;\n private readonly logger: LoggerService;\n private readonly stitcher: Stitcher;\n private readonly enableRelationsCompatibility: boolean;\n\n constructor(options: {\n database: Knex;\n logger: LoggerService;\n stitcher: Stitcher;\n enableRelationsCompatibility?: boolean;\n }) {\n this.database = options.database;\n this.logger = options.logger;\n this.stitcher = options.stitcher;\n this.enableRelationsCompatibility = Boolean(\n options.enableRelationsCompatibility,\n );\n }\n\n async entities(request?: EntitiesRequest): Promise<EntitiesResponse> {\n const db = this.database;\n const { limit, offset } = parsePagination(request?.pagination);\n\n let entitiesQuery =\n db<DbFinalEntitiesRow>('final_entities').select('final_entities.*');\n\n request?.order?.forEach(({ field }, index) => {\n const alias = `order_${index}`;\n entitiesQuery = entitiesQuery.leftOuterJoin(\n { [alias]: 'search' },\n function search(inner) {\n inner\n .on(`${alias}.entity_id`, 'final_entities.entity_id')\n .andOn(`${alias}.key`, db.raw('?', [field]));\n },\n );\n });\n\n entitiesQuery = entitiesQuery.whereNotNull('final_entities.final_entity');\n\n if (request?.filter) {\n entitiesQuery = applyEntityFilterToQuery({\n filter: request.filter,\n targetQuery: entitiesQuery,\n onEntityIdField: 'final_entities.entity_id',\n knex: db,\n });\n }\n\n request?.order?.forEach(({ order }, index) => {\n if (db.client.config.client === 'pg') {\n // pg correctly orders by the column value and handling nulls in one go\n entitiesQuery = entitiesQuery.orderBy([\n { column: `order_${index}.value`, order, nulls: 'last' },\n ]);\n } else {\n // sqlite and mysql translate the above statement ONLY into \"order by (value is null) asc\"\n // no matter what the order is, for some reason, so we have to manually add back the statement\n // that translates to \"order by value <order>\" while avoiding to give an order\n entitiesQuery = entitiesQuery.orderBy([\n { column: `order_${index}.value`, order: undefined, nulls: 'last' },\n { column: `order_${index}.value`, order },\n ]);\n }\n });\n\n if (!request?.order) {\n entitiesQuery = entitiesQuery.orderBy('final_entities.entity_ref', 'asc'); // default sort\n } else {\n entitiesQuery.orderBy('final_entities.entity_id', 'asc'); // stable sort\n }\n\n if (limit !== undefined) {\n entitiesQuery = entitiesQuery.limit(limit + 1);\n }\n if (offset !== undefined) {\n entitiesQuery = entitiesQuery.offset(offset);\n }\n\n let rows = await entitiesQuery;\n let pageInfo: DbPageInfo;\n if (limit === undefined || rows.length <= limit) {\n pageInfo = { hasNextPage: false };\n } else {\n rows = rows.slice(0, -1);\n pageInfo = {\n hasNextPage: true,\n endCursor: stringifyPagination({\n limit,\n offset: (offset ?? 0) + limit,\n }),\n };\n }\n\n return {\n entities: processRawEntitiesResult(\n rows.map(r => r.final_entity!),\n this.enableRelationsCompatibility\n ? e => {\n expandLegacyCompoundRelationsInEntity(e);\n if (request?.fields) {\n return request.fields(e);\n }\n return e;\n }\n : request?.fields,\n ),\n pageInfo,\n };\n }\n\n async entitiesBatch(\n request: EntitiesBatchRequest,\n ): Promise<EntitiesBatchResponse> {\n const lookup = new Map<string, string>();\n\n for (const chunk of lodashChunk(request.entityRefs, 200)) {\n let query = this.database<DbFinalEntitiesRow>('final_entities')\n .select({\n entityRef: 'final_entities.entity_ref',\n entity: 'final_entities.final_entity',\n })\n .whereIn('final_entities.entity_ref', chunk);\n\n if (request?.filter) {\n query = applyEntityFilterToQuery({\n filter: request.filter,\n targetQuery: query,\n onEntityIdField: 'final_entities.entity_id',\n knex: this.database,\n });\n }\n\n for (const row of await query) {\n lookup.set(row.entityRef, row.entity ? row.entity : null);\n }\n }\n\n const items = request.entityRefs.map(ref => lookup.get(ref) ?? null);\n\n return { items: processRawEntitiesResult(items, request.fields) };\n }\n\n async queryEntities(\n request: QueryEntitiesRequest,\n ): Promise<QueryEntitiesResponse> {\n const limit = request.limit ?? DEFAULT_LIMIT;\n\n const cursor: Omit<Cursor, 'orderFieldValues'> & {\n orderFieldValues?: (string | null)[];\n skipTotalItems: boolean;\n } = {\n orderFields: [],\n isPrevious: false,\n ...parseCursorFromRequest(request),\n };\n\n // For performance reasons we invoke the count query only on the first\n // request. The result is then embedded into the cursor for subsequent\n // requests. Threfore this can be undefined here, but will then get\n // populated further down.\n const shouldComputeTotalItems =\n cursor.totalItems === undefined && !cursor.skipTotalItems;\n const isFetchingBackwards = cursor.isPrevious;\n\n if (cursor.orderFields.length > 1) {\n this.logger.warn(`Only one sort field is supported, ignoring the rest`);\n }\n\n const sortField = cursor.orderFields.at(0);\n\n // The first part of the query builder is a subquery that applies all of the\n // filtering.\n const dbQuery = this.database.with(\n 'filtered',\n ['entity_id', 'final_entity', ...(sortField ? ['value'] : [])],\n inner => {\n inner\n .from<DbFinalEntitiesRow>('final_entities')\n .whereNotNull('final_entity');\n\n if (sortField) {\n inner\n .distinct()\n .leftOuterJoin('search', qb =>\n qb\n .on('search.entity_id', 'final_entities.entity_id')\n .andOnVal('search.key', sortField.field),\n )\n .select({\n entity_id: 'final_entities.entity_id',\n final_entity: 'final_entities.final_entity',\n value: 'search.value',\n });\n } else {\n inner.select({\n entity_id: 'final_entities.entity_id',\n final_entity: 'final_entities.final_entity',\n });\n }\n\n // Add regular filters, if given\n if (cursor.filter) {\n applyEntityFilterToQuery({\n filter: cursor.filter,\n targetQuery: inner,\n onEntityIdField: 'final_entities.entity_id',\n knex: this.database,\n });\n }\n\n // Add full text search filters, if given\n const normalizedFullTextFilterTerm =\n cursor.fullTextFilter?.term?.trim();\n const textFilterFields = cursor.fullTextFilter?.fields ?? [\n sortField?.field || 'metadata.uid',\n ];\n if (normalizedFullTextFilterTerm) {\n if (\n textFilterFields.length === 1 &&\n textFilterFields[0] === sortField?.field\n ) {\n // If there is one item, apply the like query to the top level query which is already\n // filtered based on the singular sortField.\n inner.andWhereRaw(\n 'search.value like ?',\n `%${normalizedFullTextFilterTerm.toLocaleLowerCase('en-US')}%`,\n );\n } else {\n const matchQuery = this.database<DbSearchRow>('search')\n .select('search.entity_id')\n // textFilterFields must be lowercased to match searchable keys in database, i.e. spec.profile.displayName -> spec.profile.displayname\n .whereIn(\n 'search.key',\n textFilterFields.map(field => field.toLocaleLowerCase('en-US')),\n )\n .andWhere(function keyFilter() {\n this.andWhereRaw(\n 'search.value like ?',\n `%${normalizedFullTextFilterTerm.toLocaleLowerCase(\n 'en-US',\n )}%`,\n );\n });\n inner.andWhere('final_entities.entity_id', 'in', matchQuery);\n }\n }\n },\n );\n\n // Only pay the cost of counting the number of items if needed\n if (shouldComputeTotalItems) {\n // Note the intentional cross join here. The filtered_count dataset is\n // always exactly one row, so it won't grow the result unnecessarily. But\n // it's also important that there IS at least one row, because even if the\n // filtered dataset is empty, we still want to know the total number of\n // items.\n dbQuery\n .with('filtered_count', ['count'], inner =>\n inner.from('filtered').count('*', { as: 'count' }),\n )\n .fromRaw('filtered_count, filtered')\n .select('count', 'filtered.*');\n } else {\n dbQuery.from('filtered').select('*');\n }\n\n const isOrderingDescending = sortField?.order === 'desc';\n\n // Move forward (or backward) in the set to the correct cursor position\n if (cursor.orderFieldValues) {\n if (cursor.orderFieldValues.length === 2) {\n // The first will be the sortField value, the second the entity_id\n const [first, second] = cursor.orderFieldValues;\n dbQuery.andWhere(function nested() {\n this.where(\n 'filtered.value',\n isFetchingBackwards !== isOrderingDescending ? '<' : '>',\n first,\n )\n .orWhere('filtered.value', '=', first)\n .andWhere(\n 'filtered.entity_id',\n isFetchingBackwards !== isOrderingDescending ? '<' : '>',\n second,\n );\n });\n } else if (cursor.orderFieldValues.length === 1) {\n // This will be the entity_id\n const [first] = cursor.orderFieldValues;\n dbQuery.andWhere('entity_id', isFetchingBackwards ? '<' : '>', first);\n }\n }\n\n // Add the ordering\n let order = sortField?.order ?? 'asc';\n if (isFetchingBackwards) {\n order = invertOrder(order);\n }\n if (this.database.client.config.client === 'pg') {\n // pg correctly orders by the column value and handling nulls in one go\n dbQuery.orderBy([\n ...(sortField\n ? [\n {\n column: 'filtered.value',\n order,\n nulls: 'last',\n },\n ]\n : []),\n {\n column: 'filtered.entity_id',\n order,\n },\n ]);\n } else {\n // sqlite and mysql translate the above statement ONLY into \"order by (value is null) asc\"\n // no matter what the order is, for some reason, so we have to manually add back the statement\n // that translates to \"order by value <order>\" while avoiding to give an order\n dbQuery.orderBy([\n ...(sortField\n ? [\n {\n column: 'filtered.value',\n order: undefined,\n nulls: 'last',\n },\n {\n column: 'filtered.value',\n order,\n },\n ]\n : []),\n {\n column: 'filtered.entity_id',\n order,\n },\n ]);\n }\n\n // Apply a manually set initial offset\n if (\n isQueryEntitiesInitialRequest(request) &&\n request.offset !== undefined\n ) {\n dbQuery.offset(request.offset);\n }\n // fetch an extra item to check if there are more items.\n dbQuery.limit(isFetchingBackwards ? limit : limit + 1);\n\n const rows = shouldComputeTotalItems || limit > 0 ? await dbQuery : [];\n\n let totalItems: number;\n if (cursor.totalItems !== undefined) {\n totalItems = cursor.totalItems;\n } else if (cursor.skipTotalItems) {\n totalItems = 0;\n } else if (rows.length) {\n totalItems = Number(rows[0].count);\n } else {\n totalItems = 0;\n }\n\n if (isFetchingBackwards) {\n rows.reverse();\n }\n const hasMoreResults =\n limit > 0 && (isFetchingBackwards || rows.length > limit);\n\n // discard the extra item only when fetching forward.\n if (rows.length > limit) {\n rows.length -= 1;\n }\n\n const isInitialRequest = cursor.firstSortFieldValues === undefined;\n\n const firstRow = rows[0];\n const lastRow = rows[rows.length - 1];\n\n const firstSortFieldValues =\n cursor.firstSortFieldValues || sortFieldsFromRow(firstRow, sortField);\n\n const nextCursor: Cursor | undefined = hasMoreResults\n ? {\n ...cursor,\n orderFieldValues: sortFieldsFromRow(lastRow, sortField),\n firstSortFieldValues,\n isPrevious: false,\n totalItems,\n }\n : undefined;\n\n const prevCursor: Cursor | undefined =\n !isInitialRequest &&\n rows.length > 0 &&\n !isEqual(\n sortFieldsFromRow(firstRow, sortField),\n cursor.firstSortFieldValues,\n )\n ? {\n ...cursor,\n orderFieldValues: sortFieldsFromRow(firstRow, sortField),\n firstSortFieldValues: cursor.firstSortFieldValues,\n isPrevious: true,\n totalItems,\n }\n : undefined;\n\n return {\n items: processRawEntitiesResult(\n rows.map(r => r.final_entity!),\n request.fields,\n ),\n pageInfo: {\n ...(!!prevCursor && { prevCursor }),\n ...(!!nextCursor && { nextCursor }),\n },\n totalItems,\n };\n }\n\n async removeEntityByUid(uid: string): Promise<void> {\n const dbConfig = this.database.client.config;\n\n // Clear the hashed state of the immediate parents of the deleted entity.\n // This makes sure that when they get reprocessed, their output is written\n // down again. The reason for wanting to do this, is that if the user\n // deletes entities that ARE still emitted by the parent, the parent\n // processing will still generate the same output hash as always, which\n // means it'll never try to write down the children again (it assumes that\n // they already exist). This means that without the code below, the database\n // never \"heals\" from accidental deletes.\n if (dbConfig.client.includes('mysql')) {\n // MySQL doesn't support the syntax we need to do this in a single query,\n // http://dev.mysql.com/doc/refman/5.6/en/update.html\n const results = await this.database<DbRefreshStateRow>('refresh_state')\n .select('entity_id')\n .whereIn('entity_ref', function parents(builder) {\n return builder\n .from<DbRefreshStateRow>('refresh_state')\n .innerJoin<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n {\n 'refresh_state_references.target_entity_ref':\n 'refresh_state.entity_ref',\n },\n )\n .where('refresh_state.entity_id', '=', uid)\n .select('refresh_state_references.source_entity_ref');\n });\n await this.database<DbRefreshStateRow>('refresh_state')\n .update({\n result_hash: 'child-was-deleted',\n next_update_at: this.database.fn.now(),\n })\n .whereIn(\n 'entity_id',\n results.map(key => key.entity_id),\n );\n } else {\n await this.database<DbRefreshStateRow>('refresh_state')\n .update({\n result_hash: 'child-was-deleted',\n next_update_at: this.database.fn.now(),\n })\n .whereIn('entity_ref', function parents(builder) {\n return builder\n .from<DbRefreshStateRow>('refresh_state')\n .innerJoin<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n {\n 'refresh_state_references.target_entity_ref':\n 'refresh_state.entity_ref',\n },\n )\n .where('refresh_state.entity_id', '=', uid)\n .select('refresh_state_references.source_entity_ref');\n });\n }\n\n // Stitch the entities that the deleted one had relations to. If we do not\n // do this, the entities in the other end of the relations will still look\n // like they have a relation to the entity that was deleted, despite not\n // having any corresponding rows in the relations table.\n const relationPeers = await this.database\n .from<DbRelationsRow>('relations')\n .innerJoin<DbRefreshStateReferencesRow>('refresh_state', {\n 'refresh_state.entity_ref': 'relations.target_entity_ref',\n })\n .where('relations.originating_entity_id', '=', uid)\n .andWhere('refresh_state.entity_id', '!=', uid)\n .select({ ref: 'relations.target_entity_ref' })\n .union(other =>\n other\n .from<DbRelationsRow>('relations')\n .innerJoin<DbRefreshStateReferencesRow>('refresh_state', {\n 'refresh_state.entity_ref': 'relations.source_entity_ref',\n })\n .where('relations.originating_entity_id', '=', uid)\n .andWhere('refresh_state.entity_id', '!=', uid)\n .select({ ref: 'relations.source_entity_ref' }),\n );\n\n await this.database<DbRefreshStateRow>('refresh_state')\n .where('entity_id', uid)\n .delete();\n\n await this.stitcher.stitch({\n entityRefs: new Set(relationPeers.map(p => p.ref)),\n });\n }\n\n async entityAncestry(rootRef: string): Promise<EntityAncestryResponse> {\n const [rootRow] = await this.database<DbFinalEntitiesRow>('final_entities')\n .where('final_entities.entity_ref', '=', rootRef)\n .select({\n entityJson: 'final_entities.final_entity',\n });\n\n if (!rootRow) {\n throw new NotFoundError(`No such entity ${rootRef}`);\n }\n\n const rootEntity = JSON.parse(rootRow.entityJson) as Entity;\n const seenEntityRefs = new Set<string>();\n const todo = new Array<Entity>();\n const items = new Array<{ entity: Entity; parentEntityRefs: string[] }>();\n\n for (\n let current: Entity | undefined = rootEntity;\n current;\n current = todo.pop()\n ) {\n const currentRef = stringifyEntityRef(current);\n seenEntityRefs.add(currentRef);\n\n const parentRows = await this.database<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n )\n .innerJoin<DbFinalEntitiesRow>('final_entities', {\n 'refresh_state_references.source_entity_ref':\n 'final_entities.entity_ref',\n })\n .where('refresh_state_references.target_entity_ref', '=', currentRef)\n .select({\n parentEntityRef: 'final_entities.entity_ref',\n parentEntityJson: 'final_entities.final_entity',\n });\n\n const parentRefs: string[] = [];\n for (const { parentEntityRef, parentEntityJson } of parentRows) {\n parentRefs.push(parentEntityRef);\n if (!seenEntityRefs.has(parentEntityRef)) {\n seenEntityRefs.add(parentEntityRef);\n todo.push(JSON.parse(parentEntityJson));\n }\n }\n\n items.push({\n entity: current,\n parentEntityRefs: parentRefs,\n });\n }\n\n return {\n rootEntityRef: stringifyEntityRef(rootEntity),\n items,\n };\n }\n\n async facets(request: EntityFacetsRequest): Promise<EntityFacetsResponse> {\n const query = this.database<DbSearchRow>('search')\n .whereIn(\n 'search.key',\n request.facets.map(f => f.toLocaleLowerCase('en-US')),\n )\n .whereNotNull('search.original_value')\n .select({\n facet: 'search.key',\n value: 'search.original_value',\n count: this.database.raw('count(*)'),\n })\n .groupBy(['search.key', 'search.original_value']);\n\n if (request.filter) {\n applyEntityFilterToQuery({\n filter: request.filter,\n targetQuery: query,\n onEntityIdField: 'search.entity_id',\n knex: this.database,\n });\n }\n\n const rows = await query;\n\n const facets: EntityFacetsResponse['facets'] = {};\n for (const facet of request.facets) {\n const facetLowercase = facet.toLocaleLowerCase('en-US');\n facets[facet] = rows\n .filter(row => row.facet === facetLowercase)\n .map(row => ({\n value: String(row.value),\n count: Number(row.count),\n }));\n }\n\n return { facets };\n }\n}\n\nconst entityFilterParser: z.ZodSchema<EntityFilter> = z.lazy(() =>\n z\n .object({\n key: z.string(),\n values: z.array(z.string()).optional(),\n })\n .or(z.object({ not: entityFilterParser }))\n .or(z.object({ anyOf: z.array(entityFilterParser) }))\n .or(z.object({ allOf: z.array(entityFilterParser) })),\n);\n\nexport const cursorParser: z.ZodSchema<Cursor> = z.object({\n orderFields: z.array(\n z.object({ field: z.string(), order: z.enum(['asc', 'desc']) }),\n ),\n orderFieldValues: z.array(z.string().or(z.null())),\n filter: entityFilterParser.optional(),\n isPrevious: z.boolean(),\n query: z.string().optional(),\n firstSortFieldValues: z.array(z.string().or(z.null())).optional(),\n totalItems: z.number().optional(),\n});\n\nfunction parseCursorFromRequest(\n request?: QueryEntitiesRequest,\n): Partial<Cursor> & { skipTotalItems: boolean } {\n if (isQueryEntitiesInitialRequest(request)) {\n const {\n filter,\n orderFields: sortFields = [],\n fullTextFilter,\n skipTotalItems = false,\n } = request;\n return { filter, orderFields: sortFields, fullTextFilter, skipTotalItems };\n }\n if (isQueryEntitiesCursorRequest(request)) {\n return {\n ...request.cursor,\n // Doesn't matter here\n skipTotalItems: false,\n };\n }\n return {\n skipTotalItems: false,\n };\n}\n\nfunction invertOrder(order: EntityOrder['order']) {\n return order === 'asc' ? 'desc' : 'asc';\n}\n\nfunction sortFieldsFromRow(\n row: DbSearchRow & DbFinalEntitiesRow,\n sortField?: EntityOrder | undefined,\n) {\n return sortField ? [row?.value, row?.entity_id] : [row?.entity_id];\n}\n"],"names":["InputError","applyEntityFilterToQuery","processRawEntitiesResult","expandLegacyCompoundRelationsInEntity","lodashChunk","isQueryEntitiesInitialRequest","isEqual","NotFoundError","stringifyEntityRef","z","isQueryEntitiesCursorRequest"],"mappings":"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"}
@@ -136,7 +136,7 @@ async function createRouter(options) {
136
136
  skipTotalItems: true
137
137
  } : { credentials, fields, limit, cursor }
138
138
  );
139
- if (await currentWrite) {
139
+ if (await currentWrite === "closed") {
140
140
  return;
141
141
  }
142
142
  if (result.items.entities.length) {
@@ -1 +1 @@
1
- {"version":3,"file":"createRouter.cjs.js","sources":["../../src/service/createRouter.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 {\n AuditorService,\n AuthService,\n HttpAuthService,\n LoggerService,\n PermissionsService,\n SchedulerService,\n} from '@backstage/backend-plugin-api';\nimport {\n ANNOTATION_LOCATION,\n ANNOTATION_ORIGIN_LOCATION,\n Entity,\n parseLocationRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { InputError, serializeError } from '@backstage/errors';\nimport { LocationAnalyzer } from '@backstage/plugin-catalog-node';\nimport express from 'express';\nimport yn from 'yn';\nimport { z } from 'zod';\nimport { Cursor, EntitiesCatalog } from '../catalog/types';\nimport { CatalogProcessingOrchestrator } from '../processing/types';\nimport { validateEntityEnvelope } from '../processing/util';\nimport { createOpenApiRouter } from '../schema/openapi';\nimport { AuthorizedValidationService } from './AuthorizedValidationService';\nimport {\n basicEntityFilter,\n entitiesBatchRequest,\n parseEntityFilterParams,\n parseEntityTransformParams,\n parseQueryEntitiesParams,\n} from './request';\nimport { parseEntityFacetParams } from './request/parseEntityFacetParams';\nimport { parseEntityOrderParams } from './request/parseEntityOrderParams';\nimport { parseEntityPaginationParams } from './request/parseEntityPaginationParams';\nimport {\n createEntityArrayJsonStream,\n writeEntitiesResponse,\n writeSingleEntityResponse,\n} from './response';\nimport { LocationService, RefreshService } from './types';\nimport {\n disallowReadonlyMode,\n encodeCursor,\n locationInput,\n validateRequestBody,\n} from './util';\n\n/**\n * Options used by {@link createRouter}.\n */\nexport interface RouterOptions {\n entitiesCatalog?: EntitiesCatalog;\n locationAnalyzer?: LocationAnalyzer;\n locationService: LocationService;\n orchestrator?: CatalogProcessingOrchestrator;\n refreshService?: RefreshService;\n scheduler?: SchedulerService;\n logger: LoggerService;\n config: Config;\n permissionIntegrationRouter?: express.Router;\n auth: AuthService;\n httpAuth: HttpAuthService;\n permissionsService: PermissionsService;\n // TODO: Require AuditorService once `backend-legacy` is removed\n auditor?: AuditorService;\n enableRelationsCompatibility?: boolean;\n}\n\n/**\n * Creates a catalog router.\n */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const router = await createOpenApiRouter({\n validatorOptions: {\n // We want the spec to be up to date with the expected value, but the return type needs\n // to be controlled by the router implementation not the request validator.\n ignorePaths: /^\\/validate-entity\\/?$/,\n },\n });\n const {\n entitiesCatalog,\n locationAnalyzer,\n locationService,\n orchestrator,\n refreshService,\n config,\n logger,\n permissionIntegrationRouter,\n permissionsService,\n auth,\n httpAuth,\n auditor,\n enableRelationsCompatibility = false,\n } = options;\n\n const readonlyEnabled =\n config.getOptionalBoolean('catalog.readonly') || false;\n if (readonlyEnabled) {\n logger.info('Catalog is running in readonly mode');\n }\n\n if (refreshService) {\n // TODO: Potentially find a way to track the ancestor that gets refreshed to refresh this entity (as well as the child of that ancestor?)\n router.post('/refresh', async (req, res) => {\n const { authorizationToken, ...restBody } = req.body;\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-mutate',\n severityLevel: 'medium',\n meta: {\n queryType: 'refresh',\n entityRef: restBody.entityRef,\n },\n request: req,\n });\n\n try {\n const credentials = authorizationToken\n ? await auth.authenticate(authorizationToken)\n : await httpAuth.credentials(req);\n\n await refreshService.refresh({\n ...restBody,\n credentials,\n });\n\n await auditorEvent?.success();\n res.status(200).end();\n } catch (err) {\n await auditorEvent?.fail({ error: err });\n throw err;\n }\n });\n }\n\n if (permissionIntegrationRouter) {\n router.use(permissionIntegrationRouter);\n }\n\n if (entitiesCatalog) {\n router\n .get('/entities', async (req, res) => {\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-fetch',\n request: req,\n meta: {\n queryType: 'all',\n query: req.query,\n },\n });\n\n try {\n const filter = parseEntityFilterParams(req.query);\n const fields = parseEntityTransformParams(req.query);\n const order = parseEntityOrderParams(req.query);\n const pagination = parseEntityPaginationParams(req.query);\n const credentials = await httpAuth.credentials(req);\n\n // When pagination parameters are passed in, use the legacy slow path\n // that loads all entities into memory\n\n if (pagination || enableRelationsCompatibility === true) {\n const { entities, pageInfo } = await entitiesCatalog.entities({\n filter,\n fields,\n order,\n pagination,\n credentials,\n });\n\n // Add a Link header to the next page\n if (pageInfo.hasNextPage) {\n const url = new URL(`http://ignored${req.url}`);\n url.searchParams.delete('offset');\n url.searchParams.set('after', pageInfo.endCursor);\n res.setHeader(\n 'link',\n `<${url.pathname}${url.search}>; rel=\"next\"`,\n );\n }\n\n await auditorEvent?.success();\n\n await writeEntitiesResponse({\n res,\n items: entities,\n alwaysUseObjectMode: enableRelationsCompatibility,\n });\n return;\n }\n\n const responseStream = createEntityArrayJsonStream(res);\n const limit = 10000;\n let cursor: Cursor | undefined;\n\n try {\n let currentWrite: Promise<boolean> | undefined = undefined;\n do {\n const result = await entitiesCatalog.queryEntities(\n !cursor\n ? {\n credentials,\n fields,\n limit,\n filter,\n orderFields: order,\n skipTotalItems: true,\n }\n : { credentials, fields, limit, cursor },\n );\n\n // Wait for previous write to complete\n if (await currentWrite) {\n return; // Client closed connection\n }\n\n if (result.items.entities.length) {\n currentWrite = responseStream.send(result.items);\n }\n\n cursor = result.pageInfo?.nextCursor;\n } while (cursor);\n\n // Wait for last write to complete\n await currentWrite;\n\n await auditorEvent?.success();\n\n responseStream.complete();\n } finally {\n responseStream.close();\n }\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .get('/entities/by-query', async (req, res) => {\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-fetch',\n request: req,\n meta: {\n queryType: 'by-query',\n },\n });\n\n try {\n const { items, pageInfo, totalItems } =\n await entitiesCatalog.queryEntities({\n limit: req.query.limit,\n offset: req.query.offset,\n ...parseQueryEntitiesParams(req.query),\n credentials: await httpAuth.credentials(req),\n });\n\n const meta = {\n totalItems,\n pageInfo: {\n ...(pageInfo.nextCursor && {\n nextCursor: encodeCursor(pageInfo.nextCursor),\n }),\n ...(pageInfo.prevCursor && {\n prevCursor: encodeCursor(pageInfo.prevCursor),\n }),\n },\n };\n\n await auditorEvent?.success({\n // Let's not log out the entities since this can make the log very big\n meta,\n });\n\n await writeEntitiesResponse({\n res,\n items,\n alwaysUseObjectMode: enableRelationsCompatibility,\n responseWrapper: entities => ({\n items: entities,\n ...meta,\n }),\n });\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .get('/entities/by-uid/:uid', async (req, res) => {\n const { uid } = req.params;\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-fetch',\n request: req,\n meta: {\n queryType: 'by-uid',\n uid: uid,\n },\n });\n\n try {\n const { entities } = await entitiesCatalog.entities({\n filter: basicEntityFilter({ 'metadata.uid': uid }),\n credentials: await httpAuth.credentials(req),\n });\n\n writeSingleEntityResponse(res, entities, `No entity with uid ${uid}`);\n\n await auditorEvent?.success({\n meta: {\n // stringify to entity refs\n entities: entities.entities.reduce((arr, element) => {\n if (!element) {\n return arr;\n }\n\n if (typeof element === 'string') {\n arr.push(element);\n return arr;\n }\n\n arr.push(stringifyEntityRef(element));\n return arr;\n }, [] as string[]),\n },\n });\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .delete('/entities/by-uid/:uid', async (req, res) => {\n const { uid } = req.params;\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-mutate',\n severityLevel: 'medium',\n request: req,\n meta: {\n actionType: 'delete',\n uid: uid,\n },\n });\n\n try {\n await entitiesCatalog.removeEntityByUid(uid, {\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success();\n\n res.status(204).end();\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .get('/entities/by-name/:kind/:namespace/:name', async (req, res) => {\n const { kind, namespace, name } = req.params;\n const entityRef = stringifyEntityRef({ kind, namespace, name });\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-fetch',\n request: req,\n meta: {\n queryType: 'by-name',\n entityRef: entityRef,\n },\n });\n\n try {\n const { items } = await entitiesCatalog.entitiesBatch({\n entityRefs: [stringifyEntityRef({ kind, namespace, name })],\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success();\n\n writeSingleEntityResponse(\n res,\n items,\n `No entity named '${name}' found, with kind '${kind}' in namespace '${namespace}'`,\n );\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .get(\n '/entities/by-name/:kind/:namespace/:name/ancestry',\n async (req, res) => {\n const { kind, namespace, name } = req.params;\n const entityRef = stringifyEntityRef({ kind, namespace, name });\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-fetch',\n request: req,\n meta: {\n actionType: 'ancestry',\n entityRef: entityRef,\n },\n });\n\n try {\n const response = await entitiesCatalog.entityAncestry(entityRef, {\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success({\n meta: {\n rootEntityRef: response.rootEntityRef,\n ancestry: response.items.map(ancestryLink => {\n return {\n entityRef: stringifyEntityRef(ancestryLink.entity),\n parentEntityRefs: ancestryLink.parentEntityRefs,\n };\n }),\n },\n });\n\n res.status(200).json(response);\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n },\n )\n .post('/entities/by-refs', async (req, res) => {\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-fetch',\n request: req,\n meta: {\n queryType: 'by-refs',\n },\n });\n\n try {\n const request = entitiesBatchRequest(req);\n const { items } = await entitiesCatalog.entitiesBatch({\n entityRefs: request.entityRefs,\n filter: parseEntityFilterParams(req.query),\n fields: parseEntityTransformParams(req.query, request.fields),\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success({\n meta: {\n ...request,\n },\n });\n\n await writeEntitiesResponse({\n res,\n items,\n alwaysUseObjectMode: enableRelationsCompatibility,\n responseWrapper: entities => ({\n items: entities,\n }),\n });\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .get('/entity-facets', async (req, res) => {\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-facets',\n request: req,\n });\n\n try {\n const response = await entitiesCatalog.facets({\n filter: parseEntityFilterParams(req.query),\n facets: parseEntityFacetParams(req.query),\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success();\n\n res.status(200).json(response);\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n });\n }\n\n if (locationService) {\n router\n .post('/locations', async (req, res) => {\n const location = await validateRequestBody(req, locationInput);\n const dryRun = yn(req.query.dryRun, { default: false });\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'location-mutate',\n severityLevel: dryRun ? 'low' : 'medium',\n request: req,\n meta: {\n actionType: 'create',\n location: location,\n isDryRun: dryRun,\n },\n });\n\n try {\n // when in dryRun addLocation is effectively a read operation so we don't\n // need to disallow readonly\n if (!dryRun) {\n disallowReadonlyMode(readonlyEnabled);\n }\n\n const output = await locationService.createLocation(\n location,\n dryRun,\n {\n credentials: await httpAuth.credentials(req),\n },\n );\n\n await auditorEvent?.success({\n meta: {\n location: output.location,\n },\n });\n\n res.status(201).json(output);\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n meta: {\n location: location,\n isDryRun: dryRun,\n },\n });\n throw err;\n }\n })\n .get('/locations', async (req, res) => {\n const auditorEvent = await auditor?.createEvent({\n eventId: 'location-fetch',\n request: req,\n meta: {\n queryType: 'all',\n },\n });\n\n try {\n const locations = await locationService.listLocations({\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success();\n\n res.status(200).json(locations.map(l => ({ data: l })));\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n\n .get('/locations/:id', async (req, res) => {\n const { id } = req.params;\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'location-fetch',\n request: req,\n meta: {\n queryType: 'by-id',\n id: id,\n },\n });\n\n try {\n const output = await locationService.getLocation(id, {\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success({\n meta: {\n output: output,\n },\n });\n\n res.status(200).json(output);\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .delete('/locations/:id', async (req, res) => {\n const { id } = req.params;\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'location-mutate',\n severityLevel: 'medium',\n request: req,\n meta: {\n actionType: 'delete',\n id: id,\n },\n });\n\n disallowReadonlyMode(readonlyEnabled);\n\n try {\n await locationService.deleteLocation(id, {\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success();\n\n res.status(204).end();\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .get('/locations/by-entity/:kind/:namespace/:name', async (req, res) => {\n const { kind, namespace, name } = req.params;\n const locationRef = `${kind}:${namespace}/${name}`;\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'location-fetch',\n request: req,\n meta: {\n queryType: 'by-entity',\n locationRef: locationRef,\n },\n });\n\n try {\n const output = await locationService.getLocationByEntity(\n { kind, namespace, name },\n { credentials: await httpAuth.credentials(req) },\n );\n\n await auditorEvent?.success({\n meta: {\n output: output,\n },\n });\n\n res.status(200).json(output);\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n });\n }\n\n if (locationAnalyzer) {\n router.post('/analyze-location', async (req, res) => {\n const auditorEvent = await auditor?.createEvent({\n eventId: 'location-analyze',\n request: req,\n });\n\n try {\n const body = await validateRequestBody(\n req,\n z.object({\n location: locationInput,\n catalogFilename: z.string().optional(),\n }),\n );\n const schema = z.object({\n location: locationInput,\n catalogFilename: z.string().optional(),\n });\n const credentials = await httpAuth.credentials(req);\n const parsedBody = schema.parse(body);\n try {\n const output = await locationAnalyzer.analyzeLocation(\n parsedBody,\n credentials,\n );\n\n await auditorEvent?.success({\n meta: {\n output: output,\n },\n });\n\n res.status(200).json(output);\n } catch (err) {\n if (\n // Catch errors from parse-url library.\n err.name === 'Error' &&\n 'subject_url' in err\n ) {\n throw new InputError('The given location.target is not a URL');\n }\n throw err;\n }\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n });\n }\n\n if (orchestrator) {\n router.post('/validate-entity', async (req, res) => {\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-validate',\n request: req,\n });\n\n try {\n const bodySchema = z.object({\n entity: z.unknown(),\n location: z.string(),\n });\n\n let body: z.infer<typeof bodySchema>;\n let entity: Entity;\n let location: { type: string; target: string };\n try {\n body = await validateRequestBody(req, bodySchema);\n entity = validateEntityEnvelope(body.entity);\n location = parseLocationRef(body.location);\n if (location.type !== 'url')\n throw new TypeError(\n `Invalid location ref ${body.location}, only 'url:<target>' is supported, e.g. url:https://host/path`,\n );\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n\n return res.status(400).json({\n errors: [serializeError(err)],\n });\n }\n\n const credentials = await httpAuth.credentials(req);\n const authorizedValidationService = new AuthorizedValidationService(\n orchestrator,\n permissionsService,\n );\n const processingResult = await authorizedValidationService.process(\n {\n entity: {\n ...entity,\n metadata: {\n ...entity.metadata,\n annotations: {\n [ANNOTATION_LOCATION]: body.location,\n [ANNOTATION_ORIGIN_LOCATION]: body.location,\n ...entity.metadata.annotations,\n },\n },\n },\n },\n credentials,\n );\n\n if (!processingResult.ok) {\n const errors = processingResult.errors.map(e => serializeError(e));\n\n await auditorEvent?.fail({\n // TODO(Rugvip): Seems like there aren't proper types for AggregateError yet\n error: (AggregateError as any)(errors, 'Could not validate entity'),\n });\n\n res.status(400).json({\n errors,\n });\n }\n\n await auditorEvent?.success();\n\n return res.status(200).end();\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n });\n }\n\n return router;\n}\n"],"names":["router","createOpenApiRouter","parseEntityFilterParams","parseEntityTransformParams","parseEntityOrderParams","parseEntityPaginationParams","writeEntitiesResponse","createEntityArrayJsonStream","parseQueryEntitiesParams","encodeCursor","basicEntityFilter","writeSingleEntityResponse","stringifyEntityRef","entitiesBatchRequest","parseEntityFacetParams","validateRequestBody","locationInput","yn","disallowReadonlyMode","z","InputError","validateEntityEnvelope","parseLocationRef","serializeError","AuthorizedValidationService","ANNOTATION_LOCATION","ANNOTATION_ORIGIN_LOCATION","errors"],"mappings":"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"}
1
+ {"version":3,"file":"createRouter.cjs.js","sources":["../../src/service/createRouter.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 {\n AuditorService,\n AuthService,\n HttpAuthService,\n LoggerService,\n PermissionsService,\n SchedulerService,\n} from '@backstage/backend-plugin-api';\nimport {\n ANNOTATION_LOCATION,\n ANNOTATION_ORIGIN_LOCATION,\n Entity,\n parseLocationRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { InputError, serializeError } from '@backstage/errors';\nimport { LocationAnalyzer } from '@backstage/plugin-catalog-node';\nimport express from 'express';\nimport yn from 'yn';\nimport { z } from 'zod';\nimport { Cursor, EntitiesCatalog } from '../catalog/types';\nimport { CatalogProcessingOrchestrator } from '../processing/types';\nimport { validateEntityEnvelope } from '../processing/util';\nimport { createOpenApiRouter } from '../schema/openapi';\nimport { AuthorizedValidationService } from './AuthorizedValidationService';\nimport {\n basicEntityFilter,\n entitiesBatchRequest,\n parseEntityFilterParams,\n parseEntityTransformParams,\n parseQueryEntitiesParams,\n} from './request';\nimport { parseEntityFacetParams } from './request/parseEntityFacetParams';\nimport { parseEntityOrderParams } from './request/parseEntityOrderParams';\nimport { parseEntityPaginationParams } from './request/parseEntityPaginationParams';\nimport {\n createEntityArrayJsonStream,\n writeEntitiesResponse,\n writeSingleEntityResponse,\n} from './response';\nimport { LocationService, RefreshService } from './types';\nimport {\n disallowReadonlyMode,\n encodeCursor,\n locationInput,\n validateRequestBody,\n} from './util';\n\n/**\n * Options used by {@link createRouter}.\n */\nexport interface RouterOptions {\n entitiesCatalog?: EntitiesCatalog;\n locationAnalyzer?: LocationAnalyzer;\n locationService: LocationService;\n orchestrator?: CatalogProcessingOrchestrator;\n refreshService?: RefreshService;\n scheduler?: SchedulerService;\n logger: LoggerService;\n config: Config;\n permissionIntegrationRouter?: express.Router;\n auth: AuthService;\n httpAuth: HttpAuthService;\n permissionsService: PermissionsService;\n // TODO: Require AuditorService once `backend-legacy` is removed\n auditor?: AuditorService;\n enableRelationsCompatibility?: boolean;\n}\n\n/**\n * Creates a catalog router.\n */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const router = await createOpenApiRouter({\n validatorOptions: {\n // We want the spec to be up to date with the expected value, but the return type needs\n // to be controlled by the router implementation not the request validator.\n ignorePaths: /^\\/validate-entity\\/?$/,\n },\n });\n const {\n entitiesCatalog,\n locationAnalyzer,\n locationService,\n orchestrator,\n refreshService,\n config,\n logger,\n permissionIntegrationRouter,\n permissionsService,\n auth,\n httpAuth,\n auditor,\n enableRelationsCompatibility = false,\n } = options;\n\n const readonlyEnabled =\n config.getOptionalBoolean('catalog.readonly') || false;\n if (readonlyEnabled) {\n logger.info('Catalog is running in readonly mode');\n }\n\n if (refreshService) {\n // TODO: Potentially find a way to track the ancestor that gets refreshed to refresh this entity (as well as the child of that ancestor?)\n router.post('/refresh', async (req, res) => {\n const { authorizationToken, ...restBody } = req.body;\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-mutate',\n severityLevel: 'medium',\n meta: {\n queryType: 'refresh',\n entityRef: restBody.entityRef,\n },\n request: req,\n });\n\n try {\n const credentials = authorizationToken\n ? await auth.authenticate(authorizationToken)\n : await httpAuth.credentials(req);\n\n await refreshService.refresh({\n ...restBody,\n credentials,\n });\n\n await auditorEvent?.success();\n res.status(200).end();\n } catch (err) {\n await auditorEvent?.fail({ error: err });\n throw err;\n }\n });\n }\n\n if (permissionIntegrationRouter) {\n router.use(permissionIntegrationRouter);\n }\n\n if (entitiesCatalog) {\n router\n .get('/entities', async (req, res) => {\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-fetch',\n request: req,\n meta: {\n queryType: 'all',\n query: req.query,\n },\n });\n\n try {\n const filter = parseEntityFilterParams(req.query);\n const fields = parseEntityTransformParams(req.query);\n const order = parseEntityOrderParams(req.query);\n const pagination = parseEntityPaginationParams(req.query);\n const credentials = await httpAuth.credentials(req);\n\n // When pagination parameters are passed in, use the legacy slow path\n // that loads all entities into memory\n\n if (pagination || enableRelationsCompatibility === true) {\n const { entities, pageInfo } = await entitiesCatalog.entities({\n filter,\n fields,\n order,\n pagination,\n credentials,\n });\n\n // Add a Link header to the next page\n if (pageInfo.hasNextPage) {\n const url = new URL(`http://ignored${req.url}`);\n url.searchParams.delete('offset');\n url.searchParams.set('after', pageInfo.endCursor);\n res.setHeader(\n 'link',\n `<${url.pathname}${url.search}>; rel=\"next\"`,\n );\n }\n\n await auditorEvent?.success();\n\n await writeEntitiesResponse({\n res,\n items: entities,\n alwaysUseObjectMode: enableRelationsCompatibility,\n });\n return;\n }\n\n const responseStream = createEntityArrayJsonStream(res);\n const limit = 10000;\n let cursor: Cursor | undefined;\n\n try {\n let currentWrite: Promise<'ok' | 'closed'> | undefined = undefined;\n do {\n const result = await entitiesCatalog.queryEntities(\n !cursor\n ? {\n credentials,\n fields,\n limit,\n filter,\n orderFields: order,\n skipTotalItems: true,\n }\n : { credentials, fields, limit, cursor },\n );\n\n // Wait for previous write to complete\n if ((await currentWrite) === 'closed') {\n return; // Client closed connection\n }\n\n if (result.items.entities.length) {\n currentWrite = responseStream.send(result.items);\n }\n\n cursor = result.pageInfo?.nextCursor;\n } while (cursor);\n\n // Wait for last write to complete\n await currentWrite;\n\n await auditorEvent?.success();\n\n responseStream.complete();\n } finally {\n responseStream.close();\n }\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .get('/entities/by-query', async (req, res) => {\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-fetch',\n request: req,\n meta: {\n queryType: 'by-query',\n },\n });\n\n try {\n const { items, pageInfo, totalItems } =\n await entitiesCatalog.queryEntities({\n limit: req.query.limit,\n offset: req.query.offset,\n ...parseQueryEntitiesParams(req.query),\n credentials: await httpAuth.credentials(req),\n });\n\n const meta = {\n totalItems,\n pageInfo: {\n ...(pageInfo.nextCursor && {\n nextCursor: encodeCursor(pageInfo.nextCursor),\n }),\n ...(pageInfo.prevCursor && {\n prevCursor: encodeCursor(pageInfo.prevCursor),\n }),\n },\n };\n\n await auditorEvent?.success({\n // Let's not log out the entities since this can make the log very big\n meta,\n });\n\n await writeEntitiesResponse({\n res,\n items,\n alwaysUseObjectMode: enableRelationsCompatibility,\n responseWrapper: entities => ({\n items: entities,\n ...meta,\n }),\n });\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .get('/entities/by-uid/:uid', async (req, res) => {\n const { uid } = req.params;\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-fetch',\n request: req,\n meta: {\n queryType: 'by-uid',\n uid: uid,\n },\n });\n\n try {\n const { entities } = await entitiesCatalog.entities({\n filter: basicEntityFilter({ 'metadata.uid': uid }),\n credentials: await httpAuth.credentials(req),\n });\n\n writeSingleEntityResponse(res, entities, `No entity with uid ${uid}`);\n\n await auditorEvent?.success({\n meta: {\n // stringify to entity refs\n entities: entities.entities.reduce((arr, element) => {\n if (!element) {\n return arr;\n }\n\n if (typeof element === 'string') {\n arr.push(element);\n return arr;\n }\n\n arr.push(stringifyEntityRef(element));\n return arr;\n }, [] as string[]),\n },\n });\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .delete('/entities/by-uid/:uid', async (req, res) => {\n const { uid } = req.params;\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-mutate',\n severityLevel: 'medium',\n request: req,\n meta: {\n actionType: 'delete',\n uid: uid,\n },\n });\n\n try {\n await entitiesCatalog.removeEntityByUid(uid, {\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success();\n\n res.status(204).end();\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .get('/entities/by-name/:kind/:namespace/:name', async (req, res) => {\n const { kind, namespace, name } = req.params;\n const entityRef = stringifyEntityRef({ kind, namespace, name });\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-fetch',\n request: req,\n meta: {\n queryType: 'by-name',\n entityRef: entityRef,\n },\n });\n\n try {\n const { items } = await entitiesCatalog.entitiesBatch({\n entityRefs: [stringifyEntityRef({ kind, namespace, name })],\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success();\n\n writeSingleEntityResponse(\n res,\n items,\n `No entity named '${name}' found, with kind '${kind}' in namespace '${namespace}'`,\n );\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .get(\n '/entities/by-name/:kind/:namespace/:name/ancestry',\n async (req, res) => {\n const { kind, namespace, name } = req.params;\n const entityRef = stringifyEntityRef({ kind, namespace, name });\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-fetch',\n request: req,\n meta: {\n actionType: 'ancestry',\n entityRef: entityRef,\n },\n });\n\n try {\n const response = await entitiesCatalog.entityAncestry(entityRef, {\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success({\n meta: {\n rootEntityRef: response.rootEntityRef,\n ancestry: response.items.map(ancestryLink => {\n return {\n entityRef: stringifyEntityRef(ancestryLink.entity),\n parentEntityRefs: ancestryLink.parentEntityRefs,\n };\n }),\n },\n });\n\n res.status(200).json(response);\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n },\n )\n .post('/entities/by-refs', async (req, res) => {\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-fetch',\n request: req,\n meta: {\n queryType: 'by-refs',\n },\n });\n\n try {\n const request = entitiesBatchRequest(req);\n const { items } = await entitiesCatalog.entitiesBatch({\n entityRefs: request.entityRefs,\n filter: parseEntityFilterParams(req.query),\n fields: parseEntityTransformParams(req.query, request.fields),\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success({\n meta: {\n ...request,\n },\n });\n\n await writeEntitiesResponse({\n res,\n items,\n alwaysUseObjectMode: enableRelationsCompatibility,\n responseWrapper: entities => ({\n items: entities,\n }),\n });\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .get('/entity-facets', async (req, res) => {\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-facets',\n request: req,\n });\n\n try {\n const response = await entitiesCatalog.facets({\n filter: parseEntityFilterParams(req.query),\n facets: parseEntityFacetParams(req.query),\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success();\n\n res.status(200).json(response);\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n });\n }\n\n if (locationService) {\n router\n .post('/locations', async (req, res) => {\n const location = await validateRequestBody(req, locationInput);\n const dryRun = yn(req.query.dryRun, { default: false });\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'location-mutate',\n severityLevel: dryRun ? 'low' : 'medium',\n request: req,\n meta: {\n actionType: 'create',\n location: location,\n isDryRun: dryRun,\n },\n });\n\n try {\n // when in dryRun addLocation is effectively a read operation so we don't\n // need to disallow readonly\n if (!dryRun) {\n disallowReadonlyMode(readonlyEnabled);\n }\n\n const output = await locationService.createLocation(\n location,\n dryRun,\n {\n credentials: await httpAuth.credentials(req),\n },\n );\n\n await auditorEvent?.success({\n meta: {\n location: output.location,\n },\n });\n\n res.status(201).json(output);\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n meta: {\n location: location,\n isDryRun: dryRun,\n },\n });\n throw err;\n }\n })\n .get('/locations', async (req, res) => {\n const auditorEvent = await auditor?.createEvent({\n eventId: 'location-fetch',\n request: req,\n meta: {\n queryType: 'all',\n },\n });\n\n try {\n const locations = await locationService.listLocations({\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success();\n\n res.status(200).json(locations.map(l => ({ data: l })));\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n\n .get('/locations/:id', async (req, res) => {\n const { id } = req.params;\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'location-fetch',\n request: req,\n meta: {\n queryType: 'by-id',\n id: id,\n },\n });\n\n try {\n const output = await locationService.getLocation(id, {\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success({\n meta: {\n output: output,\n },\n });\n\n res.status(200).json(output);\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .delete('/locations/:id', async (req, res) => {\n const { id } = req.params;\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'location-mutate',\n severityLevel: 'medium',\n request: req,\n meta: {\n actionType: 'delete',\n id: id,\n },\n });\n\n disallowReadonlyMode(readonlyEnabled);\n\n try {\n await locationService.deleteLocation(id, {\n credentials: await httpAuth.credentials(req),\n });\n\n await auditorEvent?.success();\n\n res.status(204).end();\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n })\n .get('/locations/by-entity/:kind/:namespace/:name', async (req, res) => {\n const { kind, namespace, name } = req.params;\n const locationRef = `${kind}:${namespace}/${name}`;\n\n const auditorEvent = await auditor?.createEvent({\n eventId: 'location-fetch',\n request: req,\n meta: {\n queryType: 'by-entity',\n locationRef: locationRef,\n },\n });\n\n try {\n const output = await locationService.getLocationByEntity(\n { kind, namespace, name },\n { credentials: await httpAuth.credentials(req) },\n );\n\n await auditorEvent?.success({\n meta: {\n output: output,\n },\n });\n\n res.status(200).json(output);\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n });\n }\n\n if (locationAnalyzer) {\n router.post('/analyze-location', async (req, res) => {\n const auditorEvent = await auditor?.createEvent({\n eventId: 'location-analyze',\n request: req,\n });\n\n try {\n const body = await validateRequestBody(\n req,\n z.object({\n location: locationInput,\n catalogFilename: z.string().optional(),\n }),\n );\n const schema = z.object({\n location: locationInput,\n catalogFilename: z.string().optional(),\n });\n const credentials = await httpAuth.credentials(req);\n const parsedBody = schema.parse(body);\n try {\n const output = await locationAnalyzer.analyzeLocation(\n parsedBody,\n credentials,\n );\n\n await auditorEvent?.success({\n meta: {\n output: output,\n },\n });\n\n res.status(200).json(output);\n } catch (err) {\n if (\n // Catch errors from parse-url library.\n err.name === 'Error' &&\n 'subject_url' in err\n ) {\n throw new InputError('The given location.target is not a URL');\n }\n throw err;\n }\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n });\n }\n\n if (orchestrator) {\n router.post('/validate-entity', async (req, res) => {\n const auditorEvent = await auditor?.createEvent({\n eventId: 'entity-validate',\n request: req,\n });\n\n try {\n const bodySchema = z.object({\n entity: z.unknown(),\n location: z.string(),\n });\n\n let body: z.infer<typeof bodySchema>;\n let entity: Entity;\n let location: { type: string; target: string };\n try {\n body = await validateRequestBody(req, bodySchema);\n entity = validateEntityEnvelope(body.entity);\n location = parseLocationRef(body.location);\n if (location.type !== 'url')\n throw new TypeError(\n `Invalid location ref ${body.location}, only 'url:<target>' is supported, e.g. url:https://host/path`,\n );\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n\n return res.status(400).json({\n errors: [serializeError(err)],\n });\n }\n\n const credentials = await httpAuth.credentials(req);\n const authorizedValidationService = new AuthorizedValidationService(\n orchestrator,\n permissionsService,\n );\n const processingResult = await authorizedValidationService.process(\n {\n entity: {\n ...entity,\n metadata: {\n ...entity.metadata,\n annotations: {\n [ANNOTATION_LOCATION]: body.location,\n [ANNOTATION_ORIGIN_LOCATION]: body.location,\n ...entity.metadata.annotations,\n },\n },\n },\n },\n credentials,\n );\n\n if (!processingResult.ok) {\n const errors = processingResult.errors.map(e => serializeError(e));\n\n await auditorEvent?.fail({\n // TODO(Rugvip): Seems like there aren't proper types for AggregateError yet\n error: (AggregateError as any)(errors, 'Could not validate entity'),\n });\n\n res.status(400).json({\n errors,\n });\n }\n\n await auditorEvent?.success();\n\n return res.status(200).end();\n } catch (err) {\n await auditorEvent?.fail({\n error: err,\n });\n throw err;\n }\n });\n }\n\n return router;\n}\n"],"names":["router","createOpenApiRouter","parseEntityFilterParams","parseEntityTransformParams","parseEntityOrderParams","parseEntityPaginationParams","writeEntitiesResponse","createEntityArrayJsonStream","parseQueryEntitiesParams","encodeCursor","basicEntityFilter","writeSingleEntityResponse","stringifyEntityRef","entitiesBatchRequest","parseEntityFacetParams","validateRequestBody","locationInput","yn","disallowReadonlyMode","z","InputError","validateEntityEnvelope","parseLocationRef","serializeError","AuthorizedValidationService","ANNOTATION_LOCATION","ANNOTATION_ORIGIN_LOCATION","errors"],"mappings":"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"}
@@ -3,6 +3,7 @@
3
3
  var write = require('./write.cjs.js');
4
4
 
5
5
  function createEntityArrayJsonStream(res) {
6
+ const writer = write.createResponseDataWriter(res);
6
7
  const prettyPrint = process.env.NODE_ENV === "development";
7
8
  let firstSend = true;
8
9
  let completed = false;
@@ -17,11 +18,11 @@ function createEntityArrayJsonStream(res) {
17
18
  for (const item of response.entities) {
18
19
  const prefix = firstSend ? "[" : ",";
19
20
  firstSend = false;
20
- if (await write.writeResponseData(res, prefix + item)) {
21
- return true;
21
+ if (await writer(prefix + item) === "closed") {
22
+ return "closed";
22
23
  }
23
24
  }
24
- return false;
25
+ return "ok";
25
26
  }
26
27
  let data;
27
28
  if (prettyPrint) {
@@ -33,7 +34,7 @@ ${data.slice(2, -2)}`;
33
34
  data = firstSend ? data.slice(0, -1) : `,${data.slice(1, -1)}`;
34
35
  }
35
36
  firstSend = false;
36
- return write.writeResponseData(res, data);
37
+ return writer(data);
37
38
  },
38
39
  complete() {
39
40
  if (firstSend) {
@@ -1 +1 @@
1
- {"version":3,"file":"createEntityArrayJsonStream.cjs.js","sources":["../../../src/service/response/createEntityArrayJsonStream.ts"],"sourcesContent":["/*\n * Copyright 2024 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 { EntitiesResponseItems } from '../../catalog/types';\nimport { Response } from 'express';\nimport { writeResponseData } from './write';\n\nexport interface EntityArrayJsonStream {\n send(entities: EntitiesResponseItems): Promise<boolean>;\n complete(): void;\n close(): void;\n}\n\n// Helps stream EntitiesResponseItems[] as a JSON response stream to avoid performance issues\nexport function createEntityArrayJsonStream(\n res: Response,\n): EntityArrayJsonStream {\n // Imitate the httpRouter behavior of pretty-printing in development\n const prettyPrint = process.env.NODE_ENV === 'development';\n let firstSend = true;\n let completed = false;\n\n return {\n async send(response) {\n if (firstSend) {\n res.setHeader('Content-Type', 'application/json; charset=utf-8');\n res.status(200);\n res.flushHeaders();\n }\n\n if (response.type === 'raw') {\n for (const item of response.entities) {\n const prefix = firstSend ? '[' : ',';\n firstSend = false;\n\n if (await writeResponseData(res, prefix + item)) {\n return true;\n }\n }\n return false;\n }\n\n let data: string;\n if (prettyPrint) {\n data = JSON.stringify(response.entities, null, 2);\n data = firstSend ? data.slice(0, -2) : `,\\n${data.slice(2, -2)}`;\n } else {\n data = JSON.stringify(response.entities);\n data = firstSend ? data.slice(0, -1) : `,${data.slice(1, -1)}`;\n }\n\n firstSend = false;\n return writeResponseData(res, data);\n },\n complete() {\n if (firstSend) {\n res.json([]);\n } else {\n res.end(prettyPrint ? '\\n]' : ']', 'utf8');\n }\n completed = true;\n },\n close() {\n if (!completed) {\n res.end();\n }\n },\n };\n}\n"],"names":["writeResponseData"],"mappings":";;;;AA2BO,SAAS,4BACd,GACuB,EAAA;AAEvB,EAAM,MAAA,WAAA,GAAc,OAAQ,CAAA,GAAA,CAAI,QAAa,KAAA,aAAA;AAC7C,EAAA,IAAI,SAAY,GAAA,IAAA;AAChB,EAAA,IAAI,SAAY,GAAA,KAAA;AAEhB,EAAO,OAAA;AAAA,IACL,MAAM,KAAK,QAAU,EAAA;AACnB,MAAA,IAAI,SAAW,EAAA;AACb,QAAI,GAAA,CAAA,SAAA,CAAU,gBAAgB,iCAAiC,CAAA;AAC/D,QAAA,GAAA,CAAI,OAAO,GAAG,CAAA;AACd,QAAA,GAAA,CAAI,YAAa,EAAA;AAAA;AAGnB,MAAI,IAAA,QAAA,CAAS,SAAS,KAAO,EAAA;AAC3B,QAAW,KAAA,MAAA,IAAA,IAAQ,SAAS,QAAU,EAAA;AACpC,UAAM,MAAA,MAAA,GAAS,YAAY,GAAM,GAAA,GAAA;AACjC,UAAY,SAAA,GAAA,KAAA;AAEZ,UAAA,IAAI,MAAMA,uBAAA,CAAkB,GAAK,EAAA,MAAA,GAAS,IAAI,CAAG,EAAA;AAC/C,YAAO,OAAA,IAAA;AAAA;AACT;AAEF,QAAO,OAAA,KAAA;AAAA;AAGT,MAAI,IAAA,IAAA;AACJ,MAAA,IAAI,WAAa,EAAA;AACf,QAAA,IAAA,GAAO,IAAK,CAAA,SAAA,CAAU,QAAS,CAAA,QAAA,EAAU,MAAM,CAAC,CAAA;AAChD,QAAA,IAAA,GAAO,SAAY,GAAA,IAAA,CAAK,KAAM,CAAA,CAAA,EAAG,EAAE,CAAI,GAAA,CAAA;AAAA,EAAM,IAAK,CAAA,KAAA,CAAM,CAAG,EAAA,CAAA,CAAE,CAAC,CAAA,CAAA;AAAA,OACzD,MAAA;AACL,QAAO,IAAA,GAAA,IAAA,CAAK,SAAU,CAAA,QAAA,CAAS,QAAQ,CAAA;AACvC,QAAO,IAAA,GAAA,SAAA,GAAY,IAAK,CAAA,KAAA,CAAM,CAAG,EAAA,CAAA,CAAE,CAAI,GAAA,CAAA,CAAA,EAAI,IAAK,CAAA,KAAA,CAAM,CAAG,EAAA,CAAA,CAAE,CAAC,CAAA,CAAA;AAAA;AAG9D,MAAY,SAAA,GAAA,KAAA;AACZ,MAAO,OAAAA,uBAAA,CAAkB,KAAK,IAAI,CAAA;AAAA,KACpC;AAAA,IACA,QAAW,GAAA;AACT,MAAA,IAAI,SAAW,EAAA;AACb,QAAI,GAAA,CAAA,IAAA,CAAK,EAAE,CAAA;AAAA,OACN,MAAA;AACL,QAAA,GAAA,CAAI,GAAI,CAAA,WAAA,GAAc,KAAQ,GAAA,GAAA,EAAK,MAAM,CAAA;AAAA;AAE3C,MAAY,SAAA,GAAA,IAAA;AAAA,KACd;AAAA,IACA,KAAQ,GAAA;AACN,MAAA,IAAI,CAAC,SAAW,EAAA;AACd,QAAA,GAAA,CAAI,GAAI,EAAA;AAAA;AACV;AACF,GACF;AACF;;;;"}
1
+ {"version":3,"file":"createEntityArrayJsonStream.cjs.js","sources":["../../../src/service/response/createEntityArrayJsonStream.ts"],"sourcesContent":["/*\n * Copyright 2024 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 { EntitiesResponseItems } from '../../catalog/types';\nimport { Response } from 'express';\nimport { createResponseDataWriter } from './write';\n\nexport interface EntityArrayJsonStream {\n send(entities: EntitiesResponseItems): Promise<'ok' | 'closed'>;\n complete(): void;\n close(): void;\n}\n\n// Helps stream EntitiesResponseItems[] as a JSON response stream to avoid performance issues\nexport function createEntityArrayJsonStream(\n res: Response,\n): EntityArrayJsonStream {\n const writer = createResponseDataWriter(res);\n\n // Imitate the httpRouter behavior of pretty-printing in development\n const prettyPrint = process.env.NODE_ENV === 'development';\n let firstSend = true;\n let completed = false;\n\n return {\n async send(response) {\n if (firstSend) {\n res.setHeader('Content-Type', 'application/json; charset=utf-8');\n res.status(200);\n res.flushHeaders();\n }\n\n if (response.type === 'raw') {\n for (const item of response.entities) {\n const prefix = firstSend ? '[' : ',';\n firstSend = false;\n\n if ((await writer(prefix + item)) === 'closed') {\n return 'closed';\n }\n }\n return 'ok';\n }\n\n let data: string;\n if (prettyPrint) {\n data = JSON.stringify(response.entities, null, 2);\n data = firstSend ? data.slice(0, -2) : `,\\n${data.slice(2, -2)}`;\n } else {\n data = JSON.stringify(response.entities);\n data = firstSend ? data.slice(0, -1) : `,${data.slice(1, -1)}`;\n }\n\n firstSend = false;\n return writer(data);\n },\n complete() {\n if (firstSend) {\n res.json([]);\n } else {\n res.end(prettyPrint ? '\\n]' : ']', 'utf8');\n }\n completed = true;\n },\n close() {\n if (!completed) {\n res.end();\n }\n },\n };\n}\n"],"names":["createResponseDataWriter"],"mappings":";;;;AA2BO,SAAS,4BACd,GACuB,EAAA;AACvB,EAAM,MAAA,MAAA,GAASA,+BAAyB,GAAG,CAAA;AAG3C,EAAM,MAAA,WAAA,GAAc,OAAQ,CAAA,GAAA,CAAI,QAAa,KAAA,aAAA;AAC7C,EAAA,IAAI,SAAY,GAAA,IAAA;AAChB,EAAA,IAAI,SAAY,GAAA,KAAA;AAEhB,EAAO,OAAA;AAAA,IACL,MAAM,KAAK,QAAU,EAAA;AACnB,MAAA,IAAI,SAAW,EAAA;AACb,QAAI,GAAA,CAAA,SAAA,CAAU,gBAAgB,iCAAiC,CAAA;AAC/D,QAAA,GAAA,CAAI,OAAO,GAAG,CAAA;AACd,QAAA,GAAA,CAAI,YAAa,EAAA;AAAA;AAGnB,MAAI,IAAA,QAAA,CAAS,SAAS,KAAO,EAAA;AAC3B,QAAW,KAAA,MAAA,IAAA,IAAQ,SAAS,QAAU,EAAA;AACpC,UAAM,MAAA,MAAA,GAAS,YAAY,GAAM,GAAA,GAAA;AACjC,UAAY,SAAA,GAAA,KAAA;AAEZ,UAAA,IAAK,MAAM,MAAA,CAAO,MAAS,GAAA,IAAI,MAAO,QAAU,EAAA;AAC9C,YAAO,OAAA,QAAA;AAAA;AACT;AAEF,QAAO,OAAA,IAAA;AAAA;AAGT,MAAI,IAAA,IAAA;AACJ,MAAA,IAAI,WAAa,EAAA;AACf,QAAA,IAAA,GAAO,IAAK,CAAA,SAAA,CAAU,QAAS,CAAA,QAAA,EAAU,MAAM,CAAC,CAAA;AAChD,QAAA,IAAA,GAAO,SAAY,GAAA,IAAA,CAAK,KAAM,CAAA,CAAA,EAAG,EAAE,CAAI,GAAA,CAAA;AAAA,EAAM,IAAK,CAAA,KAAA,CAAM,CAAG,EAAA,CAAA,CAAE,CAAC,CAAA,CAAA;AAAA,OACzD,MAAA;AACL,QAAO,IAAA,GAAA,IAAA,CAAK,SAAU,CAAA,QAAA,CAAS,QAAQ,CAAA;AACvC,QAAO,IAAA,GAAA,SAAA,GAAY,IAAK,CAAA,KAAA,CAAM,CAAG,EAAA,CAAA,CAAE,CAAI,GAAA,CAAA,CAAA,EAAI,IAAK,CAAA,KAAA,CAAM,CAAG,EAAA,CAAA,CAAE,CAAC,CAAA,CAAA;AAAA;AAG9D,MAAY,SAAA,GAAA,KAAA;AACZ,MAAA,OAAO,OAAO,IAAI,CAAA;AAAA,KACpB;AAAA,IACA,QAAW,GAAA;AACT,MAAA,IAAI,SAAW,EAAA;AACb,QAAI,GAAA,CAAA,IAAA,CAAK,EAAE,CAAA;AAAA,OACN,MAAA;AACL,QAAA,GAAA,CAAI,GAAI,CAAA,WAAA,GAAc,KAAQ,GAAA,GAAA,EAAK,MAAM,CAAA;AAAA;AAE3C,MAAY,SAAA,GAAA,IAAA;AAAA,KACd;AAAA,IACA,KAAQ,GAAA;AACN,MAAA,IAAI,CAAC,SAAW,EAAA;AACd,QAAA,GAAA,CAAI,GAAI,EAAA;AAAA;AACV;AACF,GACF;AACF;;;;"}
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var types = require('@backstage/types');
3
4
  var errors = require('@backstage/errors');
4
5
  var process = require('./process.cjs.js');
5
6
 
@@ -20,6 +21,7 @@ function writeSingleEntityResponse(res, response, notFoundMessage) {
20
21
  }
21
22
  async function writeEntitiesResponse(options) {
22
23
  const { res, responseWrapper, alwaysUseObjectMode } = options;
24
+ const writer = createResponseDataWriter(res);
23
25
  const items = alwaysUseObjectMode ? process.processEntitiesResponseItems(options.items, (e) => e) : options.items;
24
26
  if (items.type === "object") {
25
27
  res.json(
@@ -45,38 +47,45 @@ async function writeEntitiesResponse(options) {
45
47
  for (const entity of items.entities) {
46
48
  const prefix = first ? "[" : ",";
47
49
  first = false;
48
- if (await writeResponseData(res, prefix + entity)) {
50
+ if (await writer(prefix + entity) === "closed") {
49
51
  return;
50
52
  }
51
53
  }
52
54
  res.end(`${first ? "[" : ""}]${trailing}`);
53
55
  }
54
- async function writeResponseData(res, data) {
55
- const ok = res.write(data, "utf8");
56
- if (!ok) {
56
+ function createResponseDataWriter(res) {
57
+ let drainPromise;
58
+ const closePromise = new Promise((resolve) => {
59
+ function onClose() {
60
+ res.off("drain", onDrain);
61
+ res.off("close", onClose);
62
+ resolve("closed");
63
+ }
64
+ function onDrain() {
65
+ drainPromise?.resolve("ok");
66
+ drainPromise = void 0;
67
+ }
68
+ res.on("drain", onDrain);
69
+ res.on("close", onClose);
70
+ });
71
+ return async (data) => {
72
+ if (drainPromise) {
73
+ throw new Error(
74
+ "Attempted overlapping write while waiting for previous write to drain"
75
+ );
76
+ }
77
+ if (res.write(data, "utf8")) {
78
+ return "ok";
79
+ }
57
80
  if (res.closed) {
58
- return true;
81
+ return "closed";
59
82
  }
60
- const closed = await new Promise((resolve) => {
61
- function onContinue() {
62
- res.off("drain", onContinue);
63
- res.off("close", onClose);
64
- resolve(false);
65
- }
66
- function onClose() {
67
- res.off("drain", onContinue);
68
- res.off("close", onClose);
69
- resolve(true);
70
- }
71
- res.on("drain", onContinue);
72
- res.on("close", onClose);
73
- });
74
- return closed;
75
- }
76
- return false;
83
+ drainPromise = types.createDeferred();
84
+ return Promise.race([drainPromise, closePromise]);
85
+ };
77
86
  }
78
87
 
88
+ exports.createResponseDataWriter = createResponseDataWriter;
79
89
  exports.writeEntitiesResponse = writeEntitiesResponse;
80
- exports.writeResponseData = writeResponseData;
81
90
  exports.writeSingleEntityResponse = writeSingleEntityResponse;
82
91
  //# sourceMappingURL=write.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"write.cjs.js","sources":["../../../src/service/response/write.ts"],"sourcesContent":["/*\n * Copyright 2024 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 { Response } from 'express';\nimport { EntitiesResponseItems } from '../../catalog/types';\nimport { JsonValue } from '@backstage/types';\nimport { NotFoundError } from '@backstage/errors';\nimport { processEntitiesResponseItems } from './process';\n\nconst JSON_CONTENT_TYPE = 'application/json; charset=utf-8';\n\nexport function writeSingleEntityResponse(\n res: Response,\n response: EntitiesResponseItems,\n notFoundMessage: string,\n) {\n if (response.type === 'object') {\n if (!response.entities[0]) {\n throw new NotFoundError(notFoundMessage);\n }\n\n res.json(response.entities[0]);\n } else {\n if (!response.entities[0]) {\n throw new NotFoundError(notFoundMessage);\n }\n\n res.setHeader('Content-Type', JSON_CONTENT_TYPE);\n res.end(response.entities[0]);\n }\n}\n\nexport async function writeEntitiesResponse(options: {\n res: Response;\n items: EntitiesResponseItems;\n responseWrapper?: (entities: JsonValue) => JsonValue;\n alwaysUseObjectMode?: boolean;\n}) {\n const { res, responseWrapper, alwaysUseObjectMode } = options;\n const items = alwaysUseObjectMode\n ? processEntitiesResponseItems(options.items, e => e)\n : options.items;\n\n if (items.type === 'object') {\n res.json(\n responseWrapper ? responseWrapper?.(items.entities) : items.entities,\n );\n return;\n }\n\n res.setHeader('Content-Type', JSON_CONTENT_TYPE);\n\n // responseWrapper allows the caller to render the entities within an object\n let trailing = '';\n if (responseWrapper) {\n const marker = `__MARKER_${Math.random().toString(36).slice(2, 10)}__`;\n const wrapped = JSON.stringify(responseWrapper(marker));\n const parts = wrapped.split(`\"${marker}\"`);\n if (parts.length !== 2) {\n throw new Error(\n `Entity items response was incorrectly wrapped into ${parts.length} different parts`,\n );\n }\n res.write(parts[0], 'utf8');\n trailing = parts[1];\n }\n\n let first = true;\n for (const entity of items.entities) {\n const prefix = first ? '[' : ',';\n first = false;\n\n if (await writeResponseData(res, prefix + entity)) {\n return;\n }\n }\n res.end(`${first ? '[' : ''}]${trailing}`);\n}\n\n/**\n * Writes a data to the response and waits if the response buffer needs draining.\n *\n * @internal\n * @returns true if the response was closed while waiting for the buffer to drain\n */\nexport async function writeResponseData(res: Response, data: string | Buffer) {\n const ok = res.write(data, 'utf8');\n if (!ok) {\n if (res.closed) {\n return true;\n }\n const closed = await new Promise<boolean>(resolve => {\n function onContinue() {\n res.off('drain', onContinue);\n res.off('close', onClose);\n resolve(false);\n }\n function onClose() {\n res.off('drain', onContinue);\n res.off('close', onClose);\n resolve(true);\n }\n res.on('drain', onContinue);\n res.on('close', onClose);\n });\n return closed;\n }\n return false;\n}\n"],"names":["NotFoundError","processEntitiesResponseItems"],"mappings":"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"}
1
+ {"version":3,"file":"write.cjs.js","sources":["../../../src/service/response/write.ts"],"sourcesContent":["/*\n * Copyright 2024 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 { Response } from 'express';\nimport { EntitiesResponseItems } from '../../catalog/types';\nimport { createDeferred, DeferredPromise, JsonValue } from '@backstage/types';\nimport { NotFoundError } from '@backstage/errors';\nimport { processEntitiesResponseItems } from './process';\n\nconst JSON_CONTENT_TYPE = 'application/json; charset=utf-8';\n\nexport function writeSingleEntityResponse(\n res: Response,\n response: EntitiesResponseItems,\n notFoundMessage: string,\n) {\n if (response.type === 'object') {\n if (!response.entities[0]) {\n throw new NotFoundError(notFoundMessage);\n }\n\n res.json(response.entities[0]);\n } else {\n if (!response.entities[0]) {\n throw new NotFoundError(notFoundMessage);\n }\n\n res.setHeader('Content-Type', JSON_CONTENT_TYPE);\n res.end(response.entities[0]);\n }\n}\n\nexport async function writeEntitiesResponse(options: {\n res: Response;\n items: EntitiesResponseItems;\n responseWrapper?: (entities: JsonValue) => JsonValue;\n alwaysUseObjectMode?: boolean;\n}) {\n const { res, responseWrapper, alwaysUseObjectMode } = options;\n const writer = createResponseDataWriter(res);\n\n const items = alwaysUseObjectMode\n ? processEntitiesResponseItems(options.items, e => e)\n : options.items;\n\n if (items.type === 'object') {\n res.json(\n responseWrapper ? responseWrapper?.(items.entities) : items.entities,\n );\n return;\n }\n\n res.setHeader('Content-Type', JSON_CONTENT_TYPE);\n\n // responseWrapper allows the caller to render the entities within an object\n let trailing = '';\n if (responseWrapper) {\n const marker = `__MARKER_${Math.random().toString(36).slice(2, 10)}__`;\n const wrapped = JSON.stringify(responseWrapper(marker));\n const parts = wrapped.split(`\"${marker}\"`);\n if (parts.length !== 2) {\n throw new Error(\n `Entity items response was incorrectly wrapped into ${parts.length} different parts`,\n );\n }\n res.write(parts[0], 'utf8');\n trailing = parts[1];\n }\n\n let first = true;\n for (const entity of items.entities) {\n const prefix = first ? '[' : ',';\n first = false;\n\n if ((await writer(prefix + entity)) === 'closed') {\n return;\n }\n }\n res.end(`${first ? '[' : ''}]${trailing}`);\n}\n\n/**\n * Creates a data writer that writes to the response and waits if the response\n * buffer needs draining.\n *\n * @internal\n * @returns A writer function. If a write attempt returns 'closed', the\n * connection has become closed prematurely and the caller should stop trying to\n * write.\n */\nexport function createResponseDataWriter(\n res: Response,\n): (data: string | Buffer) => Promise<'ok' | 'closed'> {\n // See https://github.com/backstage/backstage/issues/30659\n //\n // This code goes to some lengths to just add listeners once at the top,\n // instead of on every need to drain. Hence it is more complex that seems to\n // be necessary, just to avoid listener leaks.\n\n let drainPromise: DeferredPromise<'ok'> | undefined;\n\n const closePromise = new Promise<'closed'>(resolve => {\n function onClose() {\n res.off('drain', onDrain);\n res.off('close', onClose);\n resolve('closed');\n }\n function onDrain() {\n drainPromise?.resolve('ok');\n drainPromise = undefined;\n }\n res.on('drain', onDrain);\n res.on('close', onClose);\n });\n\n return async data => {\n if (drainPromise) {\n throw new Error(\n 'Attempted overlapping write while waiting for previous write to drain',\n );\n }\n\n if (res.write(data, 'utf8')) {\n return 'ok';\n }\n\n if (res.closed) {\n return 'closed';\n }\n\n drainPromise = createDeferred();\n return Promise.race([drainPromise, closePromise]);\n };\n}\n"],"names":["NotFoundError","processEntitiesResponseItems","createDeferred"],"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.0.1-next.0",
3
+ "version": "3.0.1-next.1",
4
4
  "description": "The Backstage backend plugin that provides the Backstage catalog",
5
5
  "backstage": {
6
6
  "role": "backend-plugin",
@@ -73,7 +73,7 @@
73
73
  "test": "backstage-cli package test"
74
74
  },
75
75
  "dependencies": {
76
- "@backstage/backend-openapi-utils": "0.6.0-next.0",
76
+ "@backstage/backend-openapi-utils": "0.6.0-next.1",
77
77
  "@backstage/backend-plugin-api": "1.4.2-next.0",
78
78
  "@backstage/catalog-client": "1.11.0-next.0",
79
79
  "@backstage/catalog-model": "1.7.5",
@@ -108,9 +108,9 @@
108
108
  "devDependencies": {
109
109
  "@backstage/backend-defaults": "0.11.2-next.0",
110
110
  "@backstage/backend-test-utils": "1.7.1-next.0",
111
- "@backstage/cli": "0.33.2-next.0",
111
+ "@backstage/cli": "0.34.0-next.2",
112
112
  "@backstage/plugin-permission-common": "0.9.1",
113
- "@backstage/repo-tools": "0.15.1-next.0",
113
+ "@backstage/repo-tools": "0.15.1-next.1",
114
114
  "@types/core-js": "^2.5.4",
115
115
  "@types/express": "^4.17.6",
116
116
  "@types/git-url-parse": "^9.0.0",