@backstage-community/plugin-tech-insights-backend 2.2.1 → 3.0.0

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/README.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  The backend plugin for Tech Insights.
4
4
 
5
+ ## Breaking Changes in v3.0.0
6
+
7
+ This version includes breaking changes related to permissions. Please review the changes carefully before upgrading:
8
+
9
+ - Added required permissions for accessing Tech Insights features
10
+ - Users must now have the appropriate policies added to their roles to access Tech Insights functionality
11
+ - This change enforces better security by ensuring explicit permission grants for Tech Insights operations
12
+
13
+ To upgrade, you'll need to:
14
+
15
+ 1. Update your permission policies to include the necessary Tech Insights permissions
16
+ 2. Ensure your users' roles have the appropriate policies assigned
17
+
5
18
  It provides the API for the frontend tech insights, scorecards and fact visualization functionality, as well as a framework to run fact retrievers and store fact values in to a data store.
6
19
 
7
20
  Looking for the old backend installation docs? Visit [here](./docs/old-backend-system.md).
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
2
- import { LoggerService, DatabaseService, DiscoveryService, SchedulerService, AuthService, UrlReaderService } from '@backstage/backend-plugin-api';
2
+ import { LoggerService, DatabaseService, PermissionsService, HttpAuthService, DiscoveryService, SchedulerService, AuthService, UrlReaderService } from '@backstage/backend-plugin-api';
3
3
  import { FactRetrieverRegistry as FactRetrieverRegistry$1, PersistenceContext as PersistenceContext$1, FactRetriever, FactLifecycle, FactRetrieverRegistration, FactChecker, FactCheckerFactory } from '@backstage-community/plugin-tech-insights-node';
4
4
  import { HumanDuration } from '@backstage/types';
5
5
  import { Duration } from 'luxon';
@@ -165,6 +165,14 @@ interface RouterOptions<CheckType extends Check, CheckResultType extends CheckRe
165
165
  * Implementation of Winston logger
166
166
  */
167
167
  logger: LoggerService;
168
+ /**
169
+ * Implementation of PermissionsService
170
+ */
171
+ permissions: PermissionsService;
172
+ /**
173
+ * Implementation of HttpAuthService
174
+ */
175
+ httpAuth: HttpAuthService;
168
176
  }
169
177
  /**
170
178
  * @public
@@ -4,14 +4,17 @@ var config = require('@backstage/config');
4
4
  var createFactRetriever = require('../service/fact/createFactRetriever.cjs.js');
5
5
  require('@backstage/catalog-client');
6
6
  require('lodash/isEmpty');
7
+ require('luxon');
7
8
  require('../service/fact/factRetrievers/techdocsFactRetriever.cjs.js');
8
9
  require('../service/persistence/persistenceContext.cjs.js');
9
10
  require('express');
10
11
  require('express-promise-router');
11
- require('luxon');
12
+ require('@backstage-community/plugin-tech-insights-common');
12
13
  require('@backstage/catalog-model');
13
14
  require('@backstage/errors');
14
15
  require('@backstage/backend-defaults/rootHttpRouter');
16
+ require('@backstage/plugin-permission-common');
17
+ require('@backstage/plugin-permission-node');
15
18
  require('p-limit');
16
19
 
17
20
  function readLifecycleConfig(config$1) {
@@ -1 +1 @@
1
- {"version":3,"file":"config.cjs.js","sources":["../../src/plugin/config.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config, readDurationFromConfig } from '@backstage/config';\nimport {\n FactLifecycle,\n FactRetriever,\n FactRetrieverRegistration,\n} from '@backstage-community/plugin-tech-insights-node';\nimport {\n createFactRetrieverRegistration,\n FactRetrieverRegistrationOptions,\n} from '../service';\n\ntype FactRetrieverConfig = Omit<\n FactRetrieverRegistrationOptions,\n 'factRetriever'\n>;\n\nfunction readLifecycleConfig(\n config: Config | undefined,\n): FactLifecycle | undefined {\n if (!config) {\n return undefined;\n }\n\n if (config.has('maxItems')) {\n return {\n maxItems: config.getNumber('maxItems'),\n };\n }\n\n return {\n timeToLive: readDurationFromConfig(config.getConfig('timeToLive')),\n };\n}\n\nfunction readFactRetrieverConfig(\n config: Config,\n name: string,\n): FactRetrieverConfig | undefined {\n const factRetrieverConfig = config.getOptionalConfig(\n `techInsights.factRetrievers.${name}`,\n );\n if (!factRetrieverConfig) {\n return undefined;\n }\n\n const cadence = factRetrieverConfig.getString('cadence');\n const initialDelay = factRetrieverConfig.has('initialDelay')\n ? readDurationFromConfig(factRetrieverConfig.getConfig('initialDelay'))\n : undefined;\n const lifecycle = readLifecycleConfig(\n factRetrieverConfig.getOptionalConfig('lifecycle'),\n );\n const timeout = factRetrieverConfig.has('timeout')\n ? readDurationFromConfig(factRetrieverConfig.getConfig('timeout'))\n : undefined;\n\n return {\n cadence,\n initialDelay,\n lifecycle,\n timeout,\n };\n}\n\nexport function createFactRetrieverRegistrationFromConfig(\n config: Config,\n name: string,\n factRetriever: FactRetriever,\n): FactRetrieverRegistration | undefined {\n const factRetrieverConfig = readFactRetrieverConfig(config, name);\n\n return factRetrieverConfig\n ? createFactRetrieverRegistration({\n ...factRetrieverConfig,\n factRetriever,\n })\n : undefined;\n}\n"],"names":["config","readDurationFromConfig","createFactRetrieverRegistration"],"mappings":";;;;;;;;;;;;;;;;AAgCA,SAAS,oBACPA,QAC2B,EAAA;AAC3B,EAAA,IAAI,CAACA,QAAQ,EAAA;AACX,IAAO,OAAA,KAAA,CAAA;AAAA;AAGT,EAAI,IAAAA,QAAA,CAAO,GAAI,CAAA,UAAU,CAAG,EAAA;AAC1B,IAAO,OAAA;AAAA,MACL,QAAA,EAAUA,QAAO,CAAA,SAAA,CAAU,UAAU;AAAA,KACvC;AAAA;AAGF,EAAO,OAAA;AAAA,IACL,UAAY,EAAAC,6BAAA,CAAuBD,QAAO,CAAA,SAAA,CAAU,YAAY,CAAC;AAAA,GACnE;AACF;AAEA,SAAS,uBAAA,CACPA,UACA,IACiC,EAAA;AACjC,EAAA,MAAM,sBAAsBA,QAAO,CAAA,iBAAA;AAAA,IACjC,+BAA+B,IAAI,CAAA;AAAA,GACrC;AACA,EAAA,IAAI,CAAC,mBAAqB,EAAA;AACxB,IAAO,OAAA,KAAA,CAAA;AAAA;AAGT,EAAM,MAAA,OAAA,GAAU,mBAAoB,CAAA,SAAA,CAAU,SAAS,CAAA;AACvD,EAAM,MAAA,YAAA,GAAe,mBAAoB,CAAA,GAAA,CAAI,cAAc,CAAA,GACvDC,8BAAuB,mBAAoB,CAAA,SAAA,CAAU,cAAc,CAAC,CACpE,GAAA,KAAA,CAAA;AACJ,EAAA,MAAM,SAAY,GAAA,mBAAA;AAAA,IAChB,mBAAA,CAAoB,kBAAkB,WAAW;AAAA,GACnD;AACA,EAAM,MAAA,OAAA,GAAU,mBAAoB,CAAA,GAAA,CAAI,SAAS,CAAA,GAC7CA,8BAAuB,mBAAoB,CAAA,SAAA,CAAU,SAAS,CAAC,CAC/D,GAAA,KAAA,CAAA;AAEJ,EAAO,OAAA;AAAA,IACL,OAAA;AAAA,IACA,YAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACF;AAEgB,SAAA,yCAAA,CACd,MACA,EAAA,IAAA,EACA,aACuC,EAAA;AACvC,EAAM,MAAA,mBAAA,GAAsB,uBAAwB,CAAA,MAAA,EAAQ,IAAI,CAAA;AAEhE,EAAA,OAAO,sBACHC,mDAAgC,CAAA;AAAA,IAC9B,GAAG,mBAAA;AAAA,IACH;AAAA,GACD,CACD,GAAA,KAAA,CAAA;AACN;;;;"}
1
+ {"version":3,"file":"config.cjs.js","sources":["../../src/plugin/config.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config, readDurationFromConfig } from '@backstage/config';\nimport {\n FactLifecycle,\n FactRetriever,\n FactRetrieverRegistration,\n} from '@backstage-community/plugin-tech-insights-node';\nimport {\n createFactRetrieverRegistration,\n FactRetrieverRegistrationOptions,\n} from '../service';\n\ntype FactRetrieverConfig = Omit<\n FactRetrieverRegistrationOptions,\n 'factRetriever'\n>;\n\nfunction readLifecycleConfig(\n config: Config | undefined,\n): FactLifecycle | undefined {\n if (!config) {\n return undefined;\n }\n\n if (config.has('maxItems')) {\n return {\n maxItems: config.getNumber('maxItems'),\n };\n }\n\n return {\n timeToLive: readDurationFromConfig(config.getConfig('timeToLive')),\n };\n}\n\nfunction readFactRetrieverConfig(\n config: Config,\n name: string,\n): FactRetrieverConfig | undefined {\n const factRetrieverConfig = config.getOptionalConfig(\n `techInsights.factRetrievers.${name}`,\n );\n if (!factRetrieverConfig) {\n return undefined;\n }\n\n const cadence = factRetrieverConfig.getString('cadence');\n const initialDelay = factRetrieverConfig.has('initialDelay')\n ? readDurationFromConfig(factRetrieverConfig.getConfig('initialDelay'))\n : undefined;\n const lifecycle = readLifecycleConfig(\n factRetrieverConfig.getOptionalConfig('lifecycle'),\n );\n const timeout = factRetrieverConfig.has('timeout')\n ? readDurationFromConfig(factRetrieverConfig.getConfig('timeout'))\n : undefined;\n\n return {\n cadence,\n initialDelay,\n lifecycle,\n timeout,\n };\n}\n\nexport function createFactRetrieverRegistrationFromConfig(\n config: Config,\n name: string,\n factRetriever: FactRetriever,\n): FactRetrieverRegistration | undefined {\n const factRetrieverConfig = readFactRetrieverConfig(config, name);\n\n return factRetrieverConfig\n ? createFactRetrieverRegistration({\n ...factRetrieverConfig,\n factRetriever,\n })\n : undefined;\n}\n"],"names":["config","readDurationFromConfig","createFactRetrieverRegistration"],"mappings":";;;;;;;;;;;;;;;;;;;AAgCA,SAAS,oBACPA,QAC2B,EAAA;AAC3B,EAAA,IAAI,CAACA,QAAQ,EAAA;AACX,IAAO,OAAA,KAAA,CAAA;AAAA;AAGT,EAAI,IAAAA,QAAA,CAAO,GAAI,CAAA,UAAU,CAAG,EAAA;AAC1B,IAAO,OAAA;AAAA,MACL,QAAA,EAAUA,QAAO,CAAA,SAAA,CAAU,UAAU;AAAA,KACvC;AAAA;AAGF,EAAO,OAAA;AAAA,IACL,UAAY,EAAAC,6BAAA,CAAuBD,QAAO,CAAA,SAAA,CAAU,YAAY,CAAC;AAAA,GACnE;AACF;AAEA,SAAS,uBAAA,CACPA,UACA,IACiC,EAAA;AACjC,EAAA,MAAM,sBAAsBA,QAAO,CAAA,iBAAA;AAAA,IACjC,+BAA+B,IAAI,CAAA;AAAA,GACrC;AACA,EAAA,IAAI,CAAC,mBAAqB,EAAA;AACxB,IAAO,OAAA,KAAA,CAAA;AAAA;AAGT,EAAM,MAAA,OAAA,GAAU,mBAAoB,CAAA,SAAA,CAAU,SAAS,CAAA;AACvD,EAAM,MAAA,YAAA,GAAe,mBAAoB,CAAA,GAAA,CAAI,cAAc,CAAA,GACvDC,8BAAuB,mBAAoB,CAAA,SAAA,CAAU,cAAc,CAAC,CACpE,GAAA,KAAA,CAAA;AACJ,EAAA,MAAM,SAAY,GAAA,mBAAA;AAAA,IAChB,mBAAA,CAAoB,kBAAkB,WAAW;AAAA,GACnD;AACA,EAAM,MAAA,OAAA,GAAU,mBAAoB,CAAA,GAAA,CAAI,SAAS,CAAA,GAC7CA,8BAAuB,mBAAoB,CAAA,SAAA,CAAU,SAAS,CAAC,CAC/D,GAAA,KAAA,CAAA;AAEJ,EAAO,OAAA;AAAA,IACL,OAAA;AAAA,IACA,YAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACF;AAEgB,SAAA,yCAAA,CACd,MACA,EAAA,IAAA,EACA,aACuC,EAAA;AACvC,EAAM,MAAA,mBAAA,GAAsB,uBAAwB,CAAA,MAAA,EAAQ,IAAI,CAAA;AAEhE,EAAA,OAAO,sBACHC,mDAAgC,CAAA;AAAA,IAC9B,GAAG,mBAAA;AAAA,IACH;AAAA,GACD,CACD,GAAA,KAAA,CAAA;AACN;;;;"}
@@ -55,7 +55,9 @@ const techInsightsPlugin = backendPluginApi.createBackendPlugin({
55
55
  logger: backendPluginApi.coreServices.logger,
56
56
  scheduler: backendPluginApi.coreServices.scheduler,
57
57
  auth: backendPluginApi.coreServices.auth,
58
- urlReader: backendPluginApi.coreServices.urlReader
58
+ urlReader: backendPluginApi.coreServices.urlReader,
59
+ httpAuth: backendPluginApi.coreServices.httpAuth,
60
+ permissions: backendPluginApi.coreServices.permissions
59
61
  },
60
62
  async init({
61
63
  config: config$1,
@@ -65,7 +67,9 @@ const techInsightsPlugin = backendPluginApi.createBackendPlugin({
65
67
  logger,
66
68
  scheduler,
67
69
  auth,
68
- urlReader
70
+ urlReader,
71
+ httpAuth,
72
+ permissions
69
73
  }) {
70
74
  const factRetrievers = Object.entries(
71
75
  addedFactRetrievers
@@ -93,7 +97,9 @@ const techInsightsPlugin = backendPluginApi.createBackendPlugin({
93
97
  await router.createRouter({
94
98
  ...context,
95
99
  config: config$1,
96
- logger
100
+ logger,
101
+ permissions,
102
+ httpAuth
97
103
  })
98
104
  );
99
105
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.cjs.js","sources":["../../src/plugin/plugin.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport {\n CheckResult,\n Check,\n} from '@backstage-community/plugin-tech-insights-common';\nimport {\n FactCheckerFactory,\n FactRetriever,\n FactRetrieverRegistration,\n FactRetrieverRegistry,\n PersistenceContext,\n techInsightsFactCheckerFactoryExtensionPoint,\n techInsightsFactRetrieverRegistryExtensionPoint,\n techInsightsFactRetrieversExtensionPoint,\n techInsightsPersistenceContextExtensionPoint,\n} from '@backstage-community/plugin-tech-insights-node';\nimport {\n buildTechInsightsContext,\n createRouter,\n entityMetadataFactRetriever,\n entityOwnershipFactRetriever,\n techdocsFactRetriever,\n} from '../service';\nimport { createFactRetrieverRegistrationFromConfig } from './config';\n\n/**\n * The tech-insights backend plugin.\n *\n * @public\n */\nexport const techInsightsPlugin = createBackendPlugin({\n pluginId: 'tech-insights',\n register(env) {\n let factCheckerFactory: FactCheckerFactory<Check, CheckResult> | undefined =\n undefined;\n env.registerExtensionPoint(techInsightsFactCheckerFactoryExtensionPoint, {\n setFactCheckerFactory<\n CheckType extends Check,\n CheckResultType extends CheckResult,\n >(factory: FactCheckerFactory<CheckType, CheckResultType>): void {\n factCheckerFactory = factory;\n },\n });\n\n let factRetrieverRegistry: FactRetrieverRegistry | undefined = undefined;\n env.registerExtensionPoint(\n techInsightsFactRetrieverRegistryExtensionPoint,\n {\n setFactRetrieverRegistry(registry: FactRetrieverRegistry): void {\n factRetrieverRegistry = registry;\n },\n },\n );\n\n // initialized with built-in fact retrievers\n // only added as registration if there is config for them\n const addedFactRetrievers: Record<string, FactRetriever> = {\n entityMetadataFactRetriever,\n entityOwnershipFactRetriever,\n techdocsFactRetriever,\n };\n env.registerExtensionPoint(techInsightsFactRetrieversExtensionPoint, {\n addFactRetrievers(factRetrievers: Record<string, FactRetriever>): void {\n Object.entries(factRetrievers).forEach(([key, value]) => {\n addedFactRetrievers[key] = value;\n });\n },\n });\n\n let persistenceContext: PersistenceContext | undefined = undefined;\n env.registerExtensionPoint(techInsightsPersistenceContextExtensionPoint, {\n setPersistenceContext(context: PersistenceContext): void {\n persistenceContext = context;\n },\n });\n\n env.registerInit({\n deps: {\n config: coreServices.rootConfig,\n database: coreServices.database,\n discovery: coreServices.discovery,\n httpRouter: coreServices.httpRouter,\n logger: coreServices.logger,\n scheduler: coreServices.scheduler,\n auth: coreServices.auth,\n urlReader: coreServices.urlReader,\n },\n async init({\n config,\n database,\n discovery,\n httpRouter,\n logger,\n scheduler,\n auth,\n urlReader,\n }) {\n const factRetrievers: FactRetrieverRegistration[] = Object.entries(\n addedFactRetrievers,\n )\n .map(([name, factRetriever]) =>\n createFactRetrieverRegistrationFromConfig(\n config,\n name,\n factRetriever,\n ),\n )\n .filter(registration => registration) as FactRetrieverRegistration[];\n\n const context = await buildTechInsightsContext({\n config,\n database,\n discovery,\n factCheckerFactory,\n factRetrieverRegistry,\n factRetrievers,\n logger,\n persistenceContext,\n scheduler,\n auth,\n urlReader,\n });\n\n httpRouter.use(\n await createRouter({\n ...context,\n config,\n logger,\n }),\n );\n },\n });\n },\n});\n"],"names":["createBackendPlugin","techInsightsFactCheckerFactoryExtensionPoint","techInsightsFactRetrieverRegistryExtensionPoint","entityMetadataFactRetriever","entityOwnershipFactRetriever","techdocsFactRetriever","techInsightsFactRetrieversExtensionPoint","techInsightsPersistenceContextExtensionPoint","coreServices","config","createFactRetrieverRegistrationFromConfig","buildTechInsightsContext","createRouter"],"mappings":";;;;;;;;;;;;AAiDO,MAAM,qBAAqBA,oCAAoB,CAAA;AAAA,EACpD,QAAU,EAAA,eAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAA,IAAI,kBACF,GAAA,KAAA,CAAA;AACF,IAAA,GAAA,CAAI,uBAAuBC,mEAA8C,EAAA;AAAA,MACvE,sBAGE,OAA+D,EAAA;AAC/D,QAAqB,kBAAA,GAAA,OAAA;AAAA;AACvB,KACD,CAAA;AAED,IAAA,IAAI,qBAA2D,GAAA,KAAA,CAAA;AAC/D,IAAI,GAAA,CAAA,sBAAA;AAAA,MACFC,sEAAA;AAAA,MACA;AAAA,QACE,yBAAyB,QAAuC,EAAA;AAC9D,UAAwB,qBAAA,GAAA,QAAA;AAAA;AAC1B;AACF,KACF;AAIA,IAAA,MAAM,mBAAqD,GAAA;AAAA,mCACzDC,uDAAA;AAAA,oCACAC,yDAAA;AAAA,6BACAC;AAAA,KACF;AACA,IAAA,GAAA,CAAI,uBAAuBC,+DAA0C,EAAA;AAAA,MACnE,kBAAkB,cAAqD,EAAA;AACrE,QAAO,MAAA,CAAA,OAAA,CAAQ,cAAc,CAAE,CAAA,OAAA,CAAQ,CAAC,CAAC,GAAA,EAAK,KAAK,CAAM,KAAA;AACvD,UAAA,mBAAA,CAAoB,GAAG,CAAI,GAAA,KAAA;AAAA,SAC5B,CAAA;AAAA;AACH,KACD,CAAA;AAED,IAAA,IAAI,kBAAqD,GAAA,KAAA,CAAA;AACzD,IAAA,GAAA,CAAI,uBAAuBC,mEAA8C,EAAA;AAAA,MACvE,sBAAsB,OAAmC,EAAA;AACvD,QAAqB,kBAAA,GAAA,OAAA;AAAA;AACvB,KACD,CAAA;AAED,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,QAAQC,6BAAa,CAAA,UAAA;AAAA,QACrB,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,WAAWA,6BAAa,CAAA,SAAA;AAAA,QACxB,YAAYA,6BAAa,CAAA,UAAA;AAAA,QACzB,QAAQA,6BAAa,CAAA,MAAA;AAAA,QACrB,WAAWA,6BAAa,CAAA,SAAA;AAAA,QACxB,MAAMA,6BAAa,CAAA,IAAA;AAAA,QACnB,WAAWA,6BAAa,CAAA;AAAA,OAC1B;AAAA,MACA,MAAM,IAAK,CAAA;AAAA,gBACTC,QAAA;AAAA,QACA,QAAA;AAAA,QACA,SAAA;AAAA,QACA,UAAA;AAAA,QACA,MAAA;AAAA,QACA,SAAA;AAAA,QACA,IAAA;AAAA,QACA;AAAA,OACC,EAAA;AACD,QAAA,MAAM,iBAA8C,MAAO,CAAA,OAAA;AAAA,UACzD;AAAA,SAEC,CAAA,GAAA;AAAA,UAAI,CAAC,CAAC,IAAM,EAAA,aAAa,CACxB,KAAAC,gDAAA;AAAA,YACED,QAAA;AAAA,YACA,IAAA;AAAA,YACA;AAAA;AACF,SACF,CACC,MAAO,CAAA,CAAA,YAAA,KAAgB,YAAY,CAAA;AAEtC,QAAM,MAAA,OAAA,GAAU,MAAME,mDAAyB,CAAA;AAAA,kBAC7CF,QAAA;AAAA,UACA,QAAA;AAAA,UACA,SAAA;AAAA,UACA,kBAAA;AAAA,UACA,qBAAA;AAAA,UACA,cAAA;AAAA,UACA,MAAA;AAAA,UACA,kBAAA;AAAA,UACA,SAAA;AAAA,UACA,IAAA;AAAA,UACA;AAAA,SACD,CAAA;AAED,QAAW,UAAA,CAAA,GAAA;AAAA,UACT,MAAMG,mBAAa,CAAA;AAAA,YACjB,GAAG,OAAA;AAAA,oBACHH,QAAA;AAAA,YACA;AAAA,WACD;AAAA,SACH;AAAA;AACF,KACD,CAAA;AAAA;AAEL,CAAC;;;;"}
1
+ {"version":3,"file":"plugin.cjs.js","sources":["../../src/plugin/plugin.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport {\n CheckResult,\n Check,\n} from '@backstage-community/plugin-tech-insights-common';\nimport {\n FactCheckerFactory,\n FactRetriever,\n FactRetrieverRegistration,\n FactRetrieverRegistry,\n PersistenceContext,\n techInsightsFactCheckerFactoryExtensionPoint,\n techInsightsFactRetrieverRegistryExtensionPoint,\n techInsightsFactRetrieversExtensionPoint,\n techInsightsPersistenceContextExtensionPoint,\n} from '@backstage-community/plugin-tech-insights-node';\nimport {\n buildTechInsightsContext,\n createRouter,\n entityMetadataFactRetriever,\n entityOwnershipFactRetriever,\n techdocsFactRetriever,\n} from '../service';\nimport { createFactRetrieverRegistrationFromConfig } from './config';\n\n/**\n * The tech-insights backend plugin.\n *\n * @public\n */\nexport const techInsightsPlugin = createBackendPlugin({\n pluginId: 'tech-insights',\n register(env) {\n let factCheckerFactory: FactCheckerFactory<Check, CheckResult> | undefined =\n undefined;\n env.registerExtensionPoint(techInsightsFactCheckerFactoryExtensionPoint, {\n setFactCheckerFactory<\n CheckType extends Check,\n CheckResultType extends CheckResult,\n >(factory: FactCheckerFactory<CheckType, CheckResultType>): void {\n factCheckerFactory = factory;\n },\n });\n\n let factRetrieverRegistry: FactRetrieverRegistry | undefined = undefined;\n env.registerExtensionPoint(\n techInsightsFactRetrieverRegistryExtensionPoint,\n {\n setFactRetrieverRegistry(registry: FactRetrieverRegistry): void {\n factRetrieverRegistry = registry;\n },\n },\n );\n\n // initialized with built-in fact retrievers\n // only added as registration if there is config for them\n const addedFactRetrievers: Record<string, FactRetriever> = {\n entityMetadataFactRetriever,\n entityOwnershipFactRetriever,\n techdocsFactRetriever,\n };\n env.registerExtensionPoint(techInsightsFactRetrieversExtensionPoint, {\n addFactRetrievers(factRetrievers: Record<string, FactRetriever>): void {\n Object.entries(factRetrievers).forEach(([key, value]) => {\n addedFactRetrievers[key] = value;\n });\n },\n });\n\n let persistenceContext: PersistenceContext | undefined = undefined;\n env.registerExtensionPoint(techInsightsPersistenceContextExtensionPoint, {\n setPersistenceContext(context: PersistenceContext): void {\n persistenceContext = context;\n },\n });\n\n env.registerInit({\n deps: {\n config: coreServices.rootConfig,\n database: coreServices.database,\n discovery: coreServices.discovery,\n httpRouter: coreServices.httpRouter,\n logger: coreServices.logger,\n scheduler: coreServices.scheduler,\n auth: coreServices.auth,\n urlReader: coreServices.urlReader,\n httpAuth: coreServices.httpAuth,\n permissions: coreServices.permissions,\n },\n async init({\n config,\n database,\n discovery,\n httpRouter,\n logger,\n scheduler,\n auth,\n urlReader,\n httpAuth,\n permissions,\n }) {\n const factRetrievers: FactRetrieverRegistration[] = Object.entries(\n addedFactRetrievers,\n )\n .map(([name, factRetriever]) =>\n createFactRetrieverRegistrationFromConfig(\n config,\n name,\n factRetriever,\n ),\n )\n .filter(registration => registration) as FactRetrieverRegistration[];\n\n const context = await buildTechInsightsContext({\n config,\n database,\n discovery,\n factCheckerFactory,\n factRetrieverRegistry,\n factRetrievers,\n logger,\n persistenceContext,\n scheduler,\n auth,\n urlReader,\n });\n\n httpRouter.use(\n await createRouter({\n ...context,\n config,\n logger,\n permissions,\n httpAuth,\n }),\n );\n },\n });\n },\n});\n"],"names":["createBackendPlugin","techInsightsFactCheckerFactoryExtensionPoint","techInsightsFactRetrieverRegistryExtensionPoint","entityMetadataFactRetriever","entityOwnershipFactRetriever","techdocsFactRetriever","techInsightsFactRetrieversExtensionPoint","techInsightsPersistenceContextExtensionPoint","coreServices","config","createFactRetrieverRegistrationFromConfig","buildTechInsightsContext","createRouter"],"mappings":"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"}
@@ -2,6 +2,7 @@
2
2
 
3
3
  var catalogClient = require('@backstage/catalog-client');
4
4
  var isEmpty = require('lodash/isEmpty');
5
+ var luxon = require('luxon');
5
6
 
6
7
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
7
8
 
@@ -49,7 +50,8 @@ const entityMetadataFactRetriever = {
49
50
  hasTitle: Boolean(entity.metadata?.title),
50
51
  hasDescription: Boolean(entity.metadata?.description),
51
52
  hasTags: !isEmpty__default.default(entity.metadata?.tags)
52
- }
53
+ },
54
+ timestamp: luxon.DateTime.now()
53
55
  };
54
56
  });
55
57
  }
@@ -1 +1 @@
1
- {"version":3,"file":"entityMetadataFactRetriever.cjs.js","sources":["../../../../src/service/fact/factRetrievers/entityMetadataFactRetriever.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FactRetriever,\n FactRetrieverContext,\n} from '@backstage-community/plugin-tech-insights-node';\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { Entity } from '@backstage/catalog-model';\nimport isEmpty from 'lodash/isEmpty';\n\n/**\n * Generates facts which indicate the completeness of entity metadata.\n *\n * @public\n */\nexport const entityMetadataFactRetriever: FactRetriever = {\n id: 'entityMetadataFactRetriever',\n version: '0.0.1',\n title: 'Entity Metadata',\n description:\n 'Generates facts which indicate the completeness of entity metadata',\n schema: {\n hasTitle: {\n type: 'boolean',\n description: 'The entity has a title in metadata',\n },\n hasDescription: {\n type: 'boolean',\n description: 'The entity has a description in metadata',\n },\n hasTags: {\n type: 'boolean',\n description: 'The entity has tags in metadata',\n },\n },\n handler: async ({ discovery, entityFilter, auth }: FactRetrieverContext) => {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const catalogClient = new CatalogClient({\n discoveryApi: discovery,\n });\n const entities = await catalogClient.getEntities(\n { filter: entityFilter },\n { token },\n );\n\n return entities.items.map((entity: Entity) => {\n return {\n entity: {\n namespace: entity.metadata.namespace!,\n kind: entity.kind,\n name: entity.metadata.name,\n },\n facts: {\n hasTitle: Boolean(entity.metadata?.title),\n hasDescription: Boolean(entity.metadata?.description),\n hasTags: !isEmpty(entity.metadata?.tags),\n },\n };\n });\n },\n};\n"],"names":["catalogClient","CatalogClient","isEmpty"],"mappings":";;;;;;;;;AA6BO,MAAM,2BAA6C,GAAA;AAAA,EACxD,EAAI,EAAA,6BAAA;AAAA,EACJ,OAAS,EAAA,OAAA;AAAA,EACT,KAAO,EAAA,iBAAA;AAAA,EACP,WACE,EAAA,oEAAA;AAAA,EACF,MAAQ,EAAA;AAAA,IACN,QAAU,EAAA;AAAA,MACR,IAAM,EAAA,SAAA;AAAA,MACN,WAAa,EAAA;AAAA,KACf;AAAA,IACA,cAAgB,EAAA;AAAA,MACd,IAAM,EAAA,SAAA;AAAA,MACN,WAAa,EAAA;AAAA,KACf;AAAA,IACA,OAAS,EAAA;AAAA,MACP,IAAM,EAAA,SAAA;AAAA,MACN,WAAa,EAAA;AAAA;AACf,GACF;AAAA,EACA,SAAS,OAAO,EAAE,SAAW,EAAA,YAAA,EAAc,MAAiC,KAAA;AAC1E,IAAA,MAAM,EAAE,KAAA,EAAU,GAAA,MAAM,KAAK,qBAAsB,CAAA;AAAA,MACjD,UAAA,EAAY,MAAM,IAAA,CAAK,wBAAyB,EAAA;AAAA,MAChD,cAAgB,EAAA;AAAA,KACjB,CAAA;AACD,IAAM,MAAAA,eAAA,GAAgB,IAAIC,2BAAc,CAAA;AAAA,MACtC,YAAc,EAAA;AAAA,KACf,CAAA;AACD,IAAM,MAAA,QAAA,GAAW,MAAMD,eAAc,CAAA,WAAA;AAAA,MACnC,EAAE,QAAQ,YAAa,EAAA;AAAA,MACvB,EAAE,KAAM;AAAA,KACV;AAEA,IAAA,OAAO,QAAS,CAAA,KAAA,CAAM,GAAI,CAAA,CAAC,MAAmB,KAAA;AAC5C,MAAO,OAAA;AAAA,QACL,MAAQ,EAAA;AAAA,UACN,SAAA,EAAW,OAAO,QAAS,CAAA,SAAA;AAAA,UAC3B,MAAM,MAAO,CAAA,IAAA;AAAA,UACb,IAAA,EAAM,OAAO,QAAS,CAAA;AAAA,SACxB;AAAA,QACA,KAAO,EAAA;AAAA,UACL,QAAU,EAAA,OAAA,CAAQ,MAAO,CAAA,QAAA,EAAU,KAAK,CAAA;AAAA,UACxC,cAAgB,EAAA,OAAA,CAAQ,MAAO,CAAA,QAAA,EAAU,WAAW,CAAA;AAAA,UACpD,OAAS,EAAA,CAACE,wBAAQ,CAAA,MAAA,CAAO,UAAU,IAAI;AAAA;AACzC,OACF;AAAA,KACD,CAAA;AAAA;AAEL;;;;"}
1
+ {"version":3,"file":"entityMetadataFactRetriever.cjs.js","sources":["../../../../src/service/fact/factRetrievers/entityMetadataFactRetriever.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FactRetriever,\n FactRetrieverContext,\n} from '@backstage-community/plugin-tech-insights-node';\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { Entity } from '@backstage/catalog-model';\nimport isEmpty from 'lodash/isEmpty';\nimport { DateTime } from 'luxon';\n\n/**\n * Generates facts which indicate the completeness of entity metadata.\n *\n * @public\n */\nexport const entityMetadataFactRetriever: FactRetriever = {\n id: 'entityMetadataFactRetriever',\n version: '0.0.1',\n title: 'Entity Metadata',\n description:\n 'Generates facts which indicate the completeness of entity metadata',\n schema: {\n hasTitle: {\n type: 'boolean',\n description: 'The entity has a title in metadata',\n },\n hasDescription: {\n type: 'boolean',\n description: 'The entity has a description in metadata',\n },\n hasTags: {\n type: 'boolean',\n description: 'The entity has tags in metadata',\n },\n },\n handler: async ({ discovery, entityFilter, auth }: FactRetrieverContext) => {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const catalogClient = new CatalogClient({\n discoveryApi: discovery,\n });\n const entities = await catalogClient.getEntities(\n { filter: entityFilter },\n { token },\n );\n\n return entities.items.map((entity: Entity) => {\n return {\n entity: {\n namespace: entity.metadata.namespace!,\n kind: entity.kind,\n name: entity.metadata.name,\n },\n facts: {\n hasTitle: Boolean(entity.metadata?.title),\n hasDescription: Boolean(entity.metadata?.description),\n hasTags: !isEmpty(entity.metadata?.tags),\n },\n timestamp: DateTime.now(),\n };\n });\n },\n};\n"],"names":["catalogClient","CatalogClient","isEmpty","DateTime"],"mappings":";;;;;;;;;;AA8BO,MAAM,2BAA6C,GAAA;AAAA,EACxD,EAAI,EAAA,6BAAA;AAAA,EACJ,OAAS,EAAA,OAAA;AAAA,EACT,KAAO,EAAA,iBAAA;AAAA,EACP,WACE,EAAA,oEAAA;AAAA,EACF,MAAQ,EAAA;AAAA,IACN,QAAU,EAAA;AAAA,MACR,IAAM,EAAA,SAAA;AAAA,MACN,WAAa,EAAA;AAAA,KACf;AAAA,IACA,cAAgB,EAAA;AAAA,MACd,IAAM,EAAA,SAAA;AAAA,MACN,WAAa,EAAA;AAAA,KACf;AAAA,IACA,OAAS,EAAA;AAAA,MACP,IAAM,EAAA,SAAA;AAAA,MACN,WAAa,EAAA;AAAA;AACf,GACF;AAAA,EACA,SAAS,OAAO,EAAE,SAAW,EAAA,YAAA,EAAc,MAAiC,KAAA;AAC1E,IAAA,MAAM,EAAE,KAAA,EAAU,GAAA,MAAM,KAAK,qBAAsB,CAAA;AAAA,MACjD,UAAA,EAAY,MAAM,IAAA,CAAK,wBAAyB,EAAA;AAAA,MAChD,cAAgB,EAAA;AAAA,KACjB,CAAA;AACD,IAAM,MAAAA,eAAA,GAAgB,IAAIC,2BAAc,CAAA;AAAA,MACtC,YAAc,EAAA;AAAA,KACf,CAAA;AACD,IAAM,MAAA,QAAA,GAAW,MAAMD,eAAc,CAAA,WAAA;AAAA,MACnC,EAAE,QAAQ,YAAa,EAAA;AAAA,MACvB,EAAE,KAAM;AAAA,KACV;AAEA,IAAA,OAAO,QAAS,CAAA,KAAA,CAAM,GAAI,CAAA,CAAC,MAAmB,KAAA;AAC5C,MAAO,OAAA;AAAA,QACL,MAAQ,EAAA;AAAA,UACN,SAAA,EAAW,OAAO,QAAS,CAAA,SAAA;AAAA,UAC3B,MAAM,MAAO,CAAA,IAAA;AAAA,UACb,IAAA,EAAM,OAAO,QAAS,CAAA;AAAA,SACxB;AAAA,QACA,KAAO,EAAA;AAAA,UACL,QAAU,EAAA,OAAA,CAAQ,MAAO,CAAA,QAAA,EAAU,KAAK,CAAA;AAAA,UACxC,cAAgB,EAAA,OAAA,CAAQ,MAAO,CAAA,QAAA,EAAU,WAAW,CAAA;AAAA,UACpD,OAAS,EAAA,CAACE,wBAAQ,CAAA,MAAA,CAAO,UAAU,IAAI;AAAA,SACzC;AAAA,QACA,SAAA,EAAWC,eAAS,GAAI;AAAA,OAC1B;AAAA,KACD,CAAA;AAAA;AAEL;;;;"}
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var catalogClient = require('@backstage/catalog-client');
4
+ var luxon = require('luxon');
4
5
 
5
6
  const entityOwnershipFactRetriever = {
6
7
  id: "entityOwnershipFactRetriever",
@@ -44,7 +45,8 @@ const entityOwnershipFactRetriever = {
44
45
  hasGroupOwner: Boolean(
45
46
  entity.spec?.owner && !(entity.spec?.owner).startsWith("user:")
46
47
  )
47
- }
48
+ },
49
+ timestamp: luxon.DateTime.now()
48
50
  };
49
51
  });
50
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"entityOwnershipFactRetriever.cjs.js","sources":["../../../../src/service/fact/factRetrievers/entityOwnershipFactRetriever.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FactRetriever,\n FactRetrieverContext,\n} from '@backstage-community/plugin-tech-insights-node';\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { Entity } from '@backstage/catalog-model';\n\n/**\n * Generates facts which indicate the quality of data in the spec.owner field.\n *\n * @public\n */\nexport const entityOwnershipFactRetriever: FactRetriever = {\n id: 'entityOwnershipFactRetriever',\n version: '0.0.1',\n title: 'Entity Ownership',\n description:\n 'Generates facts which indicate the quality of data in the spec.owner field',\n entityFilter: [\n { kind: ['component', 'domain', 'system', 'api', 'resource', 'template'] },\n ],\n schema: {\n hasOwner: {\n type: 'boolean',\n description: 'The spec.owner field is set',\n },\n hasGroupOwner: {\n type: 'boolean',\n description: 'The spec.owner field is set and refers to a group',\n },\n },\n handler: async ({ discovery, entityFilter, auth }: FactRetrieverContext) => {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const catalogClient = new CatalogClient({\n discoveryApi: discovery,\n });\n const entities = await catalogClient.getEntities(\n { filter: entityFilter },\n { token },\n );\n\n return entities.items.map((entity: Entity) => {\n return {\n entity: {\n namespace: entity.metadata.namespace!,\n kind: entity.kind,\n name: entity.metadata.name,\n },\n facts: {\n hasOwner: Boolean(entity.spec?.owner),\n hasGroupOwner: Boolean(\n entity.spec?.owner &&\n !(entity.spec?.owner as string).startsWith('user:'),\n ),\n },\n };\n });\n },\n};\n"],"names":["catalogClient","CatalogClient"],"mappings":";;;;AA4BO,MAAM,4BAA8C,GAAA;AAAA,EACzD,EAAI,EAAA,8BAAA;AAAA,EACJ,OAAS,EAAA,OAAA;AAAA,EACT,KAAO,EAAA,kBAAA;AAAA,EACP,WACE,EAAA,4EAAA;AAAA,EACF,YAAc,EAAA;AAAA,IACZ,EAAE,MAAM,CAAC,WAAA,EAAa,UAAU,QAAU,EAAA,KAAA,EAAO,UAAY,EAAA,UAAU,CAAE;AAAA,GAC3E;AAAA,EACA,MAAQ,EAAA;AAAA,IACN,QAAU,EAAA;AAAA,MACR,IAAM,EAAA,SAAA;AAAA,MACN,WAAa,EAAA;AAAA,KACf;AAAA,IACA,aAAe,EAAA;AAAA,MACb,IAAM,EAAA,SAAA;AAAA,MACN,WAAa,EAAA;AAAA;AACf,GACF;AAAA,EACA,SAAS,OAAO,EAAE,SAAW,EAAA,YAAA,EAAc,MAAiC,KAAA;AAC1E,IAAA,MAAM,EAAE,KAAA,EAAU,GAAA,MAAM,KAAK,qBAAsB,CAAA;AAAA,MACjD,UAAA,EAAY,MAAM,IAAA,CAAK,wBAAyB,EAAA;AAAA,MAChD,cAAgB,EAAA;AAAA,KACjB,CAAA;AACD,IAAM,MAAAA,eAAA,GAAgB,IAAIC,2BAAc,CAAA;AAAA,MACtC,YAAc,EAAA;AAAA,KACf,CAAA;AACD,IAAM,MAAA,QAAA,GAAW,MAAMD,eAAc,CAAA,WAAA;AAAA,MACnC,EAAE,QAAQ,YAAa,EAAA;AAAA,MACvB,EAAE,KAAM;AAAA,KACV;AAEA,IAAA,OAAO,QAAS,CAAA,KAAA,CAAM,GAAI,CAAA,CAAC,MAAmB,KAAA;AAC5C,MAAO,OAAA;AAAA,QACL,MAAQ,EAAA;AAAA,UACN,SAAA,EAAW,OAAO,QAAS,CAAA,SAAA;AAAA,UAC3B,MAAM,MAAO,CAAA,IAAA;AAAA,UACb,IAAA,EAAM,OAAO,QAAS,CAAA;AAAA,SACxB;AAAA,QACA,KAAO,EAAA;AAAA,UACL,QAAU,EAAA,OAAA,CAAQ,MAAO,CAAA,IAAA,EAAM,KAAK,CAAA;AAAA,UACpC,aAAe,EAAA,OAAA;AAAA,YACb,MAAA,CAAO,MAAM,KACX,IAAA,CAAA,CAAE,OAAO,IAAM,EAAA,KAAA,EAAiB,WAAW,OAAO;AAAA;AACtD;AACF,OACF;AAAA,KACD,CAAA;AAAA;AAEL;;;;"}
1
+ {"version":3,"file":"entityOwnershipFactRetriever.cjs.js","sources":["../../../../src/service/fact/factRetrievers/entityOwnershipFactRetriever.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FactRetriever,\n FactRetrieverContext,\n} from '@backstage-community/plugin-tech-insights-node';\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { Entity } from '@backstage/catalog-model';\nimport { DateTime } from 'luxon';\n\n/**\n * Generates facts which indicate the quality of data in the spec.owner field.\n *\n * @public\n */\nexport const entityOwnershipFactRetriever: FactRetriever = {\n id: 'entityOwnershipFactRetriever',\n version: '0.0.1',\n title: 'Entity Ownership',\n description:\n 'Generates facts which indicate the quality of data in the spec.owner field',\n entityFilter: [\n { kind: ['component', 'domain', 'system', 'api', 'resource', 'template'] },\n ],\n schema: {\n hasOwner: {\n type: 'boolean',\n description: 'The spec.owner field is set',\n },\n hasGroupOwner: {\n type: 'boolean',\n description: 'The spec.owner field is set and refers to a group',\n },\n },\n handler: async ({ discovery, entityFilter, auth }: FactRetrieverContext) => {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const catalogClient = new CatalogClient({\n discoveryApi: discovery,\n });\n const entities = await catalogClient.getEntities(\n { filter: entityFilter },\n { token },\n );\n\n return entities.items.map((entity: Entity) => {\n return {\n entity: {\n namespace: entity.metadata.namespace!,\n kind: entity.kind,\n name: entity.metadata.name,\n },\n facts: {\n hasOwner: Boolean(entity.spec?.owner),\n hasGroupOwner: Boolean(\n entity.spec?.owner &&\n !(entity.spec?.owner as string).startsWith('user:'),\n ),\n },\n timestamp: DateTime.now(),\n };\n });\n },\n};\n"],"names":["catalogClient","CatalogClient","DateTime"],"mappings":";;;;;AA6BO,MAAM,4BAA8C,GAAA;AAAA,EACzD,EAAI,EAAA,8BAAA;AAAA,EACJ,OAAS,EAAA,OAAA;AAAA,EACT,KAAO,EAAA,kBAAA;AAAA,EACP,WACE,EAAA,4EAAA;AAAA,EACF,YAAc,EAAA;AAAA,IACZ,EAAE,MAAM,CAAC,WAAA,EAAa,UAAU,QAAU,EAAA,KAAA,EAAO,UAAY,EAAA,UAAU,CAAE;AAAA,GAC3E;AAAA,EACA,MAAQ,EAAA;AAAA,IACN,QAAU,EAAA;AAAA,MACR,IAAM,EAAA,SAAA;AAAA,MACN,WAAa,EAAA;AAAA,KACf;AAAA,IACA,aAAe,EAAA;AAAA,MACb,IAAM,EAAA,SAAA;AAAA,MACN,WAAa,EAAA;AAAA;AACf,GACF;AAAA,EACA,SAAS,OAAO,EAAE,SAAW,EAAA,YAAA,EAAc,MAAiC,KAAA;AAC1E,IAAA,MAAM,EAAE,KAAA,EAAU,GAAA,MAAM,KAAK,qBAAsB,CAAA;AAAA,MACjD,UAAA,EAAY,MAAM,IAAA,CAAK,wBAAyB,EAAA;AAAA,MAChD,cAAgB,EAAA;AAAA,KACjB,CAAA;AACD,IAAM,MAAAA,eAAA,GAAgB,IAAIC,2BAAc,CAAA;AAAA,MACtC,YAAc,EAAA;AAAA,KACf,CAAA;AACD,IAAM,MAAA,QAAA,GAAW,MAAMD,eAAc,CAAA,WAAA;AAAA,MACnC,EAAE,QAAQ,YAAa,EAAA;AAAA,MACvB,EAAE,KAAM;AAAA,KACV;AAEA,IAAA,OAAO,QAAS,CAAA,KAAA,CAAM,GAAI,CAAA,CAAC,MAAmB,KAAA;AAC5C,MAAO,OAAA;AAAA,QACL,MAAQ,EAAA;AAAA,UACN,SAAA,EAAW,OAAO,QAAS,CAAA,SAAA;AAAA,UAC3B,MAAM,MAAO,CAAA,IAAA;AAAA,UACb,IAAA,EAAM,OAAO,QAAS,CAAA;AAAA,SACxB;AAAA,QACA,KAAO,EAAA;AAAA,UACL,QAAU,EAAA,OAAA,CAAQ,MAAO,CAAA,IAAA,EAAM,KAAK,CAAA;AAAA,UACpC,aAAe,EAAA,OAAA;AAAA,YACb,MAAA,CAAO,MAAM,KACX,IAAA,CAAA,CAAE,OAAO,IAAM,EAAA,KAAA,EAAiB,WAAW,OAAO;AAAA;AACtD,SACF;AAAA,QACA,SAAA,EAAWE,eAAS,GAAI;AAAA,OAC1B;AAAA,KACD,CAAA;AAAA;AAEL;;;;"}
@@ -2,6 +2,7 @@
2
2
 
3
3
  var catalogClient = require('@backstage/catalog-client');
4
4
  var utils = require('./utils.cjs.js');
5
+ var luxon = require('luxon');
5
6
 
6
7
  const techdocsAnnotation = "backstage.io/techdocs-ref";
7
8
  const techdocsEntityAnnotation = "backstage.io/techdocs-entity";
@@ -52,7 +53,8 @@ const techdocsFactRetriever = {
52
53
  entity,
53
54
  techdocsEntityAnnotation
54
55
  )
55
- }
56
+ },
57
+ timestamp: luxon.DateTime.now()
56
58
  };
57
59
  });
58
60
  }
@@ -1 +1 @@
1
- {"version":3,"file":"techdocsFactRetriever.cjs.js","sources":["../../../../src/service/fact/factRetrievers/techdocsFactRetriever.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FactRetriever,\n FactRetrieverContext,\n} from '@backstage-community/plugin-tech-insights-node';\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { Entity } from '@backstage/catalog-model';\nimport { entityHasAnnotation, generateAnnotationFactName } from './utils';\n\nconst techdocsAnnotation = 'backstage.io/techdocs-ref';\nconst techdocsEntityAnnotation = 'backstage.io/techdocs-entity';\nconst techdocsAnnotationFactName =\n generateAnnotationFactName(techdocsAnnotation);\nconst techdocsEntityAnnotationFactName = generateAnnotationFactName(\n techdocsEntityAnnotation,\n);\n\n/**\n * Generates facts related to the completeness of techdocs configuration for entities.\n *\n * @public\n */\nexport const techdocsFactRetriever: FactRetriever = {\n id: 'techdocsFactRetriever',\n version: '0.1.0',\n title: 'Tech Docs',\n description:\n 'Generates facts related to the completeness of techdocs configuration for entities',\n schema: {\n [techdocsAnnotationFactName]: {\n type: 'boolean',\n description: 'The entity has a TechDocs reference annotation',\n },\n [techdocsEntityAnnotationFactName]: {\n type: 'boolean',\n description: 'The entity has a TechDocs entity annotation',\n },\n },\n handler: async ({ discovery, entityFilter, auth }: FactRetrieverContext) => {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const catalogClient = new CatalogClient({\n discoveryApi: discovery,\n });\n const entities = await catalogClient.getEntities(\n { filter: entityFilter },\n { token },\n );\n\n return entities.items.map((entity: Entity) => {\n return {\n entity: {\n namespace: entity.metadata.namespace!,\n kind: entity.kind,\n name: entity.metadata.name,\n },\n facts: {\n [techdocsAnnotationFactName]: entityHasAnnotation(\n entity,\n techdocsAnnotation,\n ),\n [techdocsEntityAnnotationFactName]: entityHasAnnotation(\n entity,\n techdocsEntityAnnotation,\n ),\n },\n };\n });\n },\n};\n"],"names":["generateAnnotationFactName","catalogClient","CatalogClient","entityHasAnnotation"],"mappings":";;;;;AAwBA,MAAM,kBAAqB,GAAA,2BAAA;AAC3B,MAAM,wBAA2B,GAAA,8BAAA;AACjC,MAAM,0BAAA,GACJA,iCAA2B,kBAAkB,CAAA;AAC/C,MAAM,gCAAmC,GAAAA,gCAAA;AAAA,EACvC;AACF,CAAA;AAOO,MAAM,qBAAuC,GAAA;AAAA,EAClD,EAAI,EAAA,uBAAA;AAAA,EACJ,OAAS,EAAA,OAAA;AAAA,EACT,KAAO,EAAA,WAAA;AAAA,EACP,WACE,EAAA,oFAAA;AAAA,EACF,MAAQ,EAAA;AAAA,IACN,CAAC,0BAA0B,GAAG;AAAA,MAC5B,IAAM,EAAA,SAAA;AAAA,MACN,WAAa,EAAA;AAAA,KACf;AAAA,IACA,CAAC,gCAAgC,GAAG;AAAA,MAClC,IAAM,EAAA,SAAA;AAAA,MACN,WAAa,EAAA;AAAA;AACf,GACF;AAAA,EACA,SAAS,OAAO,EAAE,SAAW,EAAA,YAAA,EAAc,MAAiC,KAAA;AAC1E,IAAA,MAAM,EAAE,KAAA,EAAU,GAAA,MAAM,KAAK,qBAAsB,CAAA;AAAA,MACjD,UAAA,EAAY,MAAM,IAAA,CAAK,wBAAyB,EAAA;AAAA,MAChD,cAAgB,EAAA;AAAA,KACjB,CAAA;AACD,IAAM,MAAAC,eAAA,GAAgB,IAAIC,2BAAc,CAAA;AAAA,MACtC,YAAc,EAAA;AAAA,KACf,CAAA;AACD,IAAM,MAAA,QAAA,GAAW,MAAMD,eAAc,CAAA,WAAA;AAAA,MACnC,EAAE,QAAQ,YAAa,EAAA;AAAA,MACvB,EAAE,KAAM;AAAA,KACV;AAEA,IAAA,OAAO,QAAS,CAAA,KAAA,CAAM,GAAI,CAAA,CAAC,MAAmB,KAAA;AAC5C,MAAO,OAAA;AAAA,QACL,MAAQ,EAAA;AAAA,UACN,SAAA,EAAW,OAAO,QAAS,CAAA,SAAA;AAAA,UAC3B,MAAM,MAAO,CAAA,IAAA;AAAA,UACb,IAAA,EAAM,OAAO,QAAS,CAAA;AAAA,SACxB;AAAA,QACA,KAAO,EAAA;AAAA,UACL,CAAC,0BAA0B,GAAGE,yBAAA;AAAA,YAC5B,MAAA;AAAA,YACA;AAAA,WACF;AAAA,UACA,CAAC,gCAAgC,GAAGA,yBAAA;AAAA,YAClC,MAAA;AAAA,YACA;AAAA;AACF;AACF,OACF;AAAA,KACD,CAAA;AAAA;AAEL;;;;"}
1
+ {"version":3,"file":"techdocsFactRetriever.cjs.js","sources":["../../../../src/service/fact/factRetrievers/techdocsFactRetriever.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n FactRetriever,\n FactRetrieverContext,\n} from '@backstage-community/plugin-tech-insights-node';\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { Entity } from '@backstage/catalog-model';\nimport { entityHasAnnotation, generateAnnotationFactName } from './utils';\nimport { DateTime } from 'luxon';\n\nconst techdocsAnnotation = 'backstage.io/techdocs-ref';\nconst techdocsEntityAnnotation = 'backstage.io/techdocs-entity';\nconst techdocsAnnotationFactName =\n generateAnnotationFactName(techdocsAnnotation);\nconst techdocsEntityAnnotationFactName = generateAnnotationFactName(\n techdocsEntityAnnotation,\n);\n\n/**\n * Generates facts related to the completeness of techdocs configuration for entities.\n *\n * @public\n */\nexport const techdocsFactRetriever: FactRetriever = {\n id: 'techdocsFactRetriever',\n version: '0.1.0',\n title: 'Tech Docs',\n description:\n 'Generates facts related to the completeness of techdocs configuration for entities',\n schema: {\n [techdocsAnnotationFactName]: {\n type: 'boolean',\n description: 'The entity has a TechDocs reference annotation',\n },\n [techdocsEntityAnnotationFactName]: {\n type: 'boolean',\n description: 'The entity has a TechDocs entity annotation',\n },\n },\n handler: async ({ discovery, entityFilter, auth }: FactRetrieverContext) => {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const catalogClient = new CatalogClient({\n discoveryApi: discovery,\n });\n const entities = await catalogClient.getEntities(\n { filter: entityFilter },\n { token },\n );\n\n return entities.items.map((entity: Entity) => {\n return {\n entity: {\n namespace: entity.metadata.namespace!,\n kind: entity.kind,\n name: entity.metadata.name,\n },\n facts: {\n [techdocsAnnotationFactName]: entityHasAnnotation(\n entity,\n techdocsAnnotation,\n ),\n [techdocsEntityAnnotationFactName]: entityHasAnnotation(\n entity,\n techdocsEntityAnnotation,\n ),\n },\n timestamp: DateTime.now(),\n };\n });\n },\n};\n"],"names":["generateAnnotationFactName","catalogClient","CatalogClient","entityHasAnnotation","DateTime"],"mappings":";;;;;;AAyBA,MAAM,kBAAqB,GAAA,2BAAA;AAC3B,MAAM,wBAA2B,GAAA,8BAAA;AACjC,MAAM,0BAAA,GACJA,iCAA2B,kBAAkB,CAAA;AAC/C,MAAM,gCAAmC,GAAAA,gCAAA;AAAA,EACvC;AACF,CAAA;AAOO,MAAM,qBAAuC,GAAA;AAAA,EAClD,EAAI,EAAA,uBAAA;AAAA,EACJ,OAAS,EAAA,OAAA;AAAA,EACT,KAAO,EAAA,WAAA;AAAA,EACP,WACE,EAAA,oFAAA;AAAA,EACF,MAAQ,EAAA;AAAA,IACN,CAAC,0BAA0B,GAAG;AAAA,MAC5B,IAAM,EAAA,SAAA;AAAA,MACN,WAAa,EAAA;AAAA,KACf;AAAA,IACA,CAAC,gCAAgC,GAAG;AAAA,MAClC,IAAM,EAAA,SAAA;AAAA,MACN,WAAa,EAAA;AAAA;AACf,GACF;AAAA,EACA,SAAS,OAAO,EAAE,SAAW,EAAA,YAAA,EAAc,MAAiC,KAAA;AAC1E,IAAA,MAAM,EAAE,KAAA,EAAU,GAAA,MAAM,KAAK,qBAAsB,CAAA;AAAA,MACjD,UAAA,EAAY,MAAM,IAAA,CAAK,wBAAyB,EAAA;AAAA,MAChD,cAAgB,EAAA;AAAA,KACjB,CAAA;AACD,IAAM,MAAAC,eAAA,GAAgB,IAAIC,2BAAc,CAAA;AAAA,MACtC,YAAc,EAAA;AAAA,KACf,CAAA;AACD,IAAM,MAAA,QAAA,GAAW,MAAMD,eAAc,CAAA,WAAA;AAAA,MACnC,EAAE,QAAQ,YAAa,EAAA;AAAA,MACvB,EAAE,KAAM;AAAA,KACV;AAEA,IAAA,OAAO,QAAS,CAAA,KAAA,CAAM,GAAI,CAAA,CAAC,MAAmB,KAAA;AAC5C,MAAO,OAAA;AAAA,QACL,MAAQ,EAAA;AAAA,UACN,SAAA,EAAW,OAAO,QAAS,CAAA,SAAA;AAAA,UAC3B,MAAM,MAAO,CAAA,IAAA;AAAA,UACb,IAAA,EAAM,OAAO,QAAS,CAAA;AAAA,SACxB;AAAA,QACA,KAAO,EAAA;AAAA,UACL,CAAC,0BAA0B,GAAGE,yBAAA;AAAA,YAC5B,MAAA;AAAA,YACA;AAAA,WACF;AAAA,UACA,CAAC,gCAAgC,GAAGA,yBAAA;AAAA,YAClC,MAAA;AAAA,YACA;AAAA;AACF,SACF;AAAA,QACA,SAAA,EAAWC,eAAS,GAAI;AAAA,OAC1B;AAAA,KACD,CAAA;AAAA;AAEL;;;;"}
@@ -2,10 +2,13 @@
2
2
 
3
3
  var express = require('express');
4
4
  var Router = require('express-promise-router');
5
+ var pluginTechInsightsCommon = require('@backstage-community/plugin-tech-insights-common');
5
6
  var luxon = require('luxon');
6
7
  var catalogModel = require('@backstage/catalog-model');
7
8
  var errors = require('@backstage/errors');
8
9
  var rootHttpRouter = require('@backstage/backend-defaults/rootHttpRouter');
10
+ var pluginPermissionCommon = require('@backstage/plugin-permission-common');
11
+ var pluginPermissionNode = require('@backstage/plugin-permission-node');
9
12
  var pLimit = require('p-limit');
10
13
 
11
14
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
@@ -16,16 +19,41 @@ var pLimit__default = /*#__PURE__*/_interopDefaultCompat(pLimit);
16
19
 
17
20
  async function createRouter(options) {
18
21
  const router = Router__default.default();
22
+ const permissionsIntegrationRouter = pluginPermissionNode.createPermissionIntegrationRouter({
23
+ permissions: pluginTechInsightsCommon.techInsightsPermissions
24
+ });
19
25
  router.use(express__default.default.json());
20
- const { persistenceContext, factChecker, logger, config } = options;
26
+ router.use(permissionsIntegrationRouter);
27
+ const {
28
+ persistenceContext,
29
+ factChecker,
30
+ logger,
31
+ config,
32
+ permissions,
33
+ httpAuth
34
+ } = options;
21
35
  const { techInsightsStore } = persistenceContext;
22
36
  const factory = rootHttpRouter.MiddlewareFactory.create({ logger, config });
37
+ const authorize = async (request, permission) => {
38
+ const decision = (await permissions.authorize([{ permission }], {
39
+ credentials: await httpAuth.credentials(request)
40
+ }))[0];
41
+ return decision;
42
+ };
23
43
  if (factChecker) {
24
44
  logger.info("Fact checker configured. Enabling fact checking endpoints.");
25
- router.get("/checks", async (_req, res) => {
45
+ router.get("/checks", async (req, res) => {
46
+ const decision = await authorize(req, pluginTechInsightsCommon.techInsightsCheckReadPermission);
47
+ if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
48
+ throw new errors.NotAllowedError("Unauthorized");
49
+ }
26
50
  return res.json(await factChecker.getChecks());
27
51
  });
28
52
  router.post("/checks/run/:namespace/:kind/:name", async (req, res) => {
53
+ const decision = await authorize(req, pluginTechInsightsCommon.techInsightsCheckUpdatePermission);
54
+ if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
55
+ throw new errors.NotAllowedError("Unauthorized");
56
+ }
29
57
  const { namespace, kind, name } = req.params;
30
58
  const { checks } = req.body;
31
59
  const entityTriplet = catalogModel.stringifyEntityRef({ namespace, kind, name });
@@ -34,6 +62,10 @@ async function createRouter(options) {
34
62
  });
35
63
  const checksRunConcurrency = config.getOptionalNumber("techInsights.checksRunConcurrency") || 100;
36
64
  router.post("/checks/run", async (req, res) => {
65
+ const decision = await authorize(req, pluginTechInsightsCommon.techInsightsCheckUpdatePermission);
66
+ if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
67
+ throw new errors.NotAllowedError("Unauthorized");
68
+ }
37
69
  const checks = req.body.checks;
38
70
  let entities = req.body.entities;
39
71
  if (entities.length === 0) {
@@ -69,10 +101,24 @@ async function createRouter(options) {
69
101
  );
70
102
  }
71
103
  router.get("/fact-schemas", async (req, res) => {
104
+ const decision = await authorize(
105
+ req,
106
+ pluginTechInsightsCommon.techInsightsFactRetrieverReadPermission
107
+ );
108
+ if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
109
+ throw new errors.NotAllowedError("Unauthorized");
110
+ }
72
111
  const ids = req.query.ids;
73
112
  return res.json(await techInsightsStore.getLatestSchemas(ids));
74
113
  });
75
114
  router.get("/facts/latest", async (req, res) => {
115
+ const decision = await authorize(
116
+ req,
117
+ pluginTechInsightsCommon.techInsightsFactRetrieverReadPermission
118
+ );
119
+ if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
120
+ throw new errors.NotAllowedError("Unauthorized");
121
+ }
76
122
  const { entity } = req.query;
77
123
  const { namespace, kind, name } = catalogModel.parseEntityRef(entity);
78
124
  if (!req.query.ids) {
@@ -87,6 +133,13 @@ async function createRouter(options) {
87
133
  );
88
134
  });
89
135
  router.get("/facts/range", async (req, res) => {
136
+ const decision = await authorize(
137
+ req,
138
+ pluginTechInsightsCommon.techInsightsFactRetrieverReadPermission
139
+ );
140
+ if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
141
+ throw new errors.NotAllowedError("Unauthorized");
142
+ }
90
143
  const { entity } = req.query;
91
144
  const { namespace, kind, name } = catalogModel.parseEntityRef(entity);
92
145
  if (!req.query.ids) {
@@ -1 +1 @@
1
- {"version":3,"file":"router.cjs.js","sources":["../../src/service/router.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 express from 'express';\nimport Router from 'express-promise-router';\nimport { Config } from '@backstage/config';\nimport {\n FactChecker,\n PersistenceContext,\n} from '@backstage-community/plugin-tech-insights-node';\n\nimport {\n CheckResult,\n Check,\n} from '@backstage-community/plugin-tech-insights-common';\nimport { DateTime } from 'luxon';\nimport {\n CompoundEntityRef,\n parseEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { serializeError } from '@backstage/errors';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport pLimit from 'p-limit';\n\n/**\n * @public\n *\n * RouterOptions to construct TechInsights endpoints\n * @typeParam CheckType - Type of the check for the fact checker this builder returns\n * @typeParam CheckResultType - Type of the check result for the fact checker this builder returns\n */\nexport interface RouterOptions<\n CheckType extends Check,\n CheckResultType extends CheckResult,\n> {\n /**\n * Optional FactChecker implementation. If omitted, endpoints are not constructed\n */\n factChecker?: FactChecker<CheckType, CheckResultType>;\n\n /**\n * TechInsights PersistenceContext. Should contain an implementation of TechInsightsStore\n */\n persistenceContext: PersistenceContext;\n\n /**\n * Backstage config object\n */\n config: Config;\n\n /**\n * Implementation of Winston logger\n */\n logger: LoggerService;\n}\n\n/**\n * @public\n *\n * Constructs a tech-insights router.\n *\n * Exposes endpoints to handle facts\n * Exposes optional endpoints to handle checks if a FactChecker implementation is passed in\n *\n * @param options - RouterOptions object\n */\nexport async function createRouter<\n CheckType extends Check,\n CheckResultType extends CheckResult,\n>(options: RouterOptions<CheckType, CheckResultType>): Promise<express.Router> {\n const router = Router();\n router.use(express.json());\n const { persistenceContext, factChecker, logger, config } = options;\n const { techInsightsStore } = persistenceContext;\n\n const factory = MiddlewareFactory.create({ logger, config });\n\n if (factChecker) {\n logger.info('Fact checker configured. Enabling fact checking endpoints.');\n router.get('/checks', async (_req, res) => {\n return res.json(await factChecker.getChecks());\n });\n\n router.post('/checks/run/:namespace/:kind/:name', async (req, res) => {\n const { namespace, kind, name } = req.params;\n const { checks }: { checks: string[] } = req.body;\n const entityTriplet = stringifyEntityRef({ namespace, kind, name });\n const checkResult = await factChecker.runChecks(entityTriplet, checks);\n return res.json(checkResult);\n });\n\n const checksRunConcurrency =\n config.getOptionalNumber('techInsights.checksRunConcurrency') || 100;\n router.post('/checks/run', async (req, res) => {\n const checks: string[] = req.body.checks;\n let entities: CompoundEntityRef[] = req.body.entities;\n if (entities.length === 0) {\n entities = await techInsightsStore.getEntities();\n }\n const limit = pLimit(checksRunConcurrency);\n const tasks = entities.map(async entity =>\n limit(async () => {\n const entityTriplet =\n typeof entity === 'string' ? entity : stringifyEntityRef(entity);\n try {\n const results = await factChecker.runChecks(entityTriplet, checks);\n return {\n entity: entityTriplet,\n results,\n };\n } catch (e: any) {\n const error = serializeError(e);\n logger.error(`${error.name}: ${error.message}`);\n return {\n entity: entityTriplet,\n error: error,\n results: [],\n };\n }\n }),\n );\n const results = await Promise.all(tasks);\n return res.json(results);\n });\n } else {\n logger.info(\n 'Starting tech insights module without fact checking endpoints.',\n );\n }\n\n router.get('/fact-schemas', async (req, res) => {\n const ids = req.query.ids as string[];\n return res.json(await techInsightsStore.getLatestSchemas(ids));\n });\n\n /**\n * /facts/latest?entity=component:default/mycomponent&ids[]=factRetrieverId1&ids[]=factRetrieverId2\n */\n router.get('/facts/latest', async (req, res) => {\n const { entity } = req.query;\n const { namespace, kind, name } = parseEntityRef(entity as string);\n\n if (!req.query.ids) {\n return res\n .status(422)\n .json({ error: 'Failed to parse ids from request' });\n }\n const ids = [req.query.ids].flat() as string[];\n return res.json(\n await techInsightsStore.getLatestFactsByIds(\n ids,\n stringifyEntityRef({ namespace, kind, name }),\n ),\n );\n });\n\n /**\n * /facts/range?entity=component:default/mycomponent&startDateTime=2021-12-24T01:23:45&endDateTime=2021-12-31T23:59:59&ids[]=factRetrieverId1&ids[]=factRetrieverId2\n */\n router.get('/facts/range', async (req, res) => {\n const { entity } = req.query;\n const { namespace, kind, name } = parseEntityRef(entity as string);\n\n if (!req.query.ids) {\n return res\n .status(422)\n .json({ error: 'Failed to parse ids from request' });\n }\n const ids = [req.query.ids].flat() as string[];\n const startDatetime = DateTime.fromISO(req.query.startDatetime as string);\n const endDatetime = DateTime.fromISO(req.query.endDatetime as string);\n if (!startDatetime.isValid || !endDatetime.isValid) {\n return res.status(422).json({\n message: 'Failed to parse datetime from request',\n field: !startDatetime.isValid ? 'startDateTime' : 'endDateTime',\n value: !startDatetime.isValid ? startDatetime : endDatetime,\n });\n }\n const entityTriplet = stringifyEntityRef({ namespace, kind, name });\n return res.json(\n await techInsightsStore.getFactsBetweenTimestampsByIds(\n ids,\n entityTriplet,\n startDatetime,\n endDatetime,\n ),\n );\n });\n\n router.use(factory.error());\n return router;\n}\n"],"names":["Router","express","MiddlewareFactory","stringifyEntityRef","pLimit","results","serializeError","parseEntityRef","DateTime"],"mappings":"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"}
1
+ {"version":3,"file":"router.cjs.js","sources":["../../src/service/router.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 express, { Request } from 'express';\nimport Router from 'express-promise-router';\nimport { Config } from '@backstage/config';\nimport {\n FactChecker,\n PersistenceContext,\n} from '@backstage-community/plugin-tech-insights-node';\n\nimport {\n CheckResult,\n Check,\n techInsightsCheckReadPermission,\n techInsightsCheckUpdatePermission,\n techInsightsPermissions,\n techInsightsFactRetrieverReadPermission,\n} from '@backstage-community/plugin-tech-insights-common';\nimport { DateTime } from 'luxon';\nimport {\n CompoundEntityRef,\n parseEntityRef,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { serializeError } from '@backstage/errors';\nimport {\n LoggerService,\n PermissionsService,\n HttpAuthService,\n} from '@backstage/backend-plugin-api';\nimport { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport {\n AuthorizeResult,\n BasicPermission,\n} from '@backstage/plugin-permission-common';\nimport { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';\nimport { NotAllowedError } from '@backstage/errors';\nimport pLimit from 'p-limit';\n\n/**\n * @public\n *\n * RouterOptions to construct TechInsights endpoints\n * @typeParam CheckType - Type of the check for the fact checker this builder returns\n * @typeParam CheckResultType - Type of the check result for the fact checker this builder returns\n */\nexport interface RouterOptions<\n CheckType extends Check,\n CheckResultType extends CheckResult,\n> {\n /**\n * Optional FactChecker implementation. If omitted, endpoints are not constructed\n */\n factChecker?: FactChecker<CheckType, CheckResultType>;\n\n /**\n * TechInsights PersistenceContext. Should contain an implementation of TechInsightsStore\n */\n persistenceContext: PersistenceContext;\n\n /**\n * Backstage config object\n */\n config: Config;\n\n /**\n * Implementation of Winston logger\n */\n logger: LoggerService;\n\n /**\n * Implementation of PermissionsService\n */\n permissions: PermissionsService;\n\n /**\n * Implementation of HttpAuthService\n */\n httpAuth: HttpAuthService;\n}\n\n/**\n * @public\n *\n * Constructs a tech-insights router.\n *\n * Exposes endpoints to handle facts\n * Exposes optional endpoints to handle checks if a FactChecker implementation is passed in\n *\n * @param options - RouterOptions object\n */\nexport async function createRouter<\n CheckType extends Check,\n CheckResultType extends CheckResult,\n>(options: RouterOptions<CheckType, CheckResultType>): Promise<express.Router> {\n const router = Router();\n\n const permissionsIntegrationRouter = createPermissionIntegrationRouter({\n permissions: techInsightsPermissions,\n });\n\n router.use(express.json());\n router.use(permissionsIntegrationRouter);\n\n const {\n persistenceContext,\n factChecker,\n logger,\n config,\n permissions,\n httpAuth,\n } = options;\n const { techInsightsStore } = persistenceContext;\n\n const factory = MiddlewareFactory.create({ logger, config });\n\n const authorize = async (request: Request, permission: BasicPermission) => {\n const decision = (\n await permissions.authorize([{ permission: permission }], {\n credentials: await httpAuth.credentials(request),\n })\n )[0];\n\n return decision;\n };\n\n if (factChecker) {\n logger.info('Fact checker configured. Enabling fact checking endpoints.');\n router.get('/checks', async (req, res) => {\n const decision = await authorize(req, techInsightsCheckReadPermission);\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError('Unauthorized');\n }\n return res.json(await factChecker.getChecks());\n });\n\n router.post('/checks/run/:namespace/:kind/:name', async (req, res) => {\n const decision = await authorize(req, techInsightsCheckUpdatePermission);\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError('Unauthorized');\n }\n\n const { namespace, kind, name } = req.params;\n const { checks }: { checks: string[] } = req.body;\n const entityTriplet = stringifyEntityRef({ namespace, kind, name });\n const checkResult = await factChecker.runChecks(entityTriplet, checks);\n return res.json(checkResult);\n });\n\n const checksRunConcurrency =\n config.getOptionalNumber('techInsights.checksRunConcurrency') || 100;\n router.post('/checks/run', async (req, res) => {\n const decision = await authorize(req, techInsightsCheckUpdatePermission);\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError('Unauthorized');\n }\n const checks: string[] = req.body.checks;\n let entities: CompoundEntityRef[] = req.body.entities;\n if (entities.length === 0) {\n entities = await techInsightsStore.getEntities();\n }\n const limit = pLimit(checksRunConcurrency);\n const tasks = entities.map(async entity =>\n limit(async () => {\n const entityTriplet =\n typeof entity === 'string' ? entity : stringifyEntityRef(entity);\n try {\n const results = await factChecker.runChecks(entityTriplet, checks);\n return {\n entity: entityTriplet,\n results,\n };\n } catch (e: any) {\n const error = serializeError(e);\n logger.error(`${error.name}: ${error.message}`);\n return {\n entity: entityTriplet,\n error: error,\n results: [],\n };\n }\n }),\n );\n const results = await Promise.all(tasks);\n return res.json(results);\n });\n } else {\n logger.info(\n 'Starting tech insights module without fact checking endpoints.',\n );\n }\n\n router.get('/fact-schemas', async (req, res) => {\n const decision = await authorize(\n req,\n techInsightsFactRetrieverReadPermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError('Unauthorized');\n }\n\n const ids = req.query.ids as string[];\n return res.json(await techInsightsStore.getLatestSchemas(ids));\n });\n\n /**\n * /facts/latest?entity=component:default/mycomponent&ids[]=factRetrieverId1&ids[]=factRetrieverId2\n */\n router.get('/facts/latest', async (req, res) => {\n const decision = await authorize(\n req,\n techInsightsFactRetrieverReadPermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError('Unauthorized');\n }\n const { entity } = req.query;\n const { namespace, kind, name } = parseEntityRef(entity as string);\n\n if (!req.query.ids) {\n return res\n .status(422)\n .json({ error: 'Failed to parse ids from request' });\n }\n const ids = [req.query.ids].flat() as string[];\n return res.json(\n await techInsightsStore.getLatestFactsByIds(\n ids,\n stringifyEntityRef({ namespace, kind, name }),\n ),\n );\n });\n\n /**\n * /facts/range?entity=component:default/mycomponent&startDateTime=2021-12-24T01:23:45&endDateTime=2021-12-31T23:59:59&ids[]=factRetrieverId1&ids[]=factRetrieverId2\n */\n router.get('/facts/range', async (req, res) => {\n const decision = await authorize(\n req,\n techInsightsFactRetrieverReadPermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError('Unauthorized');\n }\n\n const { entity } = req.query;\n const { namespace, kind, name } = parseEntityRef(entity as string);\n\n if (!req.query.ids) {\n return res\n .status(422)\n .json({ error: 'Failed to parse ids from request' });\n }\n const ids = [req.query.ids].flat() as string[];\n const startDatetime = DateTime.fromISO(req.query.startDatetime as string);\n const endDatetime = DateTime.fromISO(req.query.endDatetime as string);\n if (!startDatetime.isValid || !endDatetime.isValid) {\n return res.status(422).json({\n message: 'Failed to parse datetime from request',\n field: !startDatetime.isValid ? 'startDateTime' : 'endDateTime',\n value: !startDatetime.isValid ? startDatetime : endDatetime,\n });\n }\n const entityTriplet = stringifyEntityRef({ namespace, kind, name });\n return res.json(\n await techInsightsStore.getFactsBetweenTimestampsByIds(\n ids,\n entityTriplet,\n startDatetime,\n endDatetime,\n ),\n );\n });\n\n router.use(factory.error());\n return router;\n}\n"],"names":["Router","createPermissionIntegrationRouter","techInsightsPermissions","express","MiddlewareFactory","techInsightsCheckReadPermission","AuthorizeResult","NotAllowedError","techInsightsCheckUpdatePermission","stringifyEntityRef","pLimit","results","serializeError","techInsightsFactRetrieverReadPermission","parseEntityRef","DateTime"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage-community/plugin-tech-insights-backend",
3
- "version": "2.2.1",
3
+ "version": "3.0.0",
4
4
  "backstage": {
5
5
  "role": "backend-plugin",
6
6
  "pluginId": "tech-insights",
@@ -57,6 +57,8 @@
57
57
  "@backstage/catalog-model": "^1.7.3",
58
58
  "@backstage/config": "^1.3.2",
59
59
  "@backstage/errors": "^1.2.7",
60
+ "@backstage/plugin-permission-common": "^0.8.4",
61
+ "@backstage/plugin-permission-node": "^0.8.7",
60
62
  "@backstage/types": "^1.2.1",
61
63
  "@types/express": "^4.17.6",
62
64
  "@types/luxon": "^3.0.0",