@thecodingsheikh/backstage-plugin-catalog-backend-module-multi-owner-processor 1.0.2 → 1.0.3

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.
@@ -2,7 +2,8 @@
2
2
 
3
3
  var pluginCatalogNode = require('@backstage/plugin-catalog-node');
4
4
  var catalogModel = require('@backstage/catalog-model');
5
- var backstagePluginMultiOwnerCommon = require('@thecodingsheikh/backstage-plugin-multi-owner-common');
5
+ var constants = require('./utils/constants.cjs.js');
6
+ var parseOwners = require('./utils/parseOwners.cjs.js');
6
7
 
7
8
  class MultiOwnerEntitiesProcessor {
8
9
  getProcessorName() {
@@ -13,7 +14,7 @@ class MultiOwnerEntitiesProcessor {
13
14
  if (!spec?.owners) {
14
15
  return entity;
15
16
  }
16
- const owners = backstagePluginMultiOwnerCommon.parseOwners(spec.owners);
17
+ const owners = parseOwners.parseOwners(spec.owners);
17
18
  if (owners.length === 0) {
18
19
  return entity;
19
20
  }
@@ -23,7 +24,7 @@ class MultiOwnerEntitiesProcessor {
23
24
  ...entity.metadata,
24
25
  annotations: {
25
26
  ...entity.metadata.annotations,
26
- [backstagePluginMultiOwnerCommon.MULTI_OWNER_ANNOTATION]: JSON.stringify(owners)
27
+ [constants.MULTI_OWNER_ANNOTATION]: JSON.stringify(owners)
27
28
  }
28
29
  }
29
30
  };
@@ -33,7 +34,7 @@ class MultiOwnerEntitiesProcessor {
33
34
  if (!spec?.owners) {
34
35
  return entity;
35
36
  }
36
- const owners = backstagePluginMultiOwnerCommon.parseOwners(spec.owners);
37
+ const owners = parseOwners.parseOwners(spec.owners);
37
38
  for (const owner of owners) {
38
39
  let ownerRef;
39
40
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"MultiOwnerEntitiesProcessor.cjs.js","sources":["../src/MultiOwnerEntitiesProcessor.ts"],"sourcesContent":["import {\n CatalogProcessor,\n CatalogProcessorEmit,\n processingResult,\n} from '@backstage/plugin-catalog-node';\nimport { Entity } from '@backstage/catalog-model';\nimport { LocationSpec } from '@backstage/plugin-catalog-common';\nimport {\n RELATION_OWNED_BY,\n RELATION_OWNER_OF,\n parseEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport {\n MULTI_OWNER_ANNOTATION,\n parseOwners,\n} from '@thecodingsheikh/backstage-plugin-multi-owner-common';\n\n/**\n * A catalog processor that reads `spec.owners` from entities and emits\n * `ownedBy` / `ownerOf` relations for each owner listed.\n *\n * It also writes the normalized owner list as a JSON annotation\n * (`backstage.io/owners`) so the frontend can display them.\n *\n * @remarks\n * This processor runs *in addition to* the built-in processor that handles\n * `spec.owner`. If both fields are present, owners from both are emitted.\n * Duplicate relations are automatically deduplicated by the catalog engine.\n */\nexport class MultiOwnerEntitiesProcessor implements CatalogProcessor {\n getProcessorName(): string {\n return 'MultiOwnerEntitiesProcessor';\n }\n\n async preProcessEntity(\n entity: Entity,\n _location: LocationSpec,\n ): Promise<Entity> {\n const spec = entity.spec as Record<string, unknown> | undefined;\n if (!spec?.owners) {\n return entity;\n }\n\n const owners = parseOwners(spec.owners);\n if (owners.length === 0) {\n return entity;\n }\n\n // Write the normalized owner list as a JSON annotation so the\n // frontend card can read it without re-parsing spec.\n return {\n ...entity,\n metadata: {\n ...entity.metadata,\n annotations: {\n ...entity.metadata.annotations,\n [MULTI_OWNER_ANNOTATION]: JSON.stringify(owners),\n },\n },\n };\n }\n\n async postProcessEntity(\n entity: Entity,\n _location: LocationSpec,\n emit: CatalogProcessorEmit,\n ): Promise<Entity> {\n const spec = entity.spec as Record<string, unknown> | undefined;\n if (!spec?.owners) {\n return entity;\n }\n\n const owners = parseOwners(spec.owners);\n\n for (const owner of owners) {\n let ownerRef: string;\n try {\n // Validate and normalize the entity reference\n const parsed = parseEntityRef(owner.name, {\n defaultKind: 'group',\n defaultNamespace: entity.metadata.namespace || 'default',\n });\n ownerRef = stringifyEntityRef(parsed);\n } catch {\n // Skip invalid references\n continue;\n }\n\n // Emit the bidirectional ownership relations\n emit(\n processingResult.relation({\n type: RELATION_OWNED_BY,\n source: {\n kind: entity.kind,\n namespace: entity.metadata.namespace || 'default',\n name: entity.metadata.name,\n },\n target: parseEntityRef(ownerRef),\n }),\n );\n\n emit(\n processingResult.relation({\n type: RELATION_OWNER_OF,\n source: parseEntityRef(ownerRef),\n target: {\n kind: entity.kind,\n namespace: entity.metadata.namespace || 'default',\n name: entity.metadata.name,\n },\n }),\n );\n }\n\n return entity;\n }\n}\n"],"names":["parseOwners","MULTI_OWNER_ANNOTATION","parseEntityRef","stringifyEntityRef","processingResult","RELATION_OWNED_BY","RELATION_OWNER_OF"],"mappings":";;;;;;AA8BO,MAAM,2BAAA,CAAwD;AAAA,EACjE,gBAAA,GAA2B;AACvB,IAAA,OAAO,6BAAA;AAAA,EACX;AAAA,EAEA,MAAM,gBAAA,CACF,MAAA,EACA,SAAA,EACe;AACf,IAAA,MAAM,OAAO,MAAA,CAAO,IAAA;AACpB,IAAA,IAAI,CAAC,MAAM,MAAA,EAAQ;AACf,MAAA,OAAO,MAAA;AAAA,IACX;AAEA,IAAA,MAAM,MAAA,GAASA,2CAAA,CAAY,IAAA,CAAK,MAAM,CAAA;AACtC,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACrB,MAAA,OAAO,MAAA;AAAA,IACX;AAIA,IAAA,OAAO;AAAA,MACH,GAAG,MAAA;AAAA,MACH,QAAA,EAAU;AAAA,QACN,GAAG,MAAA,CAAO,QAAA;AAAA,QACV,WAAA,EAAa;AAAA,UACT,GAAG,OAAO,QAAA,CAAS,WAAA;AAAA,UACnB,CAACC,sDAAsB,GAAG,IAAA,CAAK,UAAU,MAAM;AAAA;AACnD;AACJ,KACJ;AAAA,EACJ;AAAA,EAEA,MAAM,iBAAA,CACF,MAAA,EACA,SAAA,EACA,IAAA,EACe;AACf,IAAA,MAAM,OAAO,MAAA,CAAO,IAAA;AACpB,IAAA,IAAI,CAAC,MAAM,MAAA,EAAQ;AACf,MAAA,OAAO,MAAA;AAAA,IACX;AAEA,IAAA,MAAM,MAAA,GAASD,2CAAA,CAAY,IAAA,CAAK,MAAM,CAAA;AAEtC,IAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AACxB,MAAA,IAAI,QAAA;AACJ,MAAA,IAAI;AAEA,QAAA,MAAM,MAAA,GAASE,2BAAA,CAAe,KAAA,CAAM,IAAA,EAAM;AAAA,UACtC,WAAA,EAAa,OAAA;AAAA,UACb,gBAAA,EAAkB,MAAA,CAAO,QAAA,CAAS,SAAA,IAAa;AAAA,SAClD,CAAA;AACD,QAAA,QAAA,GAAWC,gCAAmB,MAAM,CAAA;AAAA,MACxC,CAAA,CAAA,MAAQ;AAEJ,QAAA;AAAA,MACJ;AAGA,MAAA,IAAA;AAAA,QACIC,mCAAiB,QAAA,CAAS;AAAA,UACtB,IAAA,EAAMC,8BAAA;AAAA,UACN,MAAA,EAAQ;AAAA,YACJ,MAAM,MAAA,CAAO,IAAA;AAAA,YACb,SAAA,EAAW,MAAA,CAAO,QAAA,CAAS,SAAA,IAAa,SAAA;AAAA,YACxC,IAAA,EAAM,OAAO,QAAA,CAAS;AAAA,WAC1B;AAAA,UACA,MAAA,EAAQH,4BAAe,QAAQ;AAAA,SAClC;AAAA,OACL;AAEA,MAAA,IAAA;AAAA,QACIE,mCAAiB,QAAA,CAAS;AAAA,UACtB,IAAA,EAAME,8BAAA;AAAA,UACN,MAAA,EAAQJ,4BAAe,QAAQ,CAAA;AAAA,UAC/B,MAAA,EAAQ;AAAA,YACJ,MAAM,MAAA,CAAO,IAAA;AAAA,YACb,SAAA,EAAW,MAAA,CAAO,QAAA,CAAS,SAAA,IAAa,SAAA;AAAA,YACxC,IAAA,EAAM,OAAO,QAAA,CAAS;AAAA;AAC1B,SACH;AAAA,OACL;AAAA,IACJ;AAEA,IAAA,OAAO,MAAA;AAAA,EACX;AACJ;;;;"}
1
+ {"version":3,"file":"MultiOwnerEntitiesProcessor.cjs.js","sources":["../src/MultiOwnerEntitiesProcessor.ts"],"sourcesContent":["import {\n CatalogProcessor,\n CatalogProcessorEmit,\n processingResult,\n} from '@backstage/plugin-catalog-node';\nimport { Entity } from '@backstage/catalog-model';\nimport { LocationSpec } from '@backstage/plugin-catalog-common';\nimport {\n RELATION_OWNED_BY,\n RELATION_OWNER_OF,\n parseEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport {\n MULTI_OWNER_ANNOTATION,\n} from './utils/constants';\nimport { parseOwners } from './utils/parseOwners';\n\n/**\n * A catalog processor that reads `spec.owners` from entities and emits\n * `ownedBy` / `ownerOf` relations for each owner listed.\n *\n * It also writes the normalized owner list as a JSON annotation\n * (`backstage.io/owners`) so the frontend can display them.\n *\n * @remarks\n * This processor runs *in addition to* the built-in processor that handles\n * `spec.owner`. If both fields are present, owners from both are emitted.\n * Duplicate relations are automatically deduplicated by the catalog engine.\n */\nexport class MultiOwnerEntitiesProcessor implements CatalogProcessor {\n getProcessorName(): string {\n return 'MultiOwnerEntitiesProcessor';\n }\n\n async preProcessEntity(\n entity: Entity,\n _location: LocationSpec,\n ): Promise<Entity> {\n const spec = entity.spec as Record<string, unknown> | undefined;\n if (!spec?.owners) {\n return entity;\n }\n\n const owners = parseOwners(spec.owners);\n if (owners.length === 0) {\n return entity;\n }\n\n // Write the normalized owner list as a JSON annotation so the\n // frontend card can read it without re-parsing spec.\n return {\n ...entity,\n metadata: {\n ...entity.metadata,\n annotations: {\n ...entity.metadata.annotations,\n [MULTI_OWNER_ANNOTATION]: JSON.stringify(owners),\n },\n },\n };\n }\n\n async postProcessEntity(\n entity: Entity,\n _location: LocationSpec,\n emit: CatalogProcessorEmit,\n ): Promise<Entity> {\n const spec = entity.spec as Record<string, unknown> | undefined;\n if (!spec?.owners) {\n return entity;\n }\n\n const owners = parseOwners(spec.owners);\n\n for (const owner of owners) {\n let ownerRef: string;\n try {\n // Validate and normalize the entity reference\n const parsed = parseEntityRef(owner.name, {\n defaultKind: 'group',\n defaultNamespace: entity.metadata.namespace || 'default',\n });\n ownerRef = stringifyEntityRef(parsed);\n } catch {\n // Skip invalid references\n continue;\n }\n\n // Emit the bidirectional ownership relations\n emit(\n processingResult.relation({\n type: RELATION_OWNED_BY,\n source: {\n kind: entity.kind,\n namespace: entity.metadata.namespace || 'default',\n name: entity.metadata.name,\n },\n target: parseEntityRef(ownerRef),\n }),\n );\n\n emit(\n processingResult.relation({\n type: RELATION_OWNER_OF,\n source: parseEntityRef(ownerRef),\n target: {\n kind: entity.kind,\n namespace: entity.metadata.namespace || 'default',\n name: entity.metadata.name,\n },\n }),\n );\n }\n\n return entity;\n }\n}\n"],"names":["parseOwners","MULTI_OWNER_ANNOTATION","parseEntityRef","stringifyEntityRef","processingResult","RELATION_OWNED_BY","RELATION_OWNER_OF"],"mappings":";;;;;;;AA8BO,MAAM,2BAAA,CAAwD;AAAA,EACjE,gBAAA,GAA2B;AACvB,IAAA,OAAO,6BAAA;AAAA,EACX;AAAA,EAEA,MAAM,gBAAA,CACF,MAAA,EACA,SAAA,EACe;AACf,IAAA,MAAM,OAAO,MAAA,CAAO,IAAA;AACpB,IAAA,IAAI,CAAC,MAAM,MAAA,EAAQ;AACf,MAAA,OAAO,MAAA;AAAA,IACX;AAEA,IAAA,MAAM,MAAA,GAASA,uBAAA,CAAY,IAAA,CAAK,MAAM,CAAA;AACtC,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACrB,MAAA,OAAO,MAAA;AAAA,IACX;AAIA,IAAA,OAAO;AAAA,MACH,GAAG,MAAA;AAAA,MACH,QAAA,EAAU;AAAA,QACN,GAAG,MAAA,CAAO,QAAA;AAAA,QACV,WAAA,EAAa;AAAA,UACT,GAAG,OAAO,QAAA,CAAS,WAAA;AAAA,UACnB,CAACC,gCAAsB,GAAG,IAAA,CAAK,UAAU,MAAM;AAAA;AACnD;AACJ,KACJ;AAAA,EACJ;AAAA,EAEA,MAAM,iBAAA,CACF,MAAA,EACA,SAAA,EACA,IAAA,EACe;AACf,IAAA,MAAM,OAAO,MAAA,CAAO,IAAA;AACpB,IAAA,IAAI,CAAC,MAAM,MAAA,EAAQ;AACf,MAAA,OAAO,MAAA;AAAA,IACX;AAEA,IAAA,MAAM,MAAA,GAASD,uBAAA,CAAY,IAAA,CAAK,MAAM,CAAA;AAEtC,IAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AACxB,MAAA,IAAI,QAAA;AACJ,MAAA,IAAI;AAEA,QAAA,MAAM,MAAA,GAASE,2BAAA,CAAe,KAAA,CAAM,IAAA,EAAM;AAAA,UACtC,WAAA,EAAa,OAAA;AAAA,UACb,gBAAA,EAAkB,MAAA,CAAO,QAAA,CAAS,SAAA,IAAa;AAAA,SAClD,CAAA;AACD,QAAA,QAAA,GAAWC,gCAAmB,MAAM,CAAA;AAAA,MACxC,CAAA,CAAA,MAAQ;AAEJ,QAAA;AAAA,MACJ;AAGA,MAAA,IAAA;AAAA,QACIC,mCAAiB,QAAA,CAAS;AAAA,UACtB,IAAA,EAAMC,8BAAA;AAAA,UACN,MAAA,EAAQ;AAAA,YACJ,MAAM,MAAA,CAAO,IAAA;AAAA,YACb,SAAA,EAAW,MAAA,CAAO,QAAA,CAAS,SAAA,IAAa,SAAA;AAAA,YACxC,IAAA,EAAM,OAAO,QAAA,CAAS;AAAA,WAC1B;AAAA,UACA,MAAA,EAAQH,4BAAe,QAAQ;AAAA,SAClC;AAAA,OACL;AAEA,MAAA,IAAA;AAAA,QACIE,mCAAiB,QAAA,CAAS;AAAA,UACtB,IAAA,EAAME,8BAAA;AAAA,UACN,MAAA,EAAQJ,4BAAe,QAAQ,CAAA;AAAA,UAC/B,MAAA,EAAQ;AAAA,YACJ,MAAM,MAAA,CAAO,IAAA;AAAA,YACb,SAAA,EAAW,MAAA,CAAO,QAAA,CAAS,SAAA,IAAa,SAAA;AAAA,YACxC,IAAA,EAAM,OAAO,QAAA,CAAS;AAAA;AAC1B,SACH;AAAA,OACL;AAAA,IACJ;AAEA,IAAA,OAAO,MAAA;AAAA,EACX;AACJ;;;;"}
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ const MULTI_OWNER_ANNOTATION = "backstage.io/owners";
4
+
5
+ exports.MULTI_OWNER_ANNOTATION = MULTI_OWNER_ANNOTATION;
6
+ //# sourceMappingURL=constants.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.cjs.js","sources":["../../src/utils/constants.ts"],"sourcesContent":["/**\n * Annotation key used to store the normalized list of owners as JSON\n * on a processed entity. This is written by the backend processor and\n * read by the frontend card.\n */\nexport const MULTI_OWNER_ANNOTATION = 'backstage.io/owners';\n\n/**\n * The field name under `spec` where multiple owners are declared\n * in the entity YAML.\n */\nexport const MULTI_OWNER_SPEC_FIELD = 'owners';\n"],"names":[],"mappings":";;AAKO,MAAM,sBAAA,GAAyB;;;;"}
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ function parseOwners(raw) {
4
+ if (!Array.isArray(raw)) {
5
+ return [];
6
+ }
7
+ const result = [];
8
+ for (const entry of raw) {
9
+ if (typeof entry === "string") {
10
+ const trimmed = entry.trim();
11
+ if (trimmed.length > 0) {
12
+ result.push({ name: trimmed });
13
+ }
14
+ } else if (typeof entry === "object" && entry !== null && // @ts-ignore
15
+ typeof entry.name === "string") {
16
+ const trimmedName = entry.name.trim();
17
+ if (trimmedName.length > 0) {
18
+ result.push({
19
+ name: trimmedName,
20
+ // @ts-ignore
21
+ ...entry.role ? { role: entry.role.trim() } : {}
22
+ });
23
+ }
24
+ }
25
+ }
26
+ return result;
27
+ }
28
+
29
+ exports.parseOwners = parseOwners;
30
+ //# sourceMappingURL=parseOwners.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseOwners.cjs.js","sources":["../../src/utils/parseOwners.ts"],"sourcesContent":["import { MultiOwnerEntry, MultiOwnerSpec } from './types';\n\n/**\n * Normalizes a `spec.owners` value into a consistent array of\n * {@link MultiOwnerEntry} objects.\n *\n * Accepts both string shorthand (`\"group:default/team\"`) and\n * full objects (`{ name: \"group:default/team\", role: \"maintainer\" }`).\n *\n * @param raw - The raw `spec.owners` value from the entity YAML\n * @returns A normalized array of owner entries, or an empty array if input is invalid\n */\nexport function parseOwners(raw: unknown): MultiOwnerEntry[] {\n if (!Array.isArray(raw)) {\n return [];\n }\n\n const result: MultiOwnerEntry[] = [];\n\n for (const entry of raw as MultiOwnerSpec) {\n if (typeof entry === 'string') {\n const trimmed = entry.trim();\n if (trimmed.length > 0) {\n result.push({ name: trimmed });\n }\n } else if (\n typeof entry === 'object' &&\n entry !== null &&\n // @ts-ignore\n typeof entry.name === 'string'\n ) {\n // @ts-ignore\n const trimmedName = entry.name.trim();\n if (trimmedName.length > 0) {\n result.push({\n name: trimmedName,\n // @ts-ignore\n ...(entry.role ? { role: entry.role.trim() } : {}),\n });\n }\n }\n // silently skip malformed entries\n }\n\n return result;\n}\n"],"names":[],"mappings":";;AAYO,SAAS,YAAY,GAAA,EAAiC;AACzD,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG;AACrB,IAAA,OAAO,EAAC;AAAA,EACZ;AAEA,EAAA,MAAM,SAA4B,EAAC;AAEnC,EAAA,KAAA,MAAW,SAAS,GAAA,EAAuB;AACvC,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,MAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAC3B,MAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACpB,QAAA,MAAA,CAAO,IAAA,CAAK,EAAE,IAAA,EAAM,OAAA,EAAS,CAAA;AAAA,MACjC;AAAA,IACJ,CAAA,MAAA,IACI,OAAO,KAAA,KAAU,QAAA,IACjB,KAAA,KAAU,IAAA;AAAA,IAEV,OAAO,KAAA,CAAM,IAAA,KAAS,QAAA,EACxB;AAEE,MAAA,MAAM,WAAA,GAAc,KAAA,CAAM,IAAA,CAAK,IAAA,EAAK;AACpC,MAAA,IAAI,WAAA,CAAY,SAAS,CAAA,EAAG;AACxB,QAAA,MAAA,CAAO,IAAA,CAAK;AAAA,UACR,IAAA,EAAM,WAAA;AAAA;AAAA,UAEN,GAAI,KAAA,CAAM,IAAA,GAAO,EAAE,IAAA,EAAM,MAAM,IAAA,CAAK,IAAA,EAAK,EAAE,GAAI;AAAC,SACnD,CAAA;AAAA,MACL;AAAA,IACJ;AAAA,EAEJ;AAEA,EAAA,OAAO,MAAA;AACX;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thecodingsheikh/backstage-plugin-catalog-backend-module-multi-owner-processor",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "types": "./dist/index.d.ts",
@@ -34,8 +34,7 @@
34
34
  "@backstage/backend-plugin-api": "^1.1.1",
35
35
  "@backstage/catalog-model": "^1.7.3",
36
36
  "@backstage/plugin-catalog-common": "^1.1.2",
37
- "@backstage/plugin-catalog-node": "^1.15.1",
38
- "@thecodingsheikh/backstage-plugin-multi-owner-common": "^1.0.2"
37
+ "@backstage/plugin-catalog-node": "^1.15.1"
39
38
  },
40
39
  "devDependencies": {
41
40
  "@backstage/backend-test-utils": "^1.2.1",