@backstage/plugin-catalog-backend 4.0.0 → 4.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 +36 -0
- package/dist/actions/createGetCatalogEntityAction.cjs.js.map +1 -1
- package/dist/actions/createGetCatalogModelDescriptionAction.cjs.js.map +1 -1
- package/dist/actions/createQueryCatalogEntitiesAction.cjs.js.map +1 -1
- package/dist/actions/createRefreshCatalogEntityAction.cjs.js.map +1 -1
- package/dist/actions/createRegisterCatalogEntitiesAction.cjs.js.map +1 -1
- package/dist/actions/createUnregisterCatalogEntitiesAction.cjs.js.map +1 -1
- package/dist/actions/createValidateEntityAction.cjs.js.map +1 -1
- package/dist/actions/index.cjs.js.map +1 -1
- package/dist/constants.cjs.js.map +1 -1
- package/dist/database/DefaultCatalogDatabase.cjs.js +1 -0
- package/dist/database/DefaultCatalogDatabase.cjs.js.map +1 -1
- package/dist/database/DefaultProcessingDatabase.cjs.js.map +1 -1
- package/dist/database/DefaultProviderDatabase.cjs.js +80 -25
- package/dist/database/DefaultProviderDatabase.cjs.js.map +1 -1
- package/dist/database/conversion.cjs.js.map +1 -1
- package/dist/database/metrics.cjs.js.map +1 -1
- package/dist/database/migrations.cjs.js.map +1 -1
- package/dist/database/operations/provider/deleteWithEagerPruningOfChildren.cjs.js.map +1 -1
- package/dist/database/operations/provider/refreshByRefreshKeys.cjs.js.map +1 -1
- package/dist/database/operations/refreshState/checkLocationKeyConflict.cjs.js.map +1 -1
- package/dist/database/operations/refreshState/insertUnprocessedEntity.cjs.js +1 -1
- package/dist/database/operations/refreshState/insertUnprocessedEntity.cjs.js.map +1 -1
- package/dist/database/operations/refreshState/updateUnprocessedEntity.cjs.js +1 -1
- package/dist/database/operations/refreshState/updateUnprocessedEntity.cjs.js.map +1 -1
- package/dist/database/operations/relations/syncRelations.cjs.js.map +1 -1
- package/dist/database/operations/stitcher/buildEntitySearch.cjs.js.map +1 -1
- package/dist/database/operations/stitcher/getDeferredStitchableEntities.cjs.js.map +1 -1
- package/dist/database/operations/stitcher/markDeferredStitchCompleted.cjs.js.map +1 -1
- package/dist/database/operations/stitcher/markForStitching.cjs.js.map +1 -1
- package/dist/database/operations/stitcher/performStitching.cjs.js.map +1 -1
- package/dist/database/operations/stitcher/syncSearchRows.cjs.js +10 -1
- package/dist/database/operations/stitcher/syncSearchRows.cjs.js.map +1 -1
- package/dist/database/operations/stitcher/util.cjs.js.map +1 -1
- package/dist/database/operations/util/deleteOrphanedEntities.cjs.js.map +1 -1
- package/dist/database/util.cjs.js.map +1 -1
- package/dist/ingestion/CatalogRules.cjs.js.map +1 -1
- package/dist/ingestion/LocationAnalyzer.cjs.js.map +1 -1
- package/dist/model/ModelHolder.cjs.js.map +1 -1
- package/dist/permissions/conditionExports.cjs.js.map +1 -1
- package/dist/permissions/rules/createPropertyRule.cjs.js.map +1 -1
- package/dist/permissions/rules/hasAnnotation.cjs.js.map +1 -1
- package/dist/permissions/rules/hasLabel.cjs.js.map +1 -1
- package/dist/permissions/rules/hasMetadata.cjs.js.map +1 -1
- package/dist/permissions/rules/hasSpec.cjs.js.map +1 -1
- package/dist/permissions/rules/index.cjs.js.map +1 -1
- package/dist/permissions/rules/isEntityKind.cjs.js.map +1 -1
- package/dist/permissions/rules/isEntityOwner.cjs.js.map +1 -1
- package/dist/permissions/rules/util.cjs.js.map +1 -1
- package/dist/processing/DefaultCatalogProcessingEngine.cjs.js.map +1 -1
- package/dist/processing/DefaultCatalogProcessingOrchestrator.cjs.js +4 -4
- package/dist/processing/DefaultCatalogProcessingOrchestrator.cjs.js.map +1 -1
- package/dist/processing/ProcessorCacheManager.cjs.js.map +1 -1
- package/dist/processing/ProcessorOutputCollector.cjs.js.map +1 -1
- package/dist/processing/TaskPipeline.cjs.js.map +1 -1
- package/dist/processing/connectEntityProviders.cjs.js +13 -16
- package/dist/processing/connectEntityProviders.cjs.js.map +1 -1
- package/dist/processing/evictEntitiesFromOrphanedProviders.cjs.js.map +1 -1
- package/dist/processing/refresh.cjs.js.map +1 -1
- package/dist/processing/util.cjs.js.map +1 -1
- package/dist/processors/AnnotateLocationEntityProcessor.cjs.js.map +1 -1
- package/dist/processors/BuiltinKindsEntityProcessor.cjs.js.map +1 -1
- package/dist/processors/FileReaderProcessor.cjs.js.map +1 -1
- package/dist/processors/ModelProcessor.cjs.js.map +1 -1
- package/dist/processors/PlaceholderProcessor.cjs.js.map +1 -1
- package/dist/processors/SchemaValidator.cjs.js.map +1 -1
- package/dist/processors/UrlReaderProcessor.cjs.js.map +1 -1
- package/dist/processors/transformLegacyPolicyToProcessor.cjs.js.map +1 -1
- package/dist/providers/ConfigLocationEntityProvider.cjs.js.map +1 -1
- package/dist/providers/DefaultLocationStore.cjs.js.map +1 -1
- package/dist/providers/GenericScmEventRefreshProvider.cjs.js.map +1 -1
- package/dist/schema/openapi/generated/router.cjs.js.map +1 -1
- package/dist/service/AuthorizedEntitiesCatalog.cjs.js.map +1 -1
- package/dist/service/AuthorizedLocationAnalyzer.cjs.js.map +1 -1
- package/dist/service/AuthorizedLocationService.cjs.js.map +1 -1
- package/dist/service/AuthorizedRefreshService.cjs.js.map +1 -1
- package/dist/service/AuthorizedValidationService.cjs.js.map +1 -1
- package/dist/service/CatalogBuilder.cjs.js.map +1 -1
- package/dist/service/CatalogPlugin.cjs.js.map +1 -1
- package/dist/service/DefaultEntitiesCatalog.cjs.js +4 -4
- package/dist/service/DefaultEntitiesCatalog.cjs.js.map +1 -1
- package/dist/service/DefaultLocationService.cjs.js.map +1 -1
- package/dist/service/DefaultRefreshService.cjs.js.map +1 -1
- package/dist/service/createRouter.cjs.js +2 -0
- package/dist/service/createRouter.cjs.js.map +1 -1
- package/dist/service/request/applyEntityFilterToQuery.cjs.js.map +1 -1
- package/dist/service/request/applyPredicateEntityFilterToQuery.cjs.js.map +1 -1
- package/dist/service/request/basicEntityFilter.cjs.js.map +1 -1
- package/dist/service/request/common.cjs.js.map +1 -1
- package/dist/service/request/entitiesBatchRequest.cjs.js.map +1 -1
- package/dist/service/request/entityFilterToFilterPredicate.cjs.js.map +1 -1
- package/dist/service/request/parseEntityFacetParams.cjs.js.map +1 -1
- package/dist/service/request/parseEntityFacetsQuery.cjs.js.map +1 -1
- package/dist/service/request/parseEntityFilterParams.cjs.js.map +1 -1
- package/dist/service/request/parseEntityOrderFieldParams.cjs.js.map +1 -1
- package/dist/service/request/parseEntityOrderParams.cjs.js.map +1 -1
- package/dist/service/request/parseEntityPaginationParams.cjs.js.map +1 -1
- package/dist/service/request/parseEntityQuery.cjs.js.map +1 -1
- package/dist/service/request/parseEntityTransformParams.cjs.js.map +1 -1
- package/dist/service/request/parseLocationQuery.cjs.js.map +1 -1
- package/dist/service/request/parseQueryEntitiesParams.cjs.js.map +1 -1
- package/dist/service/request/searchSubquery.cjs.js.map +1 -1
- package/dist/service/response/createEntityArrayJsonStream.cjs.js.map +1 -1
- package/dist/service/response/process.cjs.js +18 -5
- package/dist/service/response/process.cjs.js.map +1 -1
- package/dist/service/response/write.cjs.js.map +1 -1
- package/dist/service/util.cjs.js.map +1 -1
- package/dist/stitching/DefaultStitcher.cjs.js.map +1 -1
- package/dist/stitching/progressTracker.cjs.js.map +1 -1
- package/dist/stitching/types.cjs.js.map +1 -1
- package/dist/util/conversion.cjs.js.map +1 -1
- package/dist/util/metrics.cjs.js.map +1 -1
- package/dist/util/opentelemetry.cjs.js.map +1 -1
- package/dist/util/parse.cjs.js.map +1 -1
- package/dist/util/readScmEventHandlingConfig.cjs.js.map +1 -1
- package/package.json +21 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-backend
|
|
2
2
|
|
|
3
|
+
## 4.0.1-next.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @backstage/backend-plugin-api@1.11.0-next.1
|
|
9
|
+
- @backstage/backend-openapi-utils@0.7.3-next.1
|
|
10
|
+
- @backstage/plugin-catalog-node@2.2.6-next.1
|
|
11
|
+
- @backstage/plugin-events-node@0.4.27-next.1
|
|
12
|
+
- @backstage/plugin-permission-node@0.11.5-next.1
|
|
13
|
+
|
|
14
|
+
## 4.0.1-next.0
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- a08e746: Improved large entity provider mutations by yielding to the event loop while preparing database changes.
|
|
19
|
+
- 727d53f: Improve responsiveness to other requests while reading large sets of entities with field selection, without reducing page sizes.
|
|
20
|
+
|
|
21
|
+
Like full-entity responses, projected list responses may now be streamed without a `Content-Length` or automatically generated `ETag` header.
|
|
22
|
+
|
|
23
|
+
- e783f4b: Fixed catalog processing error messages to report stable processor names in minified builds.
|
|
24
|
+
- Updated dependencies
|
|
25
|
+
- @backstage/integration@2.1.3-next.0
|
|
26
|
+
- @backstage/config@1.3.10-next.0
|
|
27
|
+
- @backstage/errors@1.3.2-next.0
|
|
28
|
+
- @backstage/backend-plugin-api@1.10.2-next.0
|
|
29
|
+
- @backstage/filter-predicates@0.1.6-next.0
|
|
30
|
+
- @backstage/plugin-permission-common@0.9.12-next.0
|
|
31
|
+
- @backstage/plugin-permission-node@0.11.5-next.0
|
|
32
|
+
- @backstage/backend-openapi-utils@0.7.3-next.0
|
|
33
|
+
- @backstage/catalog-client@1.16.3-next.0
|
|
34
|
+
- @backstage/catalog-model@1.10.2-next.0
|
|
35
|
+
- @backstage/plugin-catalog-node@2.2.6-next.0
|
|
36
|
+
- @backstage/plugin-events-node@0.4.27-next.0
|
|
37
|
+
- @backstage/plugin-catalog-common@1.2.1-next.0
|
|
38
|
+
|
|
3
39
|
## 4.0.0
|
|
4
40
|
|
|
5
41
|
### Major Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createGetCatalogEntityAction.cjs.js","sources":["../../src/actions/createGetCatalogEntityAction.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { ConflictError, InputError } from '@backstage/errors';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createGetCatalogEntityAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'get-catalog-entity',\n title: 'Get Catalog Entity',\n attributes: {\n destructive: false,\n readOnly: true,\n idempotent: true,\n },\n description: `\nThis allows you to get a single entity from the software catalog.\nEach entity in the software catalog has a unique name, kind, and namespace. The default namespace is \"default\".\nEach entity is identified by a unique entity reference, which is a string of the form \"kind:namespace/name\".\n `,\n schema: {\n input: z =>\n z.object({\n kind: z\n .string()\n .describe(\n 'The kind of the entity to query. If the kind is unknown it can be omitted.',\n )\n .optional(),\n namespace: z\n .string()\n .describe(\n 'The namespace of the entity to query. If the namespace is unknown it can be omitted.',\n )\n .optional(),\n name: z.string().describe('The name of the entity to query'),\n }),\n // TODO: is there a better way to do this?\n output: z => z.object({}).passthrough(),\n },\n action: async ({ input, credentials }) => {\n const filter: Record<string, string> = { 'metadata.name': input.name };\n\n if (input.kind) {\n filter.kind = input.kind;\n }\n\n if (input.namespace) {\n filter['metadata.namespace'] = input.namespace;\n }\n\n const { items } = await catalog.queryEntities(\n { filter },\n {\n credentials,\n },\n );\n\n if (items.length === 0) {\n throw new InputError(`No entity found with name \"${input.name}\"`);\n }\n\n if (items.length > 1) {\n throw new ConflictError(\n `Multiple entities found with name \"${\n input.name\n }\", please provide more specific filters. Entities found: ${items\n .map(item => `\"${stringifyEntityRef(item)}\"`)\n .join(', ')}`,\n );\n }\n\n const [entity] = items;\n\n return {\n output: entity,\n };\n },\n });\n};\n"],"names":["InputError","ConflictError","stringifyEntityRef"],"mappings":";;;;;AAoBO,MAAM,+BAA+B,CAAC;AAAA,EAC3C,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,oBAAA;AAAA,IACN,KAAA,EAAO,oBAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,KAAA;AAAA,MACb,QAAA,EAAU,IAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA;AAAA,IAKb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,IAAA,EAAM,CAAA,CACH,MAAA,EAAO,CACP,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,SAAA,EAAW,CAAA,CACR,MAAA,EAAO,CACP,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO,CAAE,SAAS,iCAAiC;AAAA,OAC5D,CAAA;AAAA;AAAA,MAEH,QAAQ,CAAA,CAAA,KAAK,CAAA,CAAE,OAAO,EAAE,EAAE,WAAA;AAAY,KACxC;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,MAAM,MAAA,GAAiC,EAAE,eAAA,EAAiB,KAAA,CAAM,IAAA,EAAK;AAErE,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,MAAA,CAAO,OAAO,KAAA,CAAM,IAAA;AAAA,MACtB;AAEA,MAAA,IAAI,MAAM,SAAA,EAAW;AACnB,QAAA,MAAA,CAAO,oBAAoB,IAAI,KAAA,CAAM,SAAA;AAAA,MACvC;AAEA,MAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,OAAA,CAAQ,aAAA;AAAA,QAC9B,EAAE,MAAA,EAAO;AAAA,QACT;AAAA,UACE;AAAA;AACF,OACF;AAEA,MAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,QAAA,MAAM,IAAIA,iBAAA,CAAW,CAAA,2BAAA,EAA8B,KAAA,CAAM,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,MAClE;AAEA,MAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,QAAA,MAAM,IAAIC,oBAAA;AAAA,UACR,CAAA,mCAAA,EACE,KAAA,CAAM,IACR,CAAA,yDAAA,EAA4D,MACzD,GAAA,CAAI,CAAA,IAAA,KAAQ,CAAA,CAAA,EAAIC,+BAAA,CAAmB,IAAI,CAAC,CAAA,CAAA,CAAG,CAAA,CAC3C,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,SACf;AAAA,MACF;AAEA,MAAA,MAAM,CAAC,MAAM,CAAA,GAAI,KAAA;AAEjB,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ;AAAA,OACV;AAAA,IACF;AAAA,GACD,CAAA;AACH
|
|
1
|
+
{"version":3,"file":"createGetCatalogEntityAction.cjs.js","sources":["../../src/actions/createGetCatalogEntityAction.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { ConflictError, InputError } from '@backstage/errors';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createGetCatalogEntityAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'get-catalog-entity',\n title: 'Get Catalog Entity',\n attributes: {\n destructive: false,\n readOnly: true,\n idempotent: true,\n },\n description: `\nThis allows you to get a single entity from the software catalog.\nEach entity in the software catalog has a unique name, kind, and namespace. The default namespace is \"default\".\nEach entity is identified by a unique entity reference, which is a string of the form \"kind:namespace/name\".\n `,\n schema: {\n input: z =>\n z.object({\n kind: z\n .string()\n .describe(\n 'The kind of the entity to query. If the kind is unknown it can be omitted.',\n )\n .optional(),\n namespace: z\n .string()\n .describe(\n 'The namespace of the entity to query. If the namespace is unknown it can be omitted.',\n )\n .optional(),\n name: z.string().describe('The name of the entity to query'),\n }),\n // TODO: is there a better way to do this?\n output: z => z.object({}).passthrough(),\n },\n action: async ({ input, credentials }) => {\n const filter: Record<string, string> = { 'metadata.name': input.name };\n\n if (input.kind) {\n filter.kind = input.kind;\n }\n\n if (input.namespace) {\n filter['metadata.namespace'] = input.namespace;\n }\n\n const { items } = await catalog.queryEntities(\n { filter },\n {\n credentials,\n },\n );\n\n if (items.length === 0) {\n throw new InputError(`No entity found with name \"${input.name}\"`);\n }\n\n if (items.length > 1) {\n throw new ConflictError(\n `Multiple entities found with name \"${\n input.name\n }\", please provide more specific filters. Entities found: ${items\n .map(item => `\"${stringifyEntityRef(item)}\"`)\n .join(', ')}`,\n );\n }\n\n const [entity] = items;\n\n return {\n output: entity,\n };\n },\n });\n};\n"],"names":["InputError","ConflictError","stringifyEntityRef"],"mappings":";;;;;AAoBO,MAAM,+BAA+B,CAAC;AAAA,EAC3C,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,oBAAA;AAAA,IACN,KAAA,EAAO,oBAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,KAAA;AAAA,MACb,QAAA,EAAU,IAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA;AAAA,IAKb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,IAAA,EAAM,CAAA,CACH,MAAA,EAAO,CACP,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,SAAA,EAAW,CAAA,CACR,MAAA,EAAO,CACP,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO,CAAE,SAAS,iCAAiC;AAAA,OAC5D,CAAA;AAAA;AAAA,MAEH,QAAQ,CAAA,CAAA,KAAK,CAAA,CAAE,OAAO,EAAE,EAAE,WAAA;AAAY,KACxC;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,MAAM,MAAA,GAAiC,EAAE,eAAA,EAAiB,KAAA,CAAM,IAAA,EAAK;AAErE,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,MAAA,CAAO,OAAO,KAAA,CAAM,IAAA;AAAA,MACtB;AAEA,MAAA,IAAI,MAAM,SAAA,EAAW;AACnB,QAAA,MAAA,CAAO,oBAAoB,IAAI,KAAA,CAAM,SAAA;AAAA,MACvC;AAEA,MAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,OAAA,CAAQ,aAAA;AAAA,QAC9B,EAAE,MAAA,EAAO;AAAA,QACT;AAAA,UACE;AAAA;AACF,OACF;AAEA,MAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,QAAA,MAAM,IAAIA,iBAAA,CAAW,CAAA,2BAAA,EAA8B,KAAA,CAAM,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,MAClE;AAEA,MAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,QAAA,MAAM,IAAIC,oBAAA;AAAA,UACR,CAAA,mCAAA,EACE,KAAA,CAAM,IACR,CAAA,yDAAA,EAA4D,MACzD,GAAA,CAAI,CAAA,IAAA,KAAQ,CAAA,CAAA,EAAIC,+BAAA,CAAmB,IAAI,CAAC,CAAA,CAAA,CAAG,CAAA,CAC3C,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,SACf;AAAA,MACF;AAEA,MAAA,MAAM,CAAC,MAAM,CAAA,GAAI,KAAA;AAEjB,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ;AAAA,OACV;AAAA,IACF;AAAA,GACD,CAAA;AACH;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createGetCatalogModelDescriptionAction.cjs.js","sources":["../../src/actions/createGetCatalogModelDescriptionAction.ts"],"sourcesContent":["/*\n * Copyright 2026 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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport {\n CatalogModel,\n CatalogModelAnnotationSummary,\n CatalogModelKindSummary,\n CatalogModelLabelSummary,\n CatalogModelRelationSummary,\n CatalogModelTagSummary,\n compileCatalogModel,\n defaultCatalogEntityModel,\n} from '@backstage/catalog-model/alpha';\nimport { ModelHolder } from '../model/ModelHolder';\n\n/**\n * Lets users fetch a markdown formatted description of the catalog model. This\n * is useful for informing LLMs how to properly work with it.\n */\nexport const createGetCatalogModelDescriptionAction = ({\n modelHolder,\n actionsRegistry,\n}: {\n modelHolder: ModelHolder | undefined;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'get-catalog-model-description',\n title: 'Get a Catalog Model Description',\n description:\n 'Returns a markdown formatted description of the current catalog model, including all registered entity kinds, annotations, labels, tags, and relations.',\n attributes: {\n destructive: false,\n readOnly: true,\n idempotent: true,\n },\n schema: {\n input: z => z.object({}),\n output: z =>\n z.object({\n description: z\n .string()\n .describe(\n 'Markdown description of the catalog model including entity kinds, spec fields, and relations.',\n ),\n }),\n },\n action: async () => {\n return {\n output: {\n description: describeCatalogModel(modelHolder?.model),\n },\n };\n },\n });\n};\n\n// Compute the default description once (if needed) and cache it\nlet defaultModelDescription: string | undefined;\n\nfunction describeCatalogModel(model: CatalogModel | undefined): string {\n if (!model) {\n if (!defaultModelDescription) {\n defaultModelDescription = describeModel(\n compileCatalogModel([defaultCatalogEntityModel]),\n );\n }\n return defaultModelDescription;\n }\n\n return describeModel(model);\n}\n\nfunction describeModel(model: CatalogModel): string {\n return `# Catalog Model\n\nThe software catalog contains a few key concepts:\n\n* Entities\n * rich objects with various kinds, with different schemas\n * can have relations between each other\n* Locations\n * registered as type/target pairs\n * typically in the form of URLs that the catalog has been tasked with keeping track of\n\n## Entities\n\nThe catalog contains entities of different kinds. Every entity is an object with\nfields \"kind\", \"apiVersion\", \"metadata\", and optionally \"spec\" and \"relations\".\n\nWhen querying the catalog you use dot path notation to address fields, e.g. \"metadata.name\".\nWhen querying for entity relationships, prefer using relations over spec fields, e.g. the\nspecial syntax \"relations.ownedBy\" instead of \"spec.owner\".\n\nThe unique identifying key for an entity is its so called entity reference (or entity ref\nfor short), on the form of \"kind:namespace/name\", e.g. \"component:default/my-service\".\nThese are always uniformly lowercased in the \"en-US\" locale.\n\n## Entity Metadata field\n\nThe \"metadata\" object field on all entities has the same static schema. Some common fields\nthere are:\n\n* \"name\": The name of the entity. Must be unique within the catalog at any given point in time, for any given namespace + kind pair. This value is part of the technical identifier of the entity, and as such it will appear in URLs, database tables, entity references, and similar. It is subject to restrictions regarding what characters are allowed. If you want to use a different, more human readable string with fewer restrictions on it in user interfaces, see the \"title\" field below.\n* \"namespace\" (default value: \"default\"): The namespace that the entity belongs to.\n* \"uid\": A globally unique ID for the entity. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, but the server is free to reject requests that do so in such a way that it breaks semantics.\n* \"etag\": An opaque string that changes for each update operation to any part of the entity, including metadata. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, and the server will then reject the operation if it does not match the current stored value.\n* \"title\": A display name of the entity, to be presented in user interfaces instead of the name property, when available. This field is sometimes useful when the name is cumbersome or ends up being perceived as overly technical. The title generally does not have as stringent format requirements on it, so it may contain special characters and be more explanatory. Do keep it very short though, and avoid situations where a title can be confused with the name of another entity, or where two entities share a title. Note that this is only for display purposes, and may be ignored by some parts of the code. Entity references still always make use of the name property, not the title.\n* \"description\": A short (typically relatively few words, on one line) description of the entity.\n* \"annotations\": A map of annotations on the entity.\n* \"labels\": A map of labels on the entity.\n* \"tags\": A list of tags on the entity.\n* \"links\": A list of links on the entity.\n\n${model.getMetadata().annotations.map(describeAnnotation).join('\\n')}\n${model.getMetadata().labels.map(describeLabel).join('\\n')}\n${model.getMetadata().tags.map(describeTag).join('\\n')}\n\n## Entity Kinds\n\nThe model contains the following entity kinds:\n\n${model.listKinds().map(describeKind).join('\\n')}\n\n## Entity Relations\n\nThe model contains the following entity relations:\n\n${model.listRelations().map(describeRelation).join('\\n')}\n`;\n}\n\nfunction describeKind(kind: CatalogModelKindSummary): string {\n return `### Entity Kind \"${kind.names.kind}\"\n\n* Singular in text: \"${kind.names.singular}\"\n* Plural in text: \"${kind.names.plural}\"\n* Versions:\n${kind.versions\n .map(\n version =>\n ` * apiVersion: \"${version.apiVersion}\"${\n version.specType ? ` (spec.type: \"${version.specType}\")` : ''\n }`,\n )\n .join('\\n')}\n\n${kind.description}\n`;\n}\n\nfunction describeAnnotation(annotation: CatalogModelAnnotationSummary): string {\n const title = annotation.title ? `: ${annotation.title}` : '';\n return `### Annotation \"${annotation.name}\"${title}\n\n${annotation.description}\n`;\n}\n\nfunction describeLabel(label: CatalogModelLabelSummary): string {\n const title = label.title ? `: ${label.title}` : '';\n return `### Label \"${label.name}\"${title}\n\n${label.description}\n`;\n}\n\nfunction describeTag(tag: CatalogModelTagSummary): string {\n const title = tag.title ? `: ${tag.title}` : '';\n return `### Tag \"${tag.name}\"${title}\n\n${tag.description}\n`;\n}\n\nfunction describeRelation(relation: CatalogModelRelationSummary): string {\n return `### Relation \"${relation.forward.type}\": ${relation.forward.title}\n\n* Reverse type: \"${relation.reverse.type}\": ${relation.reverse.title}\n\n${relation.description}\n`;\n}\n"],"names":["compileCatalogModel","defaultCatalogEntityModel"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createGetCatalogModelDescriptionAction.cjs.js","sources":["../../src/actions/createGetCatalogModelDescriptionAction.ts"],"sourcesContent":["/*\n * Copyright 2026 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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport {\n CatalogModel,\n CatalogModelAnnotationSummary,\n CatalogModelKindSummary,\n CatalogModelLabelSummary,\n CatalogModelRelationSummary,\n CatalogModelTagSummary,\n compileCatalogModel,\n defaultCatalogEntityModel,\n} from '@backstage/catalog-model/alpha';\nimport { ModelHolder } from '../model/ModelHolder';\n\n/**\n * Lets users fetch a markdown formatted description of the catalog model. This\n * is useful for informing LLMs how to properly work with it.\n */\nexport const createGetCatalogModelDescriptionAction = ({\n modelHolder,\n actionsRegistry,\n}: {\n modelHolder: ModelHolder | undefined;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'get-catalog-model-description',\n title: 'Get a Catalog Model Description',\n description:\n 'Returns a markdown formatted description of the current catalog model, including all registered entity kinds, annotations, labels, tags, and relations.',\n attributes: {\n destructive: false,\n readOnly: true,\n idempotent: true,\n },\n schema: {\n input: z => z.object({}),\n output: z =>\n z.object({\n description: z\n .string()\n .describe(\n 'Markdown description of the catalog model including entity kinds, spec fields, and relations.',\n ),\n }),\n },\n action: async () => {\n return {\n output: {\n description: describeCatalogModel(modelHolder?.model),\n },\n };\n },\n });\n};\n\n// Compute the default description once (if needed) and cache it\nlet defaultModelDescription: string | undefined;\n\nfunction describeCatalogModel(model: CatalogModel | undefined): string {\n if (!model) {\n if (!defaultModelDescription) {\n defaultModelDescription = describeModel(\n compileCatalogModel([defaultCatalogEntityModel]),\n );\n }\n return defaultModelDescription;\n }\n\n return describeModel(model);\n}\n\nfunction describeModel(model: CatalogModel): string {\n return `# Catalog Model\n\nThe software catalog contains a few key concepts:\n\n* Entities\n * rich objects with various kinds, with different schemas\n * can have relations between each other\n* Locations\n * registered as type/target pairs\n * typically in the form of URLs that the catalog has been tasked with keeping track of\n\n## Entities\n\nThe catalog contains entities of different kinds. Every entity is an object with\nfields \"kind\", \"apiVersion\", \"metadata\", and optionally \"spec\" and \"relations\".\n\nWhen querying the catalog you use dot path notation to address fields, e.g. \"metadata.name\".\nWhen querying for entity relationships, prefer using relations over spec fields, e.g. the\nspecial syntax \"relations.ownedBy\" instead of \"spec.owner\".\n\nThe unique identifying key for an entity is its so called entity reference (or entity ref\nfor short), on the form of \"kind:namespace/name\", e.g. \"component:default/my-service\".\nThese are always uniformly lowercased in the \"en-US\" locale.\n\n## Entity Metadata field\n\nThe \"metadata\" object field on all entities has the same static schema. Some common fields\nthere are:\n\n* \"name\": The name of the entity. Must be unique within the catalog at any given point in time, for any given namespace + kind pair. This value is part of the technical identifier of the entity, and as such it will appear in URLs, database tables, entity references, and similar. It is subject to restrictions regarding what characters are allowed. If you want to use a different, more human readable string with fewer restrictions on it in user interfaces, see the \"title\" field below.\n* \"namespace\" (default value: \"default\"): The namespace that the entity belongs to.\n* \"uid\": A globally unique ID for the entity. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, but the server is free to reject requests that do so in such a way that it breaks semantics.\n* \"etag\": An opaque string that changes for each update operation to any part of the entity, including metadata. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, and the server will then reject the operation if it does not match the current stored value.\n* \"title\": A display name of the entity, to be presented in user interfaces instead of the name property, when available. This field is sometimes useful when the name is cumbersome or ends up being perceived as overly technical. The title generally does not have as stringent format requirements on it, so it may contain special characters and be more explanatory. Do keep it very short though, and avoid situations where a title can be confused with the name of another entity, or where two entities share a title. Note that this is only for display purposes, and may be ignored by some parts of the code. Entity references still always make use of the name property, not the title.\n* \"description\": A short (typically relatively few words, on one line) description of the entity.\n* \"annotations\": A map of annotations on the entity.\n* \"labels\": A map of labels on the entity.\n* \"tags\": A list of tags on the entity.\n* \"links\": A list of links on the entity.\n\n${model.getMetadata().annotations.map(describeAnnotation).join('\\n')}\n${model.getMetadata().labels.map(describeLabel).join('\\n')}\n${model.getMetadata().tags.map(describeTag).join('\\n')}\n\n## Entity Kinds\n\nThe model contains the following entity kinds:\n\n${model.listKinds().map(describeKind).join('\\n')}\n\n## Entity Relations\n\nThe model contains the following entity relations:\n\n${model.listRelations().map(describeRelation).join('\\n')}\n`;\n}\n\nfunction describeKind(kind: CatalogModelKindSummary): string {\n return `### Entity Kind \"${kind.names.kind}\"\n\n* Singular in text: \"${kind.names.singular}\"\n* Plural in text: \"${kind.names.plural}\"\n* Versions:\n${kind.versions\n .map(\n version =>\n ` * apiVersion: \"${version.apiVersion}\"${\n version.specType ? ` (spec.type: \"${version.specType}\")` : ''\n }`,\n )\n .join('\\n')}\n\n${kind.description}\n`;\n}\n\nfunction describeAnnotation(annotation: CatalogModelAnnotationSummary): string {\n const title = annotation.title ? `: ${annotation.title}` : '';\n return `### Annotation \"${annotation.name}\"${title}\n\n${annotation.description}\n`;\n}\n\nfunction describeLabel(label: CatalogModelLabelSummary): string {\n const title = label.title ? `: ${label.title}` : '';\n return `### Label \"${label.name}\"${title}\n\n${label.description}\n`;\n}\n\nfunction describeTag(tag: CatalogModelTagSummary): string {\n const title = tag.title ? `: ${tag.title}` : '';\n return `### Tag \"${tag.name}\"${title}\n\n${tag.description}\n`;\n}\n\nfunction describeRelation(relation: CatalogModelRelationSummary): string {\n return `### Relation \"${relation.forward.type}\": ${relation.forward.title}\n\n* Reverse type: \"${relation.reverse.type}\": ${relation.reverse.title}\n\n${relation.description}\n`;\n}\n"],"names":["compileCatalogModel","defaultCatalogEntityModel"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createQueryCatalogEntitiesAction.cjs.js","sources":["../../src/actions/createQueryCatalogEntitiesAction.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { createZodV3FilterPredicateSchema } from '@backstage/filter-predicates';\n\nconst QUERY_SYNTAX = `\n## Query Syntax\n\nThe query uses predicate expressions with dot-notation field paths.\n\nSimple matching:\n { query: { kind: \"Component\" } }\n { query: { kind: \"Component\", \"spec.type\": \"service\" } }\n\nValue operators:\n { query: { kind: { \"$in\": [\"API\", \"Component\"] } } }\n { query: { \"metadata.annotations.backstage.io/techdocs-ref\": { \"$exists\": true } } }\n { query: { \"metadata.tags\": { \"$contains\": \"java\" } } }\n { query: { \"metadata.name\": { \"$hasPrefix\": \"team-\" } } }\n\nLogical operators:\n { query: { \"$all\": [{ kind: \"Component\" }, { \"spec.lifecycle\": \"production\" }] } }\n { query: { \"$any\": [{ \"spec.type\": \"service\" }, { \"spec.type\": \"website\" }] } }\n { query: { \"$not\": { kind: \"Group\" } } }\n\nQuerying relations - find all entities owned by a specific group:\n { query: { \"relations.ownedby\": \"group:default/team-alpha\" } }\n\nCombined example - find production services or websites with TechDocs:\n { query: { \"$all\": [\n { kind: \"Component\", \"spec.lifecycle\": \"production\" },\n { \"$any\": [{ \"spec.type\": \"service\" }, { \"spec.type\": \"website\" }] },\n { \"metadata.annotations.backstage.io/techdocs-ref\": { \"$exists\": true } }\n ] } }\n\n## Other Options\n\nLimit returned fields: { fields: [\"kind\", \"metadata.name\", \"metadata.namespace\"] }\nSort results: { orderFields: { field: \"metadata.name\", order: \"asc\" } }\nFull text search: { fullTextFilter: { term: \"auth\", fields: [\"metadata.name\", \"metadata.title\"] } }\nPagination: Use limit (e.g. 20) and the returned nextPageCursor for subsequent requests via cursor.\n`;\n\nconst INLINE_MODEL_DESCRIPTION = `\nQuery entities from the Backstage Software Catalog using predicate filters.\n\n## Catalog Model\n\nThe catalog contains entities of different kinds. Every entity has \"kind\", \"apiVersion\", \"metadata\", and optionally \"spec\" and \"relations\". Fields use dot notation for querying.\n\nCommon metadata fields on all entities: name, namespace (default: \"default\"), title, description, labels, annotations, tags (string array), links.\n\nEntity references use the format \"kind:namespace/name\", e.g. \"component:default/my-service\" or \"user:default/jane.doe\".\n\n### Entity Kinds\n\n**Component** - A piece of software such as a service, website, or library.\n spec fields: type (e.g. \"service\", \"website\", \"library\"), lifecycle (e.g. \"production\", \"experimental\", \"deprecated\"), owner (entity ref), system, subcomponentOf, providesApis, consumesApis, dependsOn, dependencyOf.\n\n**API** - An interface that components expose, such as REST APIs or event streams.\n spec fields: type (e.g. \"openapi\", \"asyncapi\", \"graphql\", \"grpc\"), lifecycle, owner (entity ref), definition (the API spec content), system.\n\n**System** - A collection of components, APIs, and resources that together expose some functionality.\n spec fields: owner (entity ref), domain, type.\n\n**Domain** - A grouping of systems that share terminology, domain models, and business purpose.\n spec fields: owner (entity ref), subdomainOf, type.\n\n**Resource** - Infrastructure required to operate a component, such as databases or storage buckets.\n spec fields: type, owner (entity ref), system, dependsOn, dependencyOf.\n\n**Group** - An organizational entity such as a team or business unit.\n spec fields: type (e.g. \"team\", \"business-unit\"), children (entity refs), parent (entity ref), members (entity refs), profile (displayName, email, picture).\n\n**User** - A person, such as an employee or contractor.\n spec fields: memberOf (entity refs), profile (displayName, email, picture).\n\n**Location** - A marker that references other catalog descriptor files to be ingested.\n spec fields: type, target, targets, presence.\n\n### Relations\n\nEntities have bidirectional relations stored in the \"relations\" array. Common relation types: ownedBy/ownerOf, dependsOn/dependencyOf, providesApi/apiProvidedBy, consumesApi/apiConsumedBy, parentOf/childOf, memberOf/hasMember, partOf/hasPart.\n\nRelations can be queried via \"relations.<type>\" e.g. \"relations.ownedby: user:default/jane-doe\". The value there must always be a valid entity reference.\n\nWhen querying for entity relationships, prefer using relations over spec fields. For example, use \"relations.ownedby\" instead of \"spec.owner\" to find entities owned by a particular group or user.\n${QUERY_SYNTAX}`;\n\nconst MODEL_REFERENCE_DESCRIPTION = `\nQuery entities from the Backstage Software Catalog using predicate filters.\n\nFor a complete list of entity kinds, fields, relations, and other queryable attributes available in the catalog, use \\`get-catalog-model-description\\`.\n${QUERY_SYNTAX}`;\n\nexport const createQueryCatalogEntitiesAction = ({\n catalog,\n actionsRegistry,\n useExperimentalCatalogLayersDescriptions,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n useExperimentalCatalogLayersDescriptions?: boolean;\n}) => {\n actionsRegistry.register({\n name: 'query-catalog-entities',\n title: 'Query Catalog Entities',\n attributes: {\n destructive: false,\n readOnly: true,\n idempotent: true,\n },\n description: useExperimentalCatalogLayersDescriptions\n ? MODEL_REFERENCE_DESCRIPTION\n : INLINE_MODEL_DESCRIPTION,\n schema: {\n input: z => {\n // MCP/LLM clients often send numeric arguments as strings. Convert numeric\n // strings to numbers, but leave everything else untouched so Zod rejects the\n // original value (e.g. reports \"received string\" for 'abc' rather than\n // \"received NaN\", and doesn't silently turn true/null/'' into 1/0/0).\n const numericFromString = (value: unknown) => {\n if (typeof value === 'string' && value.trim() !== '') {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : value;\n }\n return value;\n };\n\n return z.object({\n query: createZodV3FilterPredicateSchema(z)\n .optional()\n .describe(\n 'Entity predicate query. Supports field matching, $all, $any, $not, $exists, $in, $contains, and $hasPrefix operators.',\n ),\n fields: z\n .array(z.string())\n .optional()\n .describe(\n 'Specific fields to include in the response. If not provided, all fields are returned. Each entry is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n limit: z\n .preprocess(numericFromString, z.number().int().positive())\n .optional()\n .describe('Maximum number of entities to return at a time.'),\n offset: z\n .preprocess(numericFromString, z.number().int().min(0))\n .optional()\n .describe('Number of entities to skip before returning results.'),\n orderFields: z\n .union([\n z.object({\n field: z\n .string()\n .describe(\n 'Field to order by. The format is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n order: z.enum(['asc', 'desc']).describe('Sort order'),\n }),\n z.array(\n z.object({\n field: z\n .string()\n .describe(\n 'Field to order by. The format is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n order: z.enum(['asc', 'desc']).describe('Sort order'),\n }),\n ),\n ])\n .optional()\n .describe(\n 'Ordering criteria for the results. Can be a single order directive or an array for multi-field sorting.',\n ),\n fullTextFilter: z\n .object({\n term: z.string().describe('Full text search term'),\n fields: z\n .array(z.string())\n .optional()\n .describe(\n 'Fields to search within. Each entry is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n })\n .optional()\n .describe('Full text search criteria'),\n cursor: z\n .string()\n .optional()\n .describe(\n 'Cursor for pagination. This can be used only after the first request with a response containing a cursor. If a cursor is given it takes precedence over `offset`.',\n ),\n });\n },\n output: z =>\n z.object({\n items: z\n .array(z.object({}).passthrough())\n .describe('List of entities'),\n totalItems: z.number().describe('Total number of entities'),\n hasMoreEntities: z\n .boolean()\n .describe('Whether more entities are available'),\n nextPageCursor: z\n .string()\n .optional()\n .describe('Next page cursor used to fetch next page of entities'),\n }),\n },\n action: async ({ input, credentials }) => {\n const response = await catalog.queryEntities(\n {\n ...input,\n query: input.query,\n },\n { credentials },\n );\n\n return {\n output: {\n items: response.items,\n totalItems: response.totalItems,\n hasMoreEntities: !!response.pageInfo.nextCursor,\n nextPageCursor: response.pageInfo.nextCursor,\n },\n };\n },\n });\n};\n"],"names":["createZodV3FilterPredicateSchema"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createQueryCatalogEntitiesAction.cjs.js","sources":["../../src/actions/createQueryCatalogEntitiesAction.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { createZodV3FilterPredicateSchema } from '@backstage/filter-predicates';\n\nconst QUERY_SYNTAX = `\n## Query Syntax\n\nThe query uses predicate expressions with dot-notation field paths.\n\nSimple matching:\n { query: { kind: \"Component\" } }\n { query: { kind: \"Component\", \"spec.type\": \"service\" } }\n\nValue operators:\n { query: { kind: { \"$in\": [\"API\", \"Component\"] } } }\n { query: { \"metadata.annotations.backstage.io/techdocs-ref\": { \"$exists\": true } } }\n { query: { \"metadata.tags\": { \"$contains\": \"java\" } } }\n { query: { \"metadata.name\": { \"$hasPrefix\": \"team-\" } } }\n\nLogical operators:\n { query: { \"$all\": [{ kind: \"Component\" }, { \"spec.lifecycle\": \"production\" }] } }\n { query: { \"$any\": [{ \"spec.type\": \"service\" }, { \"spec.type\": \"website\" }] } }\n { query: { \"$not\": { kind: \"Group\" } } }\n\nQuerying relations - find all entities owned by a specific group:\n { query: { \"relations.ownedby\": \"group:default/team-alpha\" } }\n\nCombined example - find production services or websites with TechDocs:\n { query: { \"$all\": [\n { kind: \"Component\", \"spec.lifecycle\": \"production\" },\n { \"$any\": [{ \"spec.type\": \"service\" }, { \"spec.type\": \"website\" }] },\n { \"metadata.annotations.backstage.io/techdocs-ref\": { \"$exists\": true } }\n ] } }\n\n## Other Options\n\nLimit returned fields: { fields: [\"kind\", \"metadata.name\", \"metadata.namespace\"] }\nSort results: { orderFields: { field: \"metadata.name\", order: \"asc\" } }\nFull text search: { fullTextFilter: { term: \"auth\", fields: [\"metadata.name\", \"metadata.title\"] } }\nPagination: Use limit (e.g. 20) and the returned nextPageCursor for subsequent requests via cursor.\n`;\n\nconst INLINE_MODEL_DESCRIPTION = `\nQuery entities from the Backstage Software Catalog using predicate filters.\n\n## Catalog Model\n\nThe catalog contains entities of different kinds. Every entity has \"kind\", \"apiVersion\", \"metadata\", and optionally \"spec\" and \"relations\". Fields use dot notation for querying.\n\nCommon metadata fields on all entities: name, namespace (default: \"default\"), title, description, labels, annotations, tags (string array), links.\n\nEntity references use the format \"kind:namespace/name\", e.g. \"component:default/my-service\" or \"user:default/jane.doe\".\n\n### Entity Kinds\n\n**Component** - A piece of software such as a service, website, or library.\n spec fields: type (e.g. \"service\", \"website\", \"library\"), lifecycle (e.g. \"production\", \"experimental\", \"deprecated\"), owner (entity ref), system, subcomponentOf, providesApis, consumesApis, dependsOn, dependencyOf.\n\n**API** - An interface that components expose, such as REST APIs or event streams.\n spec fields: type (e.g. \"openapi\", \"asyncapi\", \"graphql\", \"grpc\"), lifecycle, owner (entity ref), definition (the API spec content), system.\n\n**System** - A collection of components, APIs, and resources that together expose some functionality.\n spec fields: owner (entity ref), domain, type.\n\n**Domain** - A grouping of systems that share terminology, domain models, and business purpose.\n spec fields: owner (entity ref), subdomainOf, type.\n\n**Resource** - Infrastructure required to operate a component, such as databases or storage buckets.\n spec fields: type, owner (entity ref), system, dependsOn, dependencyOf.\n\n**Group** - An organizational entity such as a team or business unit.\n spec fields: type (e.g. \"team\", \"business-unit\"), children (entity refs), parent (entity ref), members (entity refs), profile (displayName, email, picture).\n\n**User** - A person, such as an employee or contractor.\n spec fields: memberOf (entity refs), profile (displayName, email, picture).\n\n**Location** - A marker that references other catalog descriptor files to be ingested.\n spec fields: type, target, targets, presence.\n\n### Relations\n\nEntities have bidirectional relations stored in the \"relations\" array. Common relation types: ownedBy/ownerOf, dependsOn/dependencyOf, providesApi/apiProvidedBy, consumesApi/apiConsumedBy, parentOf/childOf, memberOf/hasMember, partOf/hasPart.\n\nRelations can be queried via \"relations.<type>\" e.g. \"relations.ownedby: user:default/jane-doe\". The value there must always be a valid entity reference.\n\nWhen querying for entity relationships, prefer using relations over spec fields. For example, use \"relations.ownedby\" instead of \"spec.owner\" to find entities owned by a particular group or user.\n${QUERY_SYNTAX}`;\n\nconst MODEL_REFERENCE_DESCRIPTION = `\nQuery entities from the Backstage Software Catalog using predicate filters.\n\nFor a complete list of entity kinds, fields, relations, and other queryable attributes available in the catalog, use \\`get-catalog-model-description\\`.\n${QUERY_SYNTAX}`;\n\nexport const createQueryCatalogEntitiesAction = ({\n catalog,\n actionsRegistry,\n useExperimentalCatalogLayersDescriptions,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n useExperimentalCatalogLayersDescriptions?: boolean;\n}) => {\n actionsRegistry.register({\n name: 'query-catalog-entities',\n title: 'Query Catalog Entities',\n attributes: {\n destructive: false,\n readOnly: true,\n idempotent: true,\n },\n description: useExperimentalCatalogLayersDescriptions\n ? MODEL_REFERENCE_DESCRIPTION\n : INLINE_MODEL_DESCRIPTION,\n schema: {\n input: z => {\n // MCP/LLM clients often send numeric arguments as strings. Convert numeric\n // strings to numbers, but leave everything else untouched so Zod rejects the\n // original value (e.g. reports \"received string\" for 'abc' rather than\n // \"received NaN\", and doesn't silently turn true/null/'' into 1/0/0).\n const numericFromString = (value: unknown) => {\n if (typeof value === 'string' && value.trim() !== '') {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : value;\n }\n return value;\n };\n\n return z.object({\n query: createZodV3FilterPredicateSchema(z)\n .optional()\n .describe(\n 'Entity predicate query. Supports field matching, $all, $any, $not, $exists, $in, $contains, and $hasPrefix operators.',\n ),\n fields: z\n .array(z.string())\n .optional()\n .describe(\n 'Specific fields to include in the response. If not provided, all fields are returned. Each entry is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n limit: z\n .preprocess(numericFromString, z.number().int().positive())\n .optional()\n .describe('Maximum number of entities to return at a time.'),\n offset: z\n .preprocess(numericFromString, z.number().int().min(0))\n .optional()\n .describe('Number of entities to skip before returning results.'),\n orderFields: z\n .union([\n z.object({\n field: z\n .string()\n .describe(\n 'Field to order by. The format is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n order: z.enum(['asc', 'desc']).describe('Sort order'),\n }),\n z.array(\n z.object({\n field: z\n .string()\n .describe(\n 'Field to order by. The format is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n order: z.enum(['asc', 'desc']).describe('Sort order'),\n }),\n ),\n ])\n .optional()\n .describe(\n 'Ordering criteria for the results. Can be a single order directive or an array for multi-field sorting.',\n ),\n fullTextFilter: z\n .object({\n term: z.string().describe('Full text search term'),\n fields: z\n .array(z.string())\n .optional()\n .describe(\n 'Fields to search within. Each entry is a dot separated path into an entity, e.g. `spec.type`.',\n ),\n })\n .optional()\n .describe('Full text search criteria'),\n cursor: z\n .string()\n .optional()\n .describe(\n 'Cursor for pagination. This can be used only after the first request with a response containing a cursor. If a cursor is given it takes precedence over `offset`.',\n ),\n });\n },\n output: z =>\n z.object({\n items: z\n .array(z.object({}).passthrough())\n .describe('List of entities'),\n totalItems: z.number().describe('Total number of entities'),\n hasMoreEntities: z\n .boolean()\n .describe('Whether more entities are available'),\n nextPageCursor: z\n .string()\n .optional()\n .describe('Next page cursor used to fetch next page of entities'),\n }),\n },\n action: async ({ input, credentials }) => {\n const response = await catalog.queryEntities(\n {\n ...input,\n query: input.query,\n },\n { credentials },\n );\n\n return {\n output: {\n items: response.items,\n totalItems: response.totalItems,\n hasMoreEntities: !!response.pageInfo.nextCursor,\n nextPageCursor: response.pageInfo.nextCursor,\n },\n };\n },\n });\n};\n"],"names":["createZodV3FilterPredicateSchema"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createRefreshCatalogEntityAction.cjs.js","sources":["../../src/actions/createRefreshCatalogEntityAction.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { ConflictError, InputError } from '@backstage/errors';\nimport type { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createRefreshCatalogEntityAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'refresh-catalog-entity',\n title: 'Refresh Catalog Entity',\n attributes: {\n destructive: false,\n readOnly: false,\n idempotent: true,\n },\n description: `\nThis allows you to trigger a refresh of a single entity in the software catalog, requeuing it for processing.\nThis is useful immediately after creating or updating an entity (for example via a scaffolder template invoked by an MCP client) so the new data becomes visible before subsequent actions read it.\nEach entity in the software catalog has a unique name, kind, and namespace. If the name alone matches multiple entities, provide the kind (and namespace) to disambiguate.\n `,\n schema: {\n input: z =>\n z.object({\n kind: z\n .string()\n .min(1)\n .describe(\n 'The kind of the entity to refresh, e.g. \"Component\", \"API\", \"System\". If the kind is unknown it can be omitted.',\n )\n .optional(),\n namespace: z\n .string()\n .min(1)\n .describe(\n 'The namespace of the entity to refresh. If the namespace is unknown it can be omitted.',\n )\n .optional(),\n name: z\n .string()\n .trim()\n .min(1)\n .describe('The name of the entity to refresh.'),\n }),\n output: z =>\n z.object({\n entityRef: z\n .string()\n .describe('The canonical entity reference that was refreshed.'),\n }),\n },\n action: async ({ input, credentials }) => {\n const filter: Record<string, string> = { 'metadata.name': input.name };\n\n if (input.kind) {\n filter.kind = input.kind;\n }\n\n if (input.namespace) {\n filter['metadata.namespace'] = input.namespace;\n }\n\n const { items } = await catalog.queryEntities(\n { filter },\n { credentials },\n );\n\n if (items.length === 0) {\n throw new InputError(`No entity found with name \"${input.name}\"`);\n }\n\n if (items.length > 1) {\n throw new ConflictError(\n `Multiple entities found with name \"${\n input.name\n }\", please provide more specific filters. Entities found: ${items\n .map(item => `\"${stringifyEntityRef(item)}\"`)\n .join(', ')}`,\n );\n }\n\n const entityRef = stringifyEntityRef(items[0]);\n\n await catalog.refreshEntity(entityRef, { credentials });\n\n return {\n output: { entityRef },\n };\n },\n });\n};\n"],"names":["InputError","ConflictError","stringifyEntityRef"],"mappings":";;;;;AAoBO,MAAM,mCAAmC,CAAC;AAAA,EAC/C,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,wBAAA;AAAA,IACN,KAAA,EAAO,wBAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,KAAA;AAAA,MACb,QAAA,EAAU,KAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA;AAAA,IAKb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,MAAM,CAAA,CACH,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,WAAW,CAAA,CACR,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,IAAA,EAAM,CAAA,CACH,MAAA,EAAO,CACP,IAAA,GACA,GAAA,CAAI,CAAC,CAAA,CACL,QAAA,CAAS,oCAAoC;AAAA,OACjD,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KACN,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,SAAA,EAAW,CAAA,CACR,MAAA,EAAO,CACP,SAAS,oDAAoD;AAAA,OACjE;AAAA,KACL;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,MAAM,MAAA,GAAiC,EAAE,eAAA,EAAiB,KAAA,CAAM,IAAA,EAAK;AAErE,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,MAAA,CAAO,OAAO,KAAA,CAAM,IAAA;AAAA,MACtB;AAEA,MAAA,IAAI,MAAM,SAAA,EAAW;AACnB,QAAA,MAAA,CAAO,oBAAoB,IAAI,KAAA,CAAM,SAAA;AAAA,MACvC;AAEA,MAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,OAAA,CAAQ,aAAA;AAAA,QAC9B,EAAE,MAAA,EAAO;AAAA,QACT,EAAE,WAAA;AAAY,OAChB;AAEA,MAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,QAAA,MAAM,IAAIA,iBAAA,CAAW,CAAA,2BAAA,EAA8B,KAAA,CAAM,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,MAClE;AAEA,MAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,QAAA,MAAM,IAAIC,oBAAA;AAAA,UACR,CAAA,mCAAA,EACE,KAAA,CAAM,IACR,CAAA,yDAAA,EAA4D,MACzD,GAAA,CAAI,CAAA,IAAA,KAAQ,CAAA,CAAA,EAAIC,+BAAA,CAAmB,IAAI,CAAC,CAAA,CAAA,CAAG,CAAA,CAC3C,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,SACf;AAAA,MACF;AAEA,MAAA,MAAM,SAAA,GAAYA,+BAAA,CAAmB,KAAA,CAAM,CAAC,CAAC,CAAA;AAE7C,MAAA,MAAM,OAAA,CAAQ,aAAA,CAAc,SAAA,EAAW,EAAE,aAAa,CAAA;AAEtD,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ,EAAE,SAAA;AAAU,OACtB;AAAA,IACF;AAAA,GACD,CAAA;AACH
|
|
1
|
+
{"version":3,"file":"createRefreshCatalogEntityAction.cjs.js","sources":["../../src/actions/createRefreshCatalogEntityAction.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { ConflictError, InputError } from '@backstage/errors';\nimport type { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createRefreshCatalogEntityAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'refresh-catalog-entity',\n title: 'Refresh Catalog Entity',\n attributes: {\n destructive: false,\n readOnly: false,\n idempotent: true,\n },\n description: `\nThis allows you to trigger a refresh of a single entity in the software catalog, requeuing it for processing.\nThis is useful immediately after creating or updating an entity (for example via a scaffolder template invoked by an MCP client) so the new data becomes visible before subsequent actions read it.\nEach entity in the software catalog has a unique name, kind, and namespace. If the name alone matches multiple entities, provide the kind (and namespace) to disambiguate.\n `,\n schema: {\n input: z =>\n z.object({\n kind: z\n .string()\n .min(1)\n .describe(\n 'The kind of the entity to refresh, e.g. \"Component\", \"API\", \"System\". If the kind is unknown it can be omitted.',\n )\n .optional(),\n namespace: z\n .string()\n .min(1)\n .describe(\n 'The namespace of the entity to refresh. If the namespace is unknown it can be omitted.',\n )\n .optional(),\n name: z\n .string()\n .trim()\n .min(1)\n .describe('The name of the entity to refresh.'),\n }),\n output: z =>\n z.object({\n entityRef: z\n .string()\n .describe('The canonical entity reference that was refreshed.'),\n }),\n },\n action: async ({ input, credentials }) => {\n const filter: Record<string, string> = { 'metadata.name': input.name };\n\n if (input.kind) {\n filter.kind = input.kind;\n }\n\n if (input.namespace) {\n filter['metadata.namespace'] = input.namespace;\n }\n\n const { items } = await catalog.queryEntities(\n { filter },\n { credentials },\n );\n\n if (items.length === 0) {\n throw new InputError(`No entity found with name \"${input.name}\"`);\n }\n\n if (items.length > 1) {\n throw new ConflictError(\n `Multiple entities found with name \"${\n input.name\n }\", please provide more specific filters. Entities found: ${items\n .map(item => `\"${stringifyEntityRef(item)}\"`)\n .join(', ')}`,\n );\n }\n\n const entityRef = stringifyEntityRef(items[0]);\n\n await catalog.refreshEntity(entityRef, { credentials });\n\n return {\n output: { entityRef },\n };\n },\n });\n};\n"],"names":["InputError","ConflictError","stringifyEntityRef"],"mappings":";;;;;AAoBO,MAAM,mCAAmC,CAAC;AAAA,EAC/C,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,wBAAA;AAAA,IACN,KAAA,EAAO,wBAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,KAAA;AAAA,MACb,QAAA,EAAU,KAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA;AAAA,IAKb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,MAAM,CAAA,CACH,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,WAAW,CAAA,CACR,MAAA,EAAO,CACP,GAAA,CAAI,CAAC,CAAA,CACL,QAAA;AAAA,UACC;AAAA,UAED,QAAA,EAAS;AAAA,QACZ,IAAA,EAAM,CAAA,CACH,MAAA,EAAO,CACP,IAAA,GACA,GAAA,CAAI,CAAC,CAAA,CACL,QAAA,CAAS,oCAAoC;AAAA,OACjD,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KACN,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,SAAA,EAAW,CAAA,CACR,MAAA,EAAO,CACP,SAAS,oDAAoD;AAAA,OACjE;AAAA,KACL;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,MAAM,MAAA,GAAiC,EAAE,eAAA,EAAiB,KAAA,CAAM,IAAA,EAAK;AAErE,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,MAAA,CAAO,OAAO,KAAA,CAAM,IAAA;AAAA,MACtB;AAEA,MAAA,IAAI,MAAM,SAAA,EAAW;AACnB,QAAA,MAAA,CAAO,oBAAoB,IAAI,KAAA,CAAM,SAAA;AAAA,MACvC;AAEA,MAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,OAAA,CAAQ,aAAA;AAAA,QAC9B,EAAE,MAAA,EAAO;AAAA,QACT,EAAE,WAAA;AAAY,OAChB;AAEA,MAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,QAAA,MAAM,IAAIA,iBAAA,CAAW,CAAA,2BAAA,EAA8B,KAAA,CAAM,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,MAClE;AAEA,MAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,QAAA,MAAM,IAAIC,oBAAA;AAAA,UACR,CAAA,mCAAA,EACE,KAAA,CAAM,IACR,CAAA,yDAAA,EAA4D,MACzD,GAAA,CAAI,CAAA,IAAA,KAAQ,CAAA,CAAA,EAAIC,+BAAA,CAAmB,IAAI,CAAC,CAAA,CAAA,CAAG,CAAA,CAC3C,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,SACf;AAAA,MACF;AAEA,MAAA,MAAM,SAAA,GAAYA,+BAAA,CAAmB,KAAA,CAAM,CAAC,CAAC,CAAA;AAE7C,MAAA,MAAM,OAAA,CAAQ,aAAA,CAAc,SAAA,EAAW,EAAE,aAAa,CAAA;AAEtD,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ,EAAE,SAAA;AAAU,OACtB;AAAA,IACF;AAAA,GACD,CAAA;AACH;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createRegisterCatalogEntitiesAction.cjs.js","sources":["../../src/actions/createRegisterCatalogEntitiesAction.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { InputError } from '@backstage/errors';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nconst isValidUrl = (url: string) => {\n try {\n // eslint-disable-next-line no-new\n new URL(url);\n return true;\n } catch (error) {\n return false;\n }\n};\n\nexport const createRegisterCatalogEntitiesAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'register-entity',\n title: 'Register entity in the Catalog',\n attributes: {\n destructive: false,\n readOnly: false,\n idempotent: false,\n },\n description: `Registers one or more entities in the Backstage catalog by creating a Location entity that points to a remote catalog-info.yaml file.\n\nThis action is similar to the \"Register existing component\" flow in the Backstage UI, where you provide a URL to a catalog-info.yaml file describing catalog entities such as Components, Systems, Resources, APIs, Users, and Groups. The action will create a new Location entity that references the provided file; all entities defined within that file will be imported into the catalog.\n\nA unique identifier (locationId) will be returned for the newly created Location. You can use this identifier in the future to unregister or delete the Location and all entities it owns.\n`,\n schema: {\n input: z =>\n z.object({\n locationUrl: z\n .string()\n .describe(\n `URL reference to the catalog-info.yaml file that describes the entity to register. For example: https://github.com/backstage/demo/blob/master/catalog-info.yaml`,\n ),\n }),\n output: z =>\n z.object({\n locationId: z\n .string()\n .describe('The ID of the entity that was registered'),\n }),\n },\n action: async ({ input, credentials }) => {\n if (!isValidUrl(input.locationUrl)) {\n throw new InputError(`${input.locationUrl} is an invalid URL`);\n }\n\n const result = await catalog.addLocation(\n {\n type: 'url',\n target: input.locationUrl,\n },\n {\n credentials,\n },\n );\n\n return {\n output: {\n locationId: result.location.id,\n },\n };\n },\n });\n};\n"],"names":["InputError"],"mappings":";;;;AAmBA,MAAM,UAAA,GAAa,CAAC,GAAA,KAAgB;AAClC,EAAA,IAAI;AAEF,IAAA,IAAI,IAAI,GAAG,CAAA;AACX,IAAA,OAAO,IAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,OAAO,KAAA;AAAA,EACT;AACF,CAAA;AAEO,MAAM,sCAAsC,CAAC;AAAA,EAClD,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,iBAAA;AAAA,IACN,KAAA,EAAO,gCAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,KAAA;AAAA,MACb,QAAA,EAAU,KAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa,CAAA;;AAAA;;AAAA;AAAA,CAAA;AAAA,IAMb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,WAAA,EAAa,CAAA,CACV,MAAA,EAAO,CACP,QAAA;AAAA,UACC,CAAA,+JAAA;AAAA;AACF,OACH,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KACN,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,UAAA,EAAY,CAAA,CACT,MAAA,EAAO,CACP,SAAS,0CAA0C;AAAA,OACvD;AAAA,KACL;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,IAAI,CAAC,UAAA,CAAW,KAAA,CAAM,WAAW,CAAA,EAAG;AAClC,QAAA,MAAM,IAAIA,iBAAA,CAAW,CAAA,EAAG,KAAA,CAAM,WAAW,CAAA,kBAAA,CAAoB,CAAA;AAAA,MAC/D;AAEA,MAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,WAAA;AAAA,QAC3B;AAAA,UACE,IAAA,EAAM,KAAA;AAAA,UACN,QAAQ,KAAA,CAAM;AAAA,SAChB;AAAA,QACA;AAAA,UACE;AAAA;AACF,OACF;AAEA,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ;AAAA,UACN,UAAA,EAAY,OAAO,QAAA,CAAS;AAAA;AAC9B,OACF;AAAA,IACF;AAAA,GACD,CAAA;AACH
|
|
1
|
+
{"version":3,"file":"createRegisterCatalogEntitiesAction.cjs.js","sources":["../../src/actions/createRegisterCatalogEntitiesAction.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { InputError } from '@backstage/errors';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nconst isValidUrl = (url: string) => {\n try {\n // eslint-disable-next-line no-new\n new URL(url);\n return true;\n } catch (error) {\n return false;\n }\n};\n\nexport const createRegisterCatalogEntitiesAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'register-entity',\n title: 'Register entity in the Catalog',\n attributes: {\n destructive: false,\n readOnly: false,\n idempotent: false,\n },\n description: `Registers one or more entities in the Backstage catalog by creating a Location entity that points to a remote catalog-info.yaml file.\n\nThis action is similar to the \"Register existing component\" flow in the Backstage UI, where you provide a URL to a catalog-info.yaml file describing catalog entities such as Components, Systems, Resources, APIs, Users, and Groups. The action will create a new Location entity that references the provided file; all entities defined within that file will be imported into the catalog.\n\nA unique identifier (locationId) will be returned for the newly created Location. You can use this identifier in the future to unregister or delete the Location and all entities it owns.\n`,\n schema: {\n input: z =>\n z.object({\n locationUrl: z\n .string()\n .describe(\n `URL reference to the catalog-info.yaml file that describes the entity to register. For example: https://github.com/backstage/demo/blob/master/catalog-info.yaml`,\n ),\n }),\n output: z =>\n z.object({\n locationId: z\n .string()\n .describe('The ID of the entity that was registered'),\n }),\n },\n action: async ({ input, credentials }) => {\n if (!isValidUrl(input.locationUrl)) {\n throw new InputError(`${input.locationUrl} is an invalid URL`);\n }\n\n const result = await catalog.addLocation(\n {\n type: 'url',\n target: input.locationUrl,\n },\n {\n credentials,\n },\n );\n\n return {\n output: {\n locationId: result.location.id,\n },\n };\n },\n });\n};\n"],"names":["InputError"],"mappings":";;;;AAmBA,MAAM,UAAA,GAAa,CAAC,GAAA,KAAgB;AAClC,EAAA,IAAI;AAEF,IAAA,IAAI,IAAI,GAAG,CAAA;AACX,IAAA,OAAO,IAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,OAAO,KAAA;AAAA,EACT;AACF,CAAA;AAEO,MAAM,sCAAsC,CAAC;AAAA,EAClD,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,iBAAA;AAAA,IACN,KAAA,EAAO,gCAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,KAAA;AAAA,MACb,QAAA,EAAU,KAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa,CAAA;;AAAA;;AAAA;AAAA,CAAA;AAAA,IAMb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,WAAA,EAAa,CAAA,CACV,MAAA,EAAO,CACP,QAAA;AAAA,UACC,CAAA,+JAAA;AAAA;AACF,OACH,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KACN,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,UAAA,EAAY,CAAA,CACT,MAAA,EAAO,CACP,SAAS,0CAA0C;AAAA,OACvD;AAAA,KACL;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,IAAI,CAAC,UAAA,CAAW,KAAA,CAAM,WAAW,CAAA,EAAG;AAClC,QAAA,MAAM,IAAIA,iBAAA,CAAW,CAAA,EAAG,KAAA,CAAM,WAAW,CAAA,kBAAA,CAAoB,CAAA;AAAA,MAC/D;AAEA,MAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,WAAA;AAAA,QAC3B;AAAA,UACE,IAAA,EAAM,KAAA;AAAA,UACN,QAAQ,KAAA,CAAM;AAAA,SAChB;AAAA,QACA;AAAA,UACE;AAAA;AACF,OACF;AAEA,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ;AAAA,UACN,UAAA,EAAY,OAAO,QAAA,CAAS;AAAA;AAC9B,OACF;AAAA,IACF;AAAA,GACD,CAAA;AACH;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createUnregisterCatalogEntitiesAction.cjs.js","sources":["../../src/actions/createUnregisterCatalogEntitiesAction.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { NotFoundError } from '@backstage/errors';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createUnregisterCatalogEntitiesAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'unregister-entity',\n title: 'Unregister entity from the Catalog',\n attributes: {\n destructive: true,\n readOnly: false,\n idempotent: true,\n },\n description: `Unregisters a Location entity and all entities it owns from the Backstage catalog.\n\nThis action is similar to the \"Unregister location\" function in the Backstage UI, where you provide the unique identifier (locationId) of a Location entity. Alternatively, you can provide the URL used to register the location. The action will remove the specified Location from the catalog as well as all entities that were created when the Location was imported.\n\nOnce completed, all entities associated with the Location will be deleted from the catalog.\n`,\n schema: {\n input: z =>\n z.object({\n type: z\n .union([\n z.object({\n locationId: z\n .string()\n .describe(`Location ID of the Entity to unregister`),\n }),\n z.object({\n locationUrl: z\n .string()\n .describe(\n `URL of the catalog-info.yaml file to unregister for example: https://github.com/backstage/demo/blob/master/catalog-info.yaml`,\n ),\n }),\n ])\n .describe(\n 'Identifies the entity to unregister. Provide either locationId or locationUrl.',\n ),\n }),\n output: z => z.object({}),\n },\n action: async ({ input: { type }, credentials }) => {\n if ('locationId' in type) {\n await catalog.removeLocationById(type.locationId, {\n credentials,\n });\n } else {\n const locations = await catalog\n .getLocations(\n {},\n {\n credentials,\n },\n )\n .then(response =>\n response.items.filter(\n location =>\n location.target.toLowerCase() ===\n type.locationUrl.toLowerCase(),\n ),\n );\n\n if (locations.length === 0) {\n throw new NotFoundError(\n `Location with URL ${type.locationUrl} not found`,\n );\n }\n\n for (const location of locations) {\n await catalog.removeLocationById(location.id, {\n credentials,\n });\n }\n }\n\n return { output: {} };\n },\n });\n};\n"],"names":["NotFoundError"],"mappings":";;;;AAmBO,MAAM,wCAAwC,CAAC;AAAA,EACpD,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,mBAAA;AAAA,IACN,KAAA,EAAO,oCAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,IAAA;AAAA,MACb,QAAA,EAAU,KAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa,CAAA;;AAAA;;AAAA;AAAA,CAAA;AAAA,IAMb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,IAAA,EAAM,EACH,KAAA,CAAM;AAAA,UACL,EAAE,MAAA,CAAO;AAAA,YACP,UAAA,EAAY,CAAA,CACT,MAAA,EAAO,CACP,SAAS,CAAA,uCAAA,CAAyC;AAAA,WACtD,CAAA;AAAA,UACD,EAAE,MAAA,CAAO;AAAA,YACP,WAAA,EAAa,CAAA,CACV,MAAA,EAAO,CACP,QAAA;AAAA,cACC,CAAA,4HAAA;AAAA;AACF,WACH;AAAA,SACF,CAAA,CACA,QAAA;AAAA,UACC;AAAA;AACF,OACH,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KAAK,CAAA,CAAE,MAAA,CAAO,EAAE;AAAA,KAC1B;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,EAAE,IAAA,EAAK,EAAG,aAAY,KAAM;AAClD,MAAA,IAAI,gBAAgB,IAAA,EAAM;AACxB,QAAA,MAAM,OAAA,CAAQ,kBAAA,CAAmB,IAAA,CAAK,UAAA,EAAY;AAAA,UAChD;AAAA,SACD,CAAA;AAAA,MACH,CAAA,MAAO;AACL,QAAA,MAAM,SAAA,GAAY,MAAM,OAAA,CACrB,YAAA;AAAA,UACC,EAAC;AAAA,UACD;AAAA,YACE;AAAA;AACF,SACF,CACC,IAAA;AAAA,UAAK,CAAA,QAAA,KACJ,SAAS,KAAA,CAAM,MAAA;AAAA,YACb,cACE,QAAA,CAAS,MAAA,CAAO,aAAY,KAC5B,IAAA,CAAK,YAAY,WAAA;AAAY;AACjC,SACF;AAEF,QAAA,IAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAC1B,UAAA,MAAM,IAAIA,oBAAA;AAAA,YACR,CAAA,kBAAA,EAAqB,KAAK,WAAW,CAAA,UAAA;AAAA,WACvC;AAAA,QACF;AAEA,QAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAChC,UAAA,MAAM,OAAA,CAAQ,kBAAA,CAAmB,QAAA,CAAS,EAAA,EAAI;AAAA,YAC5C;AAAA,WACD,CAAA;AAAA,QACH;AAAA,MACF;AAEA,MAAA,OAAO,EAAE,MAAA,EAAQ,EAAC,EAAE;AAAA,IACtB;AAAA,GACD,CAAA;AACH
|
|
1
|
+
{"version":3,"file":"createUnregisterCatalogEntitiesAction.cjs.js","sources":["../../src/actions/createUnregisterCatalogEntitiesAction.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { NotFoundError } from '@backstage/errors';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createUnregisterCatalogEntitiesAction = ({\n catalog,\n actionsRegistry,\n}: {\n catalog: CatalogService;\n actionsRegistry: ActionsRegistryService;\n}) => {\n actionsRegistry.register({\n name: 'unregister-entity',\n title: 'Unregister entity from the Catalog',\n attributes: {\n destructive: true,\n readOnly: false,\n idempotent: true,\n },\n description: `Unregisters a Location entity and all entities it owns from the Backstage catalog.\n\nThis action is similar to the \"Unregister location\" function in the Backstage UI, where you provide the unique identifier (locationId) of a Location entity. Alternatively, you can provide the URL used to register the location. The action will remove the specified Location from the catalog as well as all entities that were created when the Location was imported.\n\nOnce completed, all entities associated with the Location will be deleted from the catalog.\n`,\n schema: {\n input: z =>\n z.object({\n type: z\n .union([\n z.object({\n locationId: z\n .string()\n .describe(`Location ID of the Entity to unregister`),\n }),\n z.object({\n locationUrl: z\n .string()\n .describe(\n `URL of the catalog-info.yaml file to unregister for example: https://github.com/backstage/demo/blob/master/catalog-info.yaml`,\n ),\n }),\n ])\n .describe(\n 'Identifies the entity to unregister. Provide either locationId or locationUrl.',\n ),\n }),\n output: z => z.object({}),\n },\n action: async ({ input: { type }, credentials }) => {\n if ('locationId' in type) {\n await catalog.removeLocationById(type.locationId, {\n credentials,\n });\n } else {\n const locations = await catalog\n .getLocations(\n {},\n {\n credentials,\n },\n )\n .then(response =>\n response.items.filter(\n location =>\n location.target.toLowerCase() ===\n type.locationUrl.toLowerCase(),\n ),\n );\n\n if (locations.length === 0) {\n throw new NotFoundError(\n `Location with URL ${type.locationUrl} not found`,\n );\n }\n\n for (const location of locations) {\n await catalog.removeLocationById(location.id, {\n credentials,\n });\n }\n }\n\n return { output: {} };\n },\n });\n};\n"],"names":["NotFoundError"],"mappings":";;;;AAmBO,MAAM,wCAAwC,CAAC;AAAA,EACpD,OAAA;AAAA,EACA;AACF,CAAA,KAGM;AACJ,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,mBAAA;AAAA,IACN,KAAA,EAAO,oCAAA;AAAA,IACP,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,IAAA;AAAA,MACb,QAAA,EAAU,KAAA;AAAA,MACV,UAAA,EAAY;AAAA,KACd;AAAA,IACA,WAAA,EAAa,CAAA;;AAAA;;AAAA;AAAA,CAAA;AAAA,IAMb,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,IAAA,EAAM,EACH,KAAA,CAAM;AAAA,UACL,EAAE,MAAA,CAAO;AAAA,YACP,UAAA,EAAY,CAAA,CACT,MAAA,EAAO,CACP,SAAS,CAAA,uCAAA,CAAyC;AAAA,WACtD,CAAA;AAAA,UACD,EAAE,MAAA,CAAO;AAAA,YACP,WAAA,EAAa,CAAA,CACV,MAAA,EAAO,CACP,QAAA;AAAA,cACC,CAAA,4HAAA;AAAA;AACF,WACH;AAAA,SACF,CAAA,CACA,QAAA;AAAA,UACC;AAAA;AACF,OACH,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KAAK,CAAA,CAAE,MAAA,CAAO,EAAE;AAAA,KAC1B;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,EAAE,IAAA,EAAK,EAAG,aAAY,KAAM;AAClD,MAAA,IAAI,gBAAgB,IAAA,EAAM;AACxB,QAAA,MAAM,OAAA,CAAQ,kBAAA,CAAmB,IAAA,CAAK,UAAA,EAAY;AAAA,UAChD;AAAA,SACD,CAAA;AAAA,MACH,CAAA,MAAO;AACL,QAAA,MAAM,SAAA,GAAY,MAAM,OAAA,CACrB,YAAA;AAAA,UACC,EAAC;AAAA,UACD;AAAA,YACE;AAAA;AACF,SACF,CACC,IAAA;AAAA,UAAK,CAAA,QAAA,KACJ,SAAS,KAAA,CAAM,MAAA;AAAA,YACb,cACE,QAAA,CAAS,MAAA,CAAO,aAAY,KAC5B,IAAA,CAAK,YAAY,WAAA;AAAY;AACjC,SACF;AAEF,QAAA,IAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAC1B,UAAA,MAAM,IAAIA,oBAAA;AAAA,YACR,CAAA,kBAAA,EAAqB,KAAK,WAAW,CAAA,UAAA;AAAA,WACvC;AAAA,QACF;AAEA,QAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAChC,UAAA,MAAM,OAAA,CAAQ,kBAAA,CAAmB,QAAA,CAAS,EAAA,EAAI;AAAA,YAC5C;AAAA,WACD,CAAA;AAAA,QACH;AAAA,MACF;AAEA,MAAA,OAAO,EAAE,MAAA,EAAQ,EAAC,EAAE;AAAA,IACtB;AAAA,GACD,CAAA;AACH;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createValidateEntityAction.cjs.js","sources":["../../src/actions/createValidateEntityAction.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport yaml from 'yaml';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createValidateEntityAction = (options: {\n actionsRegistry: ActionsRegistryService;\n catalog: CatalogService;\n}) => {\n const { actionsRegistry, catalog } = options;\n actionsRegistry.register({\n name: 'validate-entity',\n title: 'Validate Catalog Entity',\n description: `\n This action can be used to validate catalog-info.yaml file contents meant to be used with the software catalog.\n It checks both that the YAML syntax is correct, and that the entity content is valid according to the catalog's rules.\n `,\n attributes: {\n readOnly: true,\n idempotent: true,\n destructive: false,\n },\n schema: {\n input: z =>\n z.object({\n entity: z.string().describe('Entity YAML content to validate'),\n location: z\n .string()\n .url()\n .optional()\n .describe('Location to validate'),\n }),\n output: z =>\n z.object({\n isValid: z.boolean().describe('Whether the entity is valid'),\n isValidYaml: z.boolean().describe('Whether the YAML syntax is valid'),\n errors: z.array(z.string()).describe('Array of validation errors'),\n entity: z\n .record(z.any())\n .optional()\n .describe('Parsed entity object if valid'),\n }),\n },\n action: async ({ input, credentials }) => {\n const { entity: content, location } = input;\n try {\n let entity;\n try {\n entity = yaml.parse(content);\n } catch (yamlError) {\n return {\n output: {\n isValid: false,\n isValidYaml: false,\n errors: [`YAML parsing error: ${yamlError.message}`],\n },\n };\n }\n\n const resp = await catalog.validateEntity(\n entity,\n location ?? 'url:https://localhost/entity-validator',\n { credentials },\n );\n\n return {\n output: {\n isValid: resp.valid,\n isValidYaml: true,\n errors: resp.valid ? [] : resp.errors.map(e => e.message),\n entity: resp.valid ? entity : undefined,\n },\n };\n } catch (error) {\n return {\n output: {\n isValid: false,\n isValidYaml: false,\n errors: [`Validation error: ${error.message}`],\n },\n };\n }\n },\n });\n};\n"],"names":["yaml"],"mappings":";;;;;;;;AAmBO,MAAM,0BAAA,GAA6B,CAAC,OAAA,KAGrC;AACJ,EAAA,MAAM,EAAE,eAAA,EAAiB,OAAA,EAAQ,GAAI,OAAA;AACrC,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,iBAAA;AAAA,IACN,KAAA,EAAO,yBAAA;AAAA,IACP,WAAA,EAAa;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,IAIb,UAAA,EAAY;AAAA,MACV,QAAA,EAAU,IAAA;AAAA,MACV,UAAA,EAAY,IAAA;AAAA,MACZ,WAAA,EAAa;AAAA,KACf;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO,CAAE,SAAS,iCAAiC,CAAA;AAAA,QAC7D,QAAA,EAAU,EACP,MAAA,EAAO,CACP,KAAI,CACJ,QAAA,EAAS,CACT,QAAA,CAAS,sBAAsB;AAAA,OACnC,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KACN,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,OAAA,EAAS,CAAA,CAAE,OAAA,EAAQ,CAAE,SAAS,6BAA6B,CAAA;AAAA,QAC3D,WAAA,EAAa,CAAA,CAAE,OAAA,EAAQ,CAAE,SAAS,kCAAkC,CAAA;AAAA,QACpE,MAAA,EAAQ,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,SAAS,4BAA4B,CAAA;AAAA,QACjE,MAAA,EAAQ,CAAA,CACL,MAAA,CAAO,CAAA,CAAE,GAAA,EAAK,CAAA,CACd,QAAA,EAAS,CACT,QAAA,CAAS,+BAA+B;AAAA,OAC5C;AAAA,KACL;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,QAAA,EAAS,GAAI,KAAA;AACtC,MAAA,IAAI;AACF,QAAA,IAAI,MAAA;AACJ,QAAA,IAAI;AACF,UAAA,MAAA,GAASA,qBAAA,CAAK,MAAM,OAAO,CAAA;AAAA,QAC7B,SAAS,SAAA,EAAW;AAClB,UAAA,OAAO;AAAA,YACL,MAAA,EAAQ;AAAA,cACN,OAAA,EAAS,KAAA;AAAA,cACT,WAAA,EAAa,KAAA;AAAA,cACb,MAAA,EAAQ,CAAC,CAAA,oBAAA,EAAuB,SAAA,CAAU,OAAO,CAAA,CAAE;AAAA;AACrD,WACF;AAAA,QACF;AAEA,QAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,cAAA;AAAA,UACzB,MAAA;AAAA,UACA,QAAA,IAAY,wCAAA;AAAA,UACZ,EAAE,WAAA;AAAY,SAChB;AAEA,QAAA,OAAO;AAAA,UACL,MAAA,EAAQ;AAAA,YACN,SAAS,IAAA,CAAK,KAAA;AAAA,YACd,WAAA,EAAa,IAAA;AAAA,YACb,MAAA,EAAQ,IAAA,CAAK,KAAA,GAAQ,EAAC,GAAI,KAAK,MAAA,CAAO,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,OAAO,CAAA;AAAA,YACxD,MAAA,EAAQ,IAAA,CAAK,KAAA,GAAQ,MAAA,GAAS,KAAA;AAAA;AAChC,SACF;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,OAAO;AAAA,UACL,MAAA,EAAQ;AAAA,YACN,OAAA,EAAS,KAAA;AAAA,YACT,WAAA,EAAa,KAAA;AAAA,YACb,MAAA,EAAQ,CAAC,CAAA,kBAAA,EAAqB,KAAA,CAAM,OAAO,CAAA,CAAE;AAAA;AAC/C,SACF;AAAA,MACF;AAAA,IACF;AAAA,GACD,CAAA;AACH
|
|
1
|
+
{"version":3,"file":"createValidateEntityAction.cjs.js","sources":["../../src/actions/createValidateEntityAction.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport yaml from 'yaml';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nexport const createValidateEntityAction = (options: {\n actionsRegistry: ActionsRegistryService;\n catalog: CatalogService;\n}) => {\n const { actionsRegistry, catalog } = options;\n actionsRegistry.register({\n name: 'validate-entity',\n title: 'Validate Catalog Entity',\n description: `\n This action can be used to validate catalog-info.yaml file contents meant to be used with the software catalog.\n It checks both that the YAML syntax is correct, and that the entity content is valid according to the catalog's rules.\n `,\n attributes: {\n readOnly: true,\n idempotent: true,\n destructive: false,\n },\n schema: {\n input: z =>\n z.object({\n entity: z.string().describe('Entity YAML content to validate'),\n location: z\n .string()\n .url()\n .optional()\n .describe('Location to validate'),\n }),\n output: z =>\n z.object({\n isValid: z.boolean().describe('Whether the entity is valid'),\n isValidYaml: z.boolean().describe('Whether the YAML syntax is valid'),\n errors: z.array(z.string()).describe('Array of validation errors'),\n entity: z\n .record(z.any())\n .optional()\n .describe('Parsed entity object if valid'),\n }),\n },\n action: async ({ input, credentials }) => {\n const { entity: content, location } = input;\n try {\n let entity;\n try {\n entity = yaml.parse(content);\n } catch (yamlError) {\n return {\n output: {\n isValid: false,\n isValidYaml: false,\n errors: [`YAML parsing error: ${yamlError.message}`],\n },\n };\n }\n\n const resp = await catalog.validateEntity(\n entity,\n location ?? 'url:https://localhost/entity-validator',\n { credentials },\n );\n\n return {\n output: {\n isValid: resp.valid,\n isValidYaml: true,\n errors: resp.valid ? [] : resp.errors.map(e => e.message),\n entity: resp.valid ? entity : undefined,\n },\n };\n } catch (error) {\n return {\n output: {\n isValid: false,\n isValidYaml: false,\n errors: [`Validation error: ${error.message}`],\n },\n };\n }\n },\n });\n};\n"],"names":["yaml"],"mappings":";;;;;;;;AAmBO,MAAM,0BAAA,GAA6B,CAAC,OAAA,KAGrC;AACJ,EAAA,MAAM,EAAE,eAAA,EAAiB,OAAA,EAAQ,GAAI,OAAA;AACrC,EAAA,eAAA,CAAgB,QAAA,CAAS;AAAA,IACvB,IAAA,EAAM,iBAAA;AAAA,IACN,KAAA,EAAO,yBAAA;AAAA,IACP,WAAA,EAAa;AAAA;AAAA;AAAA,MAAA,CAAA;AAAA,IAIb,UAAA,EAAY;AAAA,MACV,QAAA,EAAU,IAAA;AAAA,MACV,UAAA,EAAY,IAAA;AAAA,MACZ,WAAA,EAAa;AAAA,KACf;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,KAAA,EAAO,CAAA,CAAA,KACL,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO,CAAE,SAAS,iCAAiC,CAAA;AAAA,QAC7D,QAAA,EAAU,EACP,MAAA,EAAO,CACP,KAAI,CACJ,QAAA,EAAS,CACT,QAAA,CAAS,sBAAsB;AAAA,OACnC,CAAA;AAAA,MACH,MAAA,EAAQ,CAAA,CAAA,KACN,CAAA,CAAE,MAAA,CAAO;AAAA,QACP,OAAA,EAAS,CAAA,CAAE,OAAA,EAAQ,CAAE,SAAS,6BAA6B,CAAA;AAAA,QAC3D,WAAA,EAAa,CAAA,CAAE,OAAA,EAAQ,CAAE,SAAS,kCAAkC,CAAA;AAAA,QACpE,MAAA,EAAQ,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,SAAS,4BAA4B,CAAA;AAAA,QACjE,MAAA,EAAQ,CAAA,CACL,MAAA,CAAO,CAAA,CAAE,GAAA,EAAK,CAAA,CACd,QAAA,EAAS,CACT,QAAA,CAAS,+BAA+B;AAAA,OAC5C;AAAA,KACL;AAAA,IACA,MAAA,EAAQ,OAAO,EAAE,KAAA,EAAO,aAAY,KAAM;AACxC,MAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,QAAA,EAAS,GAAI,KAAA;AACtC,MAAA,IAAI;AACF,QAAA,IAAI,MAAA;AACJ,QAAA,IAAI;AACF,UAAA,MAAA,GAASA,qBAAA,CAAK,MAAM,OAAO,CAAA;AAAA,QAC7B,SAAS,SAAA,EAAW;AAClB,UAAA,OAAO;AAAA,YACL,MAAA,EAAQ;AAAA,cACN,OAAA,EAAS,KAAA;AAAA,cACT,WAAA,EAAa,KAAA;AAAA,cACb,MAAA,EAAQ,CAAC,CAAA,oBAAA,EAAuB,SAAA,CAAU,OAAO,CAAA,CAAE;AAAA;AACrD,WACF;AAAA,QACF;AAEA,QAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,cAAA;AAAA,UACzB,MAAA;AAAA,UACA,QAAA,IAAY,wCAAA;AAAA,UACZ,EAAE,WAAA;AAAY,SAChB;AAEA,QAAA,OAAO;AAAA,UACL,MAAA,EAAQ;AAAA,YACN,SAAS,IAAA,CAAK,KAAA;AAAA,YACd,WAAA,EAAa,IAAA;AAAA,YACb,MAAA,EAAQ,IAAA,CAAK,KAAA,GAAQ,EAAC,GAAI,KAAK,MAAA,CAAO,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,OAAO,CAAA;AAAA,YACxD,MAAA,EAAQ,IAAA,CAAK,KAAA,GAAQ,MAAA,GAAS,KAAA;AAAA;AAChC,SACF;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,OAAO;AAAA,UACL,MAAA,EAAQ;AAAA,YACN,OAAA,EAAS,KAAA;AAAA,YACT,WAAA,EAAa,KAAA;AAAA,YACb,MAAA,EAAQ,CAAC,CAAA,kBAAA,EAAqB,KAAA,CAAM,OAAO,CAAA,CAAE;AAAA;AAC/C,SACF;AAAA,MACF;AAAA,IACF;AAAA,GACD,CAAA;AACH;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../../src/actions/index.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { ModelHolder } from '../model/ModelHolder';\nimport { createGetCatalogModelDescriptionAction } from './createGetCatalogModelDescriptionAction.ts';\nimport { createGetCatalogEntityAction } from './createGetCatalogEntityAction.ts';\nimport { createValidateEntityAction } from './createValidateEntityAction.ts';\nimport { createRegisterCatalogEntitiesAction } from './createRegisterCatalogEntitiesAction.ts';\nimport { createUnregisterCatalogEntitiesAction } from './createUnregisterCatalogEntitiesAction.ts';\nimport { createQueryCatalogEntitiesAction } from './createQueryCatalogEntitiesAction.ts';\nimport { createRefreshCatalogEntityAction } from './createRefreshCatalogEntityAction.ts';\n\nexport const createCatalogActions = (options: {\n actionsRegistry: ActionsRegistryService;\n catalog: CatalogService;\n modelHolder: ModelHolder | undefined;\n useExperimentalCatalogLayersDescriptions?: boolean;\n}) => {\n createGetCatalogModelDescriptionAction(options);\n createGetCatalogEntityAction(options);\n createValidateEntityAction(options);\n createRegisterCatalogEntitiesAction(options);\n createUnregisterCatalogEntitiesAction(options);\n createQueryCatalogEntitiesAction(options);\n createRefreshCatalogEntityAction(options);\n};\n"],"names":["createGetCatalogModelDescriptionAction","createGetCatalogEntityAction","createValidateEntityAction","createRegisterCatalogEntitiesAction","createUnregisterCatalogEntitiesAction","createQueryCatalogEntitiesAction","createRefreshCatalogEntityAction"],"mappings":";;;;;;;;;;AA2BO,MAAM,oBAAA,GAAuB,CAAC,OAAA,KAK/B;AACJ,EAAAA,6EAAA,CAAuC,OAAO,CAAA;AAC9C,EAAAC,yDAAA,CAA6B,OAAO,CAAA;AACpC,EAAAC,qDAAA,CAA2B,OAAO,CAAA;AAClC,EAAAC,uEAAA,CAAoC,OAAO,CAAA;AAC3C,EAAAC,2EAAA,CAAsC,OAAO,CAAA;AAC7C,EAAAC,iEAAA,CAAiC,OAAO,CAAA;AACxC,EAAAC,iEAAA,CAAiC,OAAO,CAAA;AAC1C
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../src/actions/index.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { ModelHolder } from '../model/ModelHolder';\nimport { createGetCatalogModelDescriptionAction } from './createGetCatalogModelDescriptionAction.ts';\nimport { createGetCatalogEntityAction } from './createGetCatalogEntityAction.ts';\nimport { createValidateEntityAction } from './createValidateEntityAction.ts';\nimport { createRegisterCatalogEntitiesAction } from './createRegisterCatalogEntitiesAction.ts';\nimport { createUnregisterCatalogEntitiesAction } from './createUnregisterCatalogEntitiesAction.ts';\nimport { createQueryCatalogEntitiesAction } from './createQueryCatalogEntitiesAction.ts';\nimport { createRefreshCatalogEntityAction } from './createRefreshCatalogEntityAction.ts';\n\nexport const createCatalogActions = (options: {\n actionsRegistry: ActionsRegistryService;\n catalog: CatalogService;\n modelHolder: ModelHolder | undefined;\n useExperimentalCatalogLayersDescriptions?: boolean;\n}) => {\n createGetCatalogModelDescriptionAction(options);\n createGetCatalogEntityAction(options);\n createValidateEntityAction(options);\n createRegisterCatalogEntitiesAction(options);\n createUnregisterCatalogEntitiesAction(options);\n createQueryCatalogEntitiesAction(options);\n createRefreshCatalogEntityAction(options);\n};\n"],"names":["createGetCatalogModelDescriptionAction","createGetCatalogEntityAction","createValidateEntityAction","createRegisterCatalogEntitiesAction","createUnregisterCatalogEntitiesAction","createQueryCatalogEntitiesAction","createRefreshCatalogEntityAction"],"mappings":";;;;;;;;;;AA2BO,MAAM,oBAAA,GAAuB,CAAC,OAAA,KAK/B;AACJ,EAAAA,6EAAA,CAAuC,OAAO,CAAA;AAC9C,EAAAC,yDAAA,CAA6B,OAAO,CAAA;AACpC,EAAAC,qDAAA,CAA2B,OAAO,CAAA;AAClC,EAAAC,uEAAA,CAAoC,OAAO,CAAA;AAC3C,EAAAC,2EAAA,CAAsC,OAAO,CAAA;AAC7C,EAAAC,iEAAA,CAAiC,OAAO,CAAA;AACxC,EAAAC,iEAAA,CAAiC,OAAO,CAAA;AAC1C;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.cjs.js","sources":["../src/constants.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/** @public */\nexport const CATALOG_CONFLICTS_TOPIC = 'experimental.catalog.conflict';\n/** @public */\nexport const CATALOG_ERRORS_TOPIC = 'experimental.catalog.errors';\n"],"names":[],"mappings":";;AAiBO,MAAM,uBAAA,GAA0B;AAEhC,MAAM,oBAAA,GAAuB
|
|
1
|
+
{"version":3,"file":"constants.cjs.js","sources":["../src/constants.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/** @public */\nexport const CATALOG_CONFLICTS_TOPIC = 'experimental.catalog.conflict';\n/** @public */\nexport const CATALOG_ERRORS_TOPIC = 'experimental.catalog.errors';\n"],"names":[],"mappings":";;AAiBO,MAAM,uBAAA,GAA0B;AAEhC,MAAM,oBAAA,GAAuB;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultCatalogDatabase.cjs.js","sources":["../../src/database/DefaultCatalogDatabase.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 { NotFoundError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport {\n CatalogDatabase,\n ListAncestorsOptions,\n ListAncestorsResult,\n RefreshOptions,\n Transaction,\n} from './types';\nimport { DbRefreshStateReferencesRow, DbRefreshStateRow } from './tables';\nimport { rethrowError } from './conversion';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nconst MAX_ANCESTOR_DEPTH = 32;\n\nexport class DefaultCatalogDatabase implements CatalogDatabase {\n constructor(\n private readonly options: {\n database: Knex;\n logger: LoggerService;\n },\n ) {}\n\n async transaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T> {\n try {\n let result: T | undefined = undefined;\n\n await this.options.database.transaction(\n async tx => {\n // We can't return here, as knex swallows the return type in case the transaction is rolled back:\n // https://github.com/knex/knex/blob/e37aeaa31c8ef9c1b07d2e4d3ec6607e557d800d/lib/transaction.js#L136\n result = await fn(tx);\n },\n {\n // If we explicitly trigger a rollback, don't fail.\n doNotRejectOnRollback: true,\n },\n );\n\n return result!;\n } catch (e) {\n this.options.logger.debug(`Error during transaction, ${e}`);\n throw rethrowError(e);\n }\n }\n\n async listAncestors(\n txOpaque: Transaction,\n options: ListAncestorsOptions,\n ): Promise<ListAncestorsResult> {\n const tx = txOpaque as Knex.Transaction;\n const { entityRef } = options;\n const entityRefs = new Array<string>();\n\n let currentRef = entityRef.toLowerCase();\n for (let depth = 1; depth <= MAX_ANCESTOR_DEPTH; depth += 1) {\n const rows = await tx<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n )\n .where({ target_entity_ref: currentRef })\n .select();\n\n if (rows.length === 0) {\n if (depth === 1) {\n throw new NotFoundError(`Entity ${currentRef} not found`);\n }\n throw new NotFoundError(\n `Entity ${entityRef} has a broken parent reference chain at ${currentRef}`,\n );\n }\n\n const parentRef = rows.find(r => r.source_entity_ref)?.source_entity_ref;\n if (!parentRef) {\n // We've reached the top of the tree which is the entityProvider.\n // In this case we refresh the entity itself.\n return { entityRefs };\n }\n entityRefs.push(parentRef);\n currentRef = parentRef;\n }\n throw new Error(\n `Unable receive ancestors for ${entityRef}, reached maximum depth of ${MAX_ANCESTOR_DEPTH}`,\n );\n }\n\n async refresh(txOpaque: Transaction, options: RefreshOptions): Promise<void> {\n const tx = txOpaque as Knex.Transaction;\n const { entityRef } = options;\n\n const updateResult = await tx<DbRefreshStateRow>('refresh_state')\n .where({ entity_ref: entityRef.toLowerCase() })\n .update({ next_update_at: tx.fn.now() });\n if (updateResult === 0) {\n throw new NotFoundError(`Failed to schedule ${entityRef} for refresh`);\n }\n }\n}\n"],"names":["rethrowError","NotFoundError"],"mappings":";;;;;AA6BA,MAAM,kBAAA,GAAqB,EAAA;AAEpB,MAAM,sBAAA,CAAkD;AAAA,EAC7D,YACmB,OAAA,EAIjB;AAJiB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAIhB;AAAA,
|
|
1
|
+
{"version":3,"file":"DefaultCatalogDatabase.cjs.js","sources":["../../src/database/DefaultCatalogDatabase.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 { NotFoundError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport {\n CatalogDatabase,\n ListAncestorsOptions,\n ListAncestorsResult,\n RefreshOptions,\n Transaction,\n} from './types';\nimport { DbRefreshStateReferencesRow, DbRefreshStateRow } from './tables';\nimport { rethrowError } from './conversion';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nconst MAX_ANCESTOR_DEPTH = 32;\n\nexport class DefaultCatalogDatabase implements CatalogDatabase {\n constructor(\n private readonly options: {\n database: Knex;\n logger: LoggerService;\n },\n ) {}\n\n async transaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T> {\n try {\n let result: T | undefined = undefined;\n\n await this.options.database.transaction(\n async tx => {\n // We can't return here, as knex swallows the return type in case the transaction is rolled back:\n // https://github.com/knex/knex/blob/e37aeaa31c8ef9c1b07d2e4d3ec6607e557d800d/lib/transaction.js#L136\n result = await fn(tx);\n },\n {\n // If we explicitly trigger a rollback, don't fail.\n doNotRejectOnRollback: true,\n },\n );\n\n return result!;\n } catch (e) {\n this.options.logger.debug(`Error during transaction, ${e}`);\n throw rethrowError(e);\n }\n }\n\n async listAncestors(\n txOpaque: Transaction,\n options: ListAncestorsOptions,\n ): Promise<ListAncestorsResult> {\n const tx = txOpaque as Knex.Transaction;\n const { entityRef } = options;\n const entityRefs = new Array<string>();\n\n let currentRef = entityRef.toLowerCase();\n for (let depth = 1; depth <= MAX_ANCESTOR_DEPTH; depth += 1) {\n const rows = await tx<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n )\n .where({ target_entity_ref: currentRef })\n .select();\n\n if (rows.length === 0) {\n if (depth === 1) {\n throw new NotFoundError(`Entity ${currentRef} not found`);\n }\n throw new NotFoundError(\n `Entity ${entityRef} has a broken parent reference chain at ${currentRef}`,\n );\n }\n\n const parentRef = rows.find(r => r.source_entity_ref)?.source_entity_ref;\n if (!parentRef) {\n // We've reached the top of the tree which is the entityProvider.\n // In this case we refresh the entity itself.\n return { entityRefs };\n }\n entityRefs.push(parentRef);\n currentRef = parentRef;\n }\n throw new Error(\n `Unable receive ancestors for ${entityRef}, reached maximum depth of ${MAX_ANCESTOR_DEPTH}`,\n );\n }\n\n async refresh(txOpaque: Transaction, options: RefreshOptions): Promise<void> {\n const tx = txOpaque as Knex.Transaction;\n const { entityRef } = options;\n\n const updateResult = await tx<DbRefreshStateRow>('refresh_state')\n .where({ entity_ref: entityRef.toLowerCase() })\n .update({ next_update_at: tx.fn.now() });\n if (updateResult === 0) {\n throw new NotFoundError(`Failed to schedule ${entityRef} for refresh`);\n }\n }\n}\n"],"names":["rethrowError","NotFoundError"],"mappings":";;;;;AA6BA,MAAM,kBAAA,GAAqB,EAAA;AAEpB,MAAM,sBAAA,CAAkD;AAAA,EAC7D,YACmB,OAAA,EAIjB;AAJiB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAIhB;AAAA,EAJgB,OAAA;AAAA,EAMnB,MAAM,YAAe,EAAA,EAAiD;AACpE,IAAA,IAAI;AACF,MAAA,IAAI,MAAA,GAAwB,KAAA,CAAA;AAE5B,MAAA,MAAM,IAAA,CAAK,QAAQ,QAAA,CAAS,WAAA;AAAA,QAC1B,OAAM,EAAA,KAAM;AAGV,UAAA,MAAA,GAAS,MAAM,GAAG,EAAE,CAAA;AAAA,QACtB,CAAA;AAAA,QACA;AAAA;AAAA,UAEE,qBAAA,EAAuB;AAAA;AACzB,OACF;AAEA,MAAA,OAAO,MAAA;AAAA,IACT,SAAS,CAAA,EAAG;AACV,MAAA,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,0BAAA,EAA6B,CAAC,CAAA,CAAE,CAAA;AAC1D,MAAA,MAAMA,wBAAa,CAAC,CAAA;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAM,aAAA,CACJ,QAAA,EACA,OAAA,EAC8B;AAC9B,IAAA,MAAM,EAAA,GAAK,QAAA;AACX,IAAA,MAAM,EAAE,WAAU,GAAI,OAAA;AACtB,IAAA,MAAM,UAAA,GAAa,IAAI,KAAA,EAAc;AAErC,IAAA,IAAI,UAAA,GAAa,UAAU,WAAA,EAAY;AACvC,IAAA,KAAA,IAAS,KAAA,GAAQ,CAAA,EAAG,KAAA,IAAS,kBAAA,EAAoB,SAAS,CAAA,EAAG;AAC3D,MAAA,MAAM,OAAO,MAAM,EAAA;AAAA,QACjB;AAAA,QAEC,KAAA,CAAM,EAAE,mBAAmB,UAAA,EAAY,EACvC,MAAA,EAAO;AAEV,MAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACrB,QAAA,IAAI,UAAU,CAAA,EAAG;AACf,UAAA,MAAM,IAAIC,oBAAA,CAAc,CAAA,OAAA,EAAU,UAAU,CAAA,UAAA,CAAY,CAAA;AAAA,QAC1D;AACA,QAAA,MAAM,IAAIA,oBAAA;AAAA,UACR,CAAA,OAAA,EAAU,SAAS,CAAA,wCAAA,EAA2C,UAAU,CAAA;AAAA,SAC1E;AAAA,MACF;AAEA,MAAA,MAAM,YAAY,IAAA,CAAK,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,iBAAiB,CAAA,EAAG,iBAAA;AACvD,MAAA,IAAI,CAAC,SAAA,EAAW;AAGd,QAAA,OAAO,EAAE,UAAA,EAAW;AAAA,MACtB;AACA,MAAA,UAAA,CAAW,KAAK,SAAS,CAAA;AACzB,MAAA,UAAA,GAAa,SAAA;AAAA,IACf;AACA,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,6BAAA,EAAgC,SAAS,CAAA,2BAAA,EAA8B,kBAAkB,CAAA;AAAA,KAC3F;AAAA,EACF;AAAA,EAEA,MAAM,OAAA,CAAQ,QAAA,EAAuB,OAAA,EAAwC;AAC3E,IAAA,MAAM,EAAA,GAAK,QAAA;AACX,IAAA,MAAM,EAAE,WAAU,GAAI,OAAA;AAEtB,IAAA,MAAM,YAAA,GAAe,MAAM,EAAA,CAAsB,eAAe,EAC7D,KAAA,CAAM,EAAE,YAAY,SAAA,CAAU,WAAA,IAAe,CAAA,CAC7C,OAAO,EAAE,cAAA,EAAgB,GAAG,EAAA,CAAG,GAAA,IAAO,CAAA;AACzC,IAAA,IAAI,iBAAiB,CAAA,EAAG;AACtB,MAAA,MAAM,IAAIA,oBAAA,CAAc,CAAA,mBAAA,EAAsB,SAAS,CAAA,YAAA,CAAc,CAAA;AAAA,IACvE;AAAA,EACF;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultProcessingDatabase.cjs.js","sources":["../../src/database/DefaultProcessingDatabase.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 { Entity, stringifyEntityRef } from '@backstage/catalog-model';\nimport { ConflictError } from '@backstage/errors';\nimport { DeferredEntity } from '@backstage/plugin-catalog-node';\nimport { Knex } from 'knex';\nimport { ProcessingIntervalFunction } from '../processing/refresh';\nimport { rethrowError, timestampToDateTime } from './conversion';\nimport { initDatabaseMetrics } from './metrics';\nimport {\n DbRefreshKeysRow,\n DbRefreshStateReferencesRow,\n DbRefreshStateRow,\n DbRelationsRow,\n} from './tables';\nimport {\n GetProcessableEntitiesResult,\n ListParentsOptions,\n ListParentsResult,\n ProcessingDatabase,\n RefreshStateItem,\n Transaction,\n UpdateEntityCacheOptions,\n UpdateProcessedEntityOptions,\n} from './types';\nimport { checkLocationKeyConflict } from './operations/refreshState/checkLocationKeyConflict';\nimport { insertUnprocessedEntity } from './operations/refreshState/insertUnprocessedEntity';\nimport { updateUnprocessedEntity } from './operations/refreshState/updateUnprocessedEntity';\nimport { generateStableHash, generateTargetKey, whereInArray } from './util';\nimport {\n syncRelations,\n SyncRelationsResult,\n} from './operations/relations/syncRelations';\nimport { EventParams, EventsService } from '@backstage/plugin-events-node';\nimport { DateTime } from 'luxon';\nimport { CATALOG_CONFLICTS_TOPIC } from '../constants';\nimport { CatalogConflictEventPayload } from '../catalog/types';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { MetricsService } from '@backstage/backend-plugin-api/alpha';\n\n// The number of items that are sent per batch to the database layer, when\n// doing .batchInsert calls to knex. This needs to be low enough to not cause\n// errors in the underlying engine due to exceeding query limits, but large\n// enough to get the speed benefits.\nconst BATCH_SIZE = 50;\n\nexport class DefaultProcessingDatabase implements ProcessingDatabase {\n private readonly options: {\n database: Knex;\n logger: LoggerService;\n refreshInterval: ProcessingIntervalFunction;\n events: EventsService;\n metrics: MetricsService;\n };\n\n constructor(options: {\n database: Knex;\n logger: LoggerService;\n refreshInterval: ProcessingIntervalFunction;\n events: EventsService;\n metrics: MetricsService;\n }) {\n this.options = options;\n initDatabaseMetrics(options.database, options.metrics);\n }\n\n async updateProcessedEntity(\n txOpaque: Transaction,\n options: UpdateProcessedEntityOptions,\n ): Promise<{ relationsChange: SyncRelationsResult }> {\n const tx = txOpaque as Knex.Transaction;\n const {\n id,\n processedEntity,\n resultHash,\n errors,\n relations,\n deferredEntities,\n refreshKeys,\n locationKey,\n } = options;\n const refreshResult = await tx<DbRefreshStateRow>('refresh_state')\n .update({\n processed_entity: JSON.stringify(processedEntity),\n result_hash: resultHash,\n errors,\n location_key: locationKey,\n })\n .where('entity_id', id)\n .andWhere(inner => {\n if (!locationKey) {\n return inner.whereNull('location_key');\n }\n return inner\n .where('location_key', locationKey)\n .orWhereNull('location_key');\n });\n if (refreshResult === 0) {\n throw new ConflictError(\n `Conflicting write of processing result for ${id} with location key '${locationKey}'`,\n );\n }\n const sourceEntityRef = stringifyEntityRef(processedEntity);\n\n // Schedule all deferred entities for future processing.\n await this.addUnprocessedEntities(tx, {\n entities: deferredEntities,\n sourceEntityRef,\n });\n\n // Sync relations using a diff-based approach — only touches rows that\n // actually changed, eliminating steady-state write churn.\n const relationRows: DbRelationsRow[] = relations.map(\n ({ source, target, type }) => ({\n originating_entity_id: id,\n source_entity_ref: stringifyEntityRef(source),\n target_entity_ref: stringifyEntityRef(target),\n type,\n }),\n );\n\n const relationsResult = await syncRelations(tx, id, relationRows);\n\n // Delete old refresh keys\n await tx<DbRefreshKeysRow>('refresh_keys')\n .where({ entity_id: id })\n .delete();\n\n // Insert the refresh keys for the processed entity\n await tx.batchInsert(\n 'refresh_keys',\n refreshKeys.map(k => ({\n entity_id: id,\n key: generateTargetKey(k.key),\n })),\n BATCH_SIZE,\n );\n\n return {\n relationsChange: relationsResult,\n };\n }\n\n async updateProcessedEntityErrors(\n txOpaque: Transaction,\n options: UpdateProcessedEntityOptions,\n ): Promise<void> {\n const tx = txOpaque as Knex.Transaction;\n const { id, errors, resultHash } = options;\n\n await tx<DbRefreshStateRow>('refresh_state')\n .update({\n errors,\n result_hash: resultHash,\n })\n .where('entity_id', id);\n }\n\n async updateEntityCache(\n txOpaque: Transaction,\n options: UpdateEntityCacheOptions,\n ): Promise<void> {\n const tx = txOpaque as Knex.Transaction;\n const { id, state } = options;\n\n await tx<DbRefreshStateRow>('refresh_state')\n .update({ cache: JSON.stringify(state ?? {}) })\n .where('entity_id', id);\n }\n\n async getProcessableEntities(\n maybeTx: Transaction | Knex,\n request: { processBatchSize: number },\n ): Promise<GetProcessableEntitiesResult> {\n const knex = maybeTx as Knex.Transaction | Knex;\n const useLocking = ['mysql', 'mysql2', 'pg'].includes(\n knex.client.config.client,\n );\n\n const interval = this.options.refreshInterval();\n\n const nextUpdateAt = (\n tx: Knex | Knex.Transaction,\n refreshInterval: number,\n ) => {\n if (tx.client.config.client.includes('sqlite3')) {\n return tx.raw(`datetime('now', ?)`, [`${refreshInterval} seconds`]);\n } else if (tx.client.config.client.includes('mysql')) {\n return tx.raw(`now() + interval ${refreshInterval} second`);\n }\n return tx.raw(`now() + interval '${refreshInterval} seconds'`);\n };\n\n // The SELECT FOR UPDATE SKIP LOCKED + UPDATE must run inside a\n // single transaction so that the row locks persist until\n // next_update_at has been bumped.\n const run = async (tx: Knex | Knex.Transaction) => {\n const items: DbRefreshStateRow[] = await tx('refresh_state')\n .select([\n 'entity_id',\n 'entity_ref',\n 'unprocessed_entity',\n 'result_hash',\n 'cache',\n 'errors',\n 'location_key',\n 'next_update_at',\n ])\n .where('next_update_at', '<=', tx.fn.now())\n .limit(request.processBatchSize)\n .orderBy('next_update_at', 'asc')\n .modify(qb => {\n if (useLocking) {\n qb.forUpdate().skipLocked();\n }\n });\n\n if (items.length > 0) {\n await tx('refresh_state')\n .where(\n whereInArray(\n 'entity_ref',\n items.map(i => i.entity_ref),\n ),\n )\n .update({\n next_update_at: nextUpdateAt(tx, interval),\n });\n }\n\n return items;\n };\n\n const items =\n knex.isTransaction || !useLocking\n ? await run(knex)\n : await knex.transaction(run);\n\n return {\n items: items.map(\n i =>\n ({\n id: i.entity_id,\n entityRef: i.entity_ref,\n unprocessedEntity: JSON.parse(i.unprocessed_entity) as Entity,\n resultHash: i.result_hash || '',\n nextUpdateAt: timestampToDateTime(i.next_update_at),\n state: i.cache ? JSON.parse(i.cache) : undefined,\n errors: i.errors,\n locationKey: i.location_key,\n } satisfies RefreshStateItem),\n ),\n };\n }\n\n async listParents(\n txOpaque: Transaction,\n options: ListParentsOptions,\n ): Promise<ListParentsResult> {\n const tx = txOpaque as Knex.Transaction;\n\n const rows = await tx<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n )\n .whereIn('target_entity_ref', options.entityRefs)\n .select();\n\n const entityRefs = rows.map(r => r.source_entity_ref!).filter(Boolean);\n\n return { entityRefs };\n }\n\n async transaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T> {\n try {\n let result: T | undefined = undefined;\n\n await this.options.database.transaction(\n async tx => {\n // We can't return here, as knex swallows the return type in case the transaction is rolled back:\n // https://github.com/knex/knex/blob/e37aeaa31c8ef9c1b07d2e4d3ec6607e557d800d/lib/transaction.js#L136\n result = await fn(tx);\n },\n {\n // If we explicitly trigger a rollback, don't fail.\n doNotRejectOnRollback: true,\n },\n );\n\n return result!;\n } catch (e) {\n this.options.logger.debug(`Error during transaction, ${e}`);\n throw rethrowError(e);\n }\n }\n\n /**\n * Add a set of deferred entities for processing.\n * The entities will be added at the front of the processing queue.\n */\n private async addUnprocessedEntities(\n txOpaque: Transaction,\n options: {\n sourceEntityRef: string;\n entities: DeferredEntity[];\n },\n ): Promise<void> {\n const tx = txOpaque as Knex.Transaction;\n\n // Keeps track of the entities that we end up inserting to update refresh_state_references afterwards\n const stateReferences = new Array<string>();\n\n // Upsert all of the unprocessed entities into the refresh_state table, by\n // their entity ref.\n for (const { entity, locationKey } of options.entities) {\n const entityRef = stringifyEntityRef(entity);\n const hash = generateStableHash(entity);\n\n const updated = await updateUnprocessedEntity({\n tx,\n entity,\n hash,\n locationKey,\n });\n if (updated) {\n stateReferences.push(entityRef);\n continue;\n }\n\n const inserted = await insertUnprocessedEntity({\n tx,\n entity,\n hash,\n locationKey,\n logger: this.options.logger,\n });\n if (inserted) {\n stateReferences.push(entityRef);\n continue;\n }\n\n // If the row can't be inserted, we have a conflict, but it could be either\n // because of a conflicting locationKey or a race with another instance, so check\n // whether the conflicting entity has the same entityRef but a different locationKey\n const conflictingKey = await checkLocationKeyConflict({\n tx,\n entityRef,\n locationKey,\n });\n if (conflictingKey) {\n this.options.logger.warn(\n `Detected conflicting entityRef ${entityRef} already referenced by ${conflictingKey} and now also ${locationKey}`,\n );\n if (locationKey) {\n const eventParams: EventParams<CatalogConflictEventPayload> = {\n topic: CATALOG_CONFLICTS_TOPIC,\n eventPayload: {\n unprocessedEntity: entity,\n entityRef,\n newLocationKey: locationKey,\n existingLocationKey: conflictingKey,\n lastConflictAt: DateTime.now().toISO()!,\n },\n };\n await this.options.events.publish(eventParams);\n }\n }\n }\n\n // Lastly, replace refresh state references for the originating entity and any successfully added entities\n await tx<DbRefreshStateReferencesRow>('refresh_state_references')\n // Remove all existing references from the originating entity\n .where({ source_entity_ref: options.sourceEntityRef })\n // And remove any existing references to entities that we're inserting new references for\n .orWhereIn('target_entity_ref', stateReferences)\n .delete();\n await tx.batchInsert(\n 'refresh_state_references',\n stateReferences.map(entityRef => ({\n source_entity_ref: options.sourceEntityRef,\n target_entity_ref: entityRef,\n })),\n BATCH_SIZE,\n );\n }\n}\n"],"names":["initDatabaseMetrics","errors","ConflictError","stringifyEntityRef","syncRelations","generateTargetKey","items","whereInArray","timestampToDateTime","rethrowError","generateStableHash","updateUnprocessedEntity","insertUnprocessedEntity","checkLocationKeyConflict","CATALOG_CONFLICTS_TOPIC","DateTime"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DefaultProcessingDatabase.cjs.js","sources":["../../src/database/DefaultProcessingDatabase.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 { Entity, stringifyEntityRef } from '@backstage/catalog-model';\nimport { ConflictError } from '@backstage/errors';\nimport { DeferredEntity } from '@backstage/plugin-catalog-node';\nimport { Knex } from 'knex';\nimport { ProcessingIntervalFunction } from '../processing/refresh';\nimport { rethrowError, timestampToDateTime } from './conversion';\nimport { initDatabaseMetrics } from './metrics';\nimport {\n DbRefreshKeysRow,\n DbRefreshStateReferencesRow,\n DbRefreshStateRow,\n DbRelationsRow,\n} from './tables';\nimport {\n GetProcessableEntitiesResult,\n ListParentsOptions,\n ListParentsResult,\n ProcessingDatabase,\n RefreshStateItem,\n Transaction,\n UpdateEntityCacheOptions,\n UpdateProcessedEntityOptions,\n} from './types';\nimport { checkLocationKeyConflict } from './operations/refreshState/checkLocationKeyConflict';\nimport { insertUnprocessedEntity } from './operations/refreshState/insertUnprocessedEntity';\nimport { updateUnprocessedEntity } from './operations/refreshState/updateUnprocessedEntity';\nimport { generateStableHash, generateTargetKey, whereInArray } from './util';\nimport {\n syncRelations,\n SyncRelationsResult,\n} from './operations/relations/syncRelations';\nimport { EventParams, EventsService } from '@backstage/plugin-events-node';\nimport { DateTime } from 'luxon';\nimport { CATALOG_CONFLICTS_TOPIC } from '../constants';\nimport { CatalogConflictEventPayload } from '../catalog/types';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { MetricsService } from '@backstage/backend-plugin-api/alpha';\n\n// The number of items that are sent per batch to the database layer, when\n// doing .batchInsert calls to knex. This needs to be low enough to not cause\n// errors in the underlying engine due to exceeding query limits, but large\n// enough to get the speed benefits.\nconst BATCH_SIZE = 50;\n\nexport class DefaultProcessingDatabase implements ProcessingDatabase {\n private readonly options: {\n database: Knex;\n logger: LoggerService;\n refreshInterval: ProcessingIntervalFunction;\n events: EventsService;\n metrics: MetricsService;\n };\n\n constructor(options: {\n database: Knex;\n logger: LoggerService;\n refreshInterval: ProcessingIntervalFunction;\n events: EventsService;\n metrics: MetricsService;\n }) {\n this.options = options;\n initDatabaseMetrics(options.database, options.metrics);\n }\n\n async updateProcessedEntity(\n txOpaque: Transaction,\n options: UpdateProcessedEntityOptions,\n ): Promise<{ relationsChange: SyncRelationsResult }> {\n const tx = txOpaque as Knex.Transaction;\n const {\n id,\n processedEntity,\n resultHash,\n errors,\n relations,\n deferredEntities,\n refreshKeys,\n locationKey,\n } = options;\n const refreshResult = await tx<DbRefreshStateRow>('refresh_state')\n .update({\n processed_entity: JSON.stringify(processedEntity),\n result_hash: resultHash,\n errors,\n location_key: locationKey,\n })\n .where('entity_id', id)\n .andWhere(inner => {\n if (!locationKey) {\n return inner.whereNull('location_key');\n }\n return inner\n .where('location_key', locationKey)\n .orWhereNull('location_key');\n });\n if (refreshResult === 0) {\n throw new ConflictError(\n `Conflicting write of processing result for ${id} with location key '${locationKey}'`,\n );\n }\n const sourceEntityRef = stringifyEntityRef(processedEntity);\n\n // Schedule all deferred entities for future processing.\n await this.addUnprocessedEntities(tx, {\n entities: deferredEntities,\n sourceEntityRef,\n });\n\n // Sync relations using a diff-based approach — only touches rows that\n // actually changed, eliminating steady-state write churn.\n const relationRows: DbRelationsRow[] = relations.map(\n ({ source, target, type }) => ({\n originating_entity_id: id,\n source_entity_ref: stringifyEntityRef(source),\n target_entity_ref: stringifyEntityRef(target),\n type,\n }),\n );\n\n const relationsResult = await syncRelations(tx, id, relationRows);\n\n // Delete old refresh keys\n await tx<DbRefreshKeysRow>('refresh_keys')\n .where({ entity_id: id })\n .delete();\n\n // Insert the refresh keys for the processed entity\n await tx.batchInsert(\n 'refresh_keys',\n refreshKeys.map(k => ({\n entity_id: id,\n key: generateTargetKey(k.key),\n })),\n BATCH_SIZE,\n );\n\n return {\n relationsChange: relationsResult,\n };\n }\n\n async updateProcessedEntityErrors(\n txOpaque: Transaction,\n options: UpdateProcessedEntityOptions,\n ): Promise<void> {\n const tx = txOpaque as Knex.Transaction;\n const { id, errors, resultHash } = options;\n\n await tx<DbRefreshStateRow>('refresh_state')\n .update({\n errors,\n result_hash: resultHash,\n })\n .where('entity_id', id);\n }\n\n async updateEntityCache(\n txOpaque: Transaction,\n options: UpdateEntityCacheOptions,\n ): Promise<void> {\n const tx = txOpaque as Knex.Transaction;\n const { id, state } = options;\n\n await tx<DbRefreshStateRow>('refresh_state')\n .update({ cache: JSON.stringify(state ?? {}) })\n .where('entity_id', id);\n }\n\n async getProcessableEntities(\n maybeTx: Transaction | Knex,\n request: { processBatchSize: number },\n ): Promise<GetProcessableEntitiesResult> {\n const knex = maybeTx as Knex.Transaction | Knex;\n const useLocking = ['mysql', 'mysql2', 'pg'].includes(\n knex.client.config.client,\n );\n\n const interval = this.options.refreshInterval();\n\n const nextUpdateAt = (\n tx: Knex | Knex.Transaction,\n refreshInterval: number,\n ) => {\n if (tx.client.config.client.includes('sqlite3')) {\n return tx.raw(`datetime('now', ?)`, [`${refreshInterval} seconds`]);\n } else if (tx.client.config.client.includes('mysql')) {\n return tx.raw(`now() + interval ${refreshInterval} second`);\n }\n return tx.raw(`now() + interval '${refreshInterval} seconds'`);\n };\n\n // The SELECT FOR UPDATE SKIP LOCKED + UPDATE must run inside a\n // single transaction so that the row locks persist until\n // next_update_at has been bumped.\n const run = async (tx: Knex | Knex.Transaction) => {\n const items: DbRefreshStateRow[] = await tx('refresh_state')\n .select([\n 'entity_id',\n 'entity_ref',\n 'unprocessed_entity',\n 'result_hash',\n 'cache',\n 'errors',\n 'location_key',\n 'next_update_at',\n ])\n .where('next_update_at', '<=', tx.fn.now())\n .limit(request.processBatchSize)\n .orderBy('next_update_at', 'asc')\n .modify(qb => {\n if (useLocking) {\n qb.forUpdate().skipLocked();\n }\n });\n\n if (items.length > 0) {\n await tx('refresh_state')\n .where(\n whereInArray(\n 'entity_ref',\n items.map(i => i.entity_ref),\n ),\n )\n .update({\n next_update_at: nextUpdateAt(tx, interval),\n });\n }\n\n return items;\n };\n\n const items =\n knex.isTransaction || !useLocking\n ? await run(knex)\n : await knex.transaction(run);\n\n return {\n items: items.map(\n i =>\n ({\n id: i.entity_id,\n entityRef: i.entity_ref,\n unprocessedEntity: JSON.parse(i.unprocessed_entity) as Entity,\n resultHash: i.result_hash || '',\n nextUpdateAt: timestampToDateTime(i.next_update_at),\n state: i.cache ? JSON.parse(i.cache) : undefined,\n errors: i.errors,\n locationKey: i.location_key,\n } satisfies RefreshStateItem),\n ),\n };\n }\n\n async listParents(\n txOpaque: Transaction,\n options: ListParentsOptions,\n ): Promise<ListParentsResult> {\n const tx = txOpaque as Knex.Transaction;\n\n const rows = await tx<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n )\n .whereIn('target_entity_ref', options.entityRefs)\n .select();\n\n const entityRefs = rows.map(r => r.source_entity_ref!).filter(Boolean);\n\n return { entityRefs };\n }\n\n async transaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T> {\n try {\n let result: T | undefined = undefined;\n\n await this.options.database.transaction(\n async tx => {\n // We can't return here, as knex swallows the return type in case the transaction is rolled back:\n // https://github.com/knex/knex/blob/e37aeaa31c8ef9c1b07d2e4d3ec6607e557d800d/lib/transaction.js#L136\n result = await fn(tx);\n },\n {\n // If we explicitly trigger a rollback, don't fail.\n doNotRejectOnRollback: true,\n },\n );\n\n return result!;\n } catch (e) {\n this.options.logger.debug(`Error during transaction, ${e}`);\n throw rethrowError(e);\n }\n }\n\n /**\n * Add a set of deferred entities for processing.\n * The entities will be added at the front of the processing queue.\n */\n private async addUnprocessedEntities(\n txOpaque: Transaction,\n options: {\n sourceEntityRef: string;\n entities: DeferredEntity[];\n },\n ): Promise<void> {\n const tx = txOpaque as Knex.Transaction;\n\n // Keeps track of the entities that we end up inserting to update refresh_state_references afterwards\n const stateReferences = new Array<string>();\n\n // Upsert all of the unprocessed entities into the refresh_state table, by\n // their entity ref.\n for (const { entity, locationKey } of options.entities) {\n const entityRef = stringifyEntityRef(entity);\n const hash = generateStableHash(entity);\n\n const updated = await updateUnprocessedEntity({\n tx,\n entity,\n hash,\n locationKey,\n });\n if (updated) {\n stateReferences.push(entityRef);\n continue;\n }\n\n const inserted = await insertUnprocessedEntity({\n tx,\n entity,\n hash,\n locationKey,\n logger: this.options.logger,\n });\n if (inserted) {\n stateReferences.push(entityRef);\n continue;\n }\n\n // If the row can't be inserted, we have a conflict, but it could be either\n // because of a conflicting locationKey or a race with another instance, so check\n // whether the conflicting entity has the same entityRef but a different locationKey\n const conflictingKey = await checkLocationKeyConflict({\n tx,\n entityRef,\n locationKey,\n });\n if (conflictingKey) {\n this.options.logger.warn(\n `Detected conflicting entityRef ${entityRef} already referenced by ${conflictingKey} and now also ${locationKey}`,\n );\n if (locationKey) {\n const eventParams: EventParams<CatalogConflictEventPayload> = {\n topic: CATALOG_CONFLICTS_TOPIC,\n eventPayload: {\n unprocessedEntity: entity,\n entityRef,\n newLocationKey: locationKey,\n existingLocationKey: conflictingKey,\n lastConflictAt: DateTime.now().toISO()!,\n },\n };\n await this.options.events.publish(eventParams);\n }\n }\n }\n\n // Lastly, replace refresh state references for the originating entity and any successfully added entities\n await tx<DbRefreshStateReferencesRow>('refresh_state_references')\n // Remove all existing references from the originating entity\n .where({ source_entity_ref: options.sourceEntityRef })\n // And remove any existing references to entities that we're inserting new references for\n .orWhereIn('target_entity_ref', stateReferences)\n .delete();\n await tx.batchInsert(\n 'refresh_state_references',\n stateReferences.map(entityRef => ({\n source_entity_ref: options.sourceEntityRef,\n target_entity_ref: entityRef,\n })),\n BATCH_SIZE,\n );\n }\n}\n"],"names":["initDatabaseMetrics","errors","ConflictError","stringifyEntityRef","syncRelations","generateTargetKey","items","whereInArray","timestampToDateTime","rethrowError","generateStableHash","updateUnprocessedEntity","insertUnprocessedEntity","checkLocationKeyConflict","CATALOG_CONFLICTS_TOPIC","DateTime"],"mappings":"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"}
|