@backstage/plugin-catalog-backend-module-azure 0.2.3-next.0 → 0.2.3-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @backstage/plugin-catalog-backend-module-azure
2
2
 
3
+ ## 0.2.3-next.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @backstage/plugin-catalog-node@1.13.1-next.1
9
+ - @backstage/integration@1.15.1-next.1
10
+ - @backstage/backend-plugin-api@1.0.1-next.1
11
+ - @backstage/config@1.2.0
12
+ - @backstage/plugin-catalog-common@1.1.0
13
+
14
+ ## 0.2.3-next.1
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies
19
+ - @backstage/integration@1.15.1-next.0
20
+ - @backstage/backend-plugin-api@1.0.1-next.0
21
+ - @backstage/config@1.2.0
22
+ - @backstage/plugin-catalog-common@1.1.0
23
+ - @backstage/plugin-catalog-node@1.13.1-next.0
24
+
3
25
  ## 0.2.3-next.0
4
26
 
5
27
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-backend-module-azure__alpha",
3
- "version": "0.2.3-next.0",
3
+ "version": "0.2.3-next.2",
4
4
  "main": "../dist/alpha.cjs.js",
5
5
  "types": "../dist/alpha.d.ts"
6
6
  }
package/dist/alpha.cjs.js CHANGED
@@ -2,36 +2,9 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var backendPluginApi = require('@backstage/backend-plugin-api');
6
- var alpha = require('@backstage/plugin-catalog-node/alpha');
7
- var AzureDevOpsEntityProvider = require('./cjs/AzureDevOpsEntityProvider-BnGCzUOK.cjs.js');
8
- require('@backstage/integration');
9
- require('@backstage/plugin-catalog-node');
10
- require('uuid');
11
- require('node-fetch');
5
+ var catalogModuleAzureDevOpsEntityProvider = require('./module/catalogModuleAzureDevOpsEntityProvider.cjs.js');
12
6
 
13
- const catalogModuleAzureDevOpsEntityProvider = backendPluginApi.createBackendModule({
14
- pluginId: "catalog",
15
- moduleId: "azure-dev-ops-entity-provider",
16
- register(env) {
17
- env.registerInit({
18
- deps: {
19
- config: backendPluginApi.coreServices.rootConfig,
20
- catalog: alpha.catalogProcessingExtensionPoint,
21
- logger: backendPluginApi.coreServices.logger,
22
- scheduler: backendPluginApi.coreServices.scheduler
23
- },
24
- async init({ config, catalog, logger, scheduler }) {
25
- catalog.addEntityProvider(
26
- AzureDevOpsEntityProvider.AzureDevOpsEntityProvider.fromConfig(config, {
27
- logger,
28
- scheduler
29
- })
30
- );
31
- }
32
- });
33
- }
34
- });
35
7
 
36
- exports.default = catalogModuleAzureDevOpsEntityProvider;
8
+
9
+ exports.default = catalogModuleAzureDevOpsEntityProvider.catalogModuleAzureDevOpsEntityProvider;
37
10
  //# sourceMappingURL=alpha.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"alpha.cjs.js","sources":["../src/module/catalogModuleAzureDevOpsEntityProvider.ts"],"sourcesContent":["/*\n * Copyright 2022 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 createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';\nimport { AzureDevOpsEntityProvider } from '../providers';\n\n/**\n * Registers the AzureDevOpsEntityProvider with the catalog processing extension point.\n *\n * @alpha\n */\nexport const catalogModuleAzureDevOpsEntityProvider = createBackendModule({\n pluginId: 'catalog',\n moduleId: 'azure-dev-ops-entity-provider',\n register(env) {\n env.registerInit({\n deps: {\n config: coreServices.rootConfig,\n catalog: catalogProcessingExtensionPoint,\n logger: coreServices.logger,\n scheduler: coreServices.scheduler,\n },\n async init({ config, catalog, logger, scheduler }) {\n catalog.addEntityProvider(\n AzureDevOpsEntityProvider.fromConfig(config, {\n logger,\n scheduler,\n }),\n );\n },\n });\n },\n});\n"],"names":["createBackendModule","coreServices","catalogProcessingExtensionPoint","AzureDevOpsEntityProvider"],"mappings":";;;;;;;;;;;;AA4BO,MAAM,yCAAyCA,oCAAoB,CAAA;AAAA,EACxE,QAAU,EAAA,SAAA;AAAA,EACV,QAAU,EAAA,+BAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,QAAQC,6BAAa,CAAA,UAAA;AAAA,QACrB,OAAS,EAAAC,qCAAA;AAAA,QACT,QAAQD,6BAAa,CAAA,MAAA;AAAA,QACrB,WAAWA,6BAAa,CAAA,SAAA;AAAA,OAC1B;AAAA,MACA,MAAM,IAAK,CAAA,EAAE,QAAQ,OAAS,EAAA,MAAA,EAAQ,WAAa,EAAA;AACjD,QAAQ,OAAA,CAAA,iBAAA;AAAA,UACNE,mDAAA,CAA0B,WAAW,MAAQ,EAAA;AAAA,YAC3C,MAAA;AAAA,YACA,SAAA;AAAA,WACD,CAAA;AAAA,SACH,CAAA;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"alpha.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
package/dist/index.cjs.js CHANGED
@@ -1,110 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var integration = require('@backstage/integration');
4
- var pluginCatalogNode = require('@backstage/plugin-catalog-node');
5
- var AzureDevOpsEntityProvider = require('./cjs/AzureDevOpsEntityProvider-BnGCzUOK.cjs.js');
6
- require('@backstage/backend-plugin-api');
7
- require('uuid');
8
- require('node-fetch');
3
+ var AzureDevOpsDiscoveryProcessor = require('./processors/AzureDevOpsDiscoveryProcessor.cjs.js');
4
+ var AzureDevOpsEntityProvider = require('./providers/AzureDevOpsEntityProvider.cjs.js');
9
5
 
10
- class AzureDevOpsDiscoveryProcessor {
11
- integrations;
12
- credentialsProvider;
13
- logger;
14
- static fromConfig(config, options) {
15
- const integrations = integration.ScmIntegrations.fromConfig(config);
16
- return new AzureDevOpsDiscoveryProcessor({
17
- ...options,
18
- integrations
19
- });
20
- }
21
- constructor(options) {
22
- this.integrations = options.integrations;
23
- this.logger = options.logger;
24
- this.credentialsProvider = integration.DefaultAzureDevOpsCredentialsProvider.fromIntegrations(
25
- options.integrations
26
- );
27
- }
28
- getProcessorName() {
29
- return "AzureDevOpsDiscoveryProcessor";
30
- }
31
- async readLocation(location, _optional, emit) {
32
- if (location.type !== "azure-discovery") {
33
- return false;
34
- }
35
- const azureConfig = this.integrations.azure.byUrl(location.target)?.config;
36
- if (!azureConfig) {
37
- throw new Error(
38
- `There is no Azure integration that matches ${location.target}. Please add a configuration entry for it under integrations.azure`
39
- );
40
- }
41
- const { baseUrl, org, project, repo, catalogPath, branch } = parseUrl(
42
- location.target
43
- );
44
- this.logger.info(
45
- `Reading Azure DevOps repositories from ${location.target}`
46
- );
47
- const files = await AzureDevOpsEntityProvider.codeSearch(
48
- this.credentialsProvider,
49
- azureConfig,
50
- org,
51
- project,
52
- repo,
53
- catalogPath,
54
- branch
55
- );
56
- this.logger.debug(
57
- `Found ${files.length} files in Azure DevOps from ${location.target}.`
58
- );
59
- for (const file of files) {
60
- let target = `${baseUrl}/${org}/${project}/_git/${file.repository.name}?path=${file.path}`;
61
- if (branch) {
62
- target += `&version=GB${branch}`;
63
- }
64
- emit(
65
- pluginCatalogNode.processingResult.location({
66
- type: "url",
67
- target,
68
- // Not all locations may actually exist, since the user defined them as a wildcard pattern.
69
- // Thus, we emit them as optional and let the downstream processor find them while not outputting
70
- // an error if it couldn't.
71
- presence: "optional"
72
- })
73
- );
74
- }
75
- return true;
76
- }
77
- }
78
- function parseUrl(urlString) {
79
- const url = new URL(urlString);
80
- const path = url.pathname.slice(1).split("/");
81
- const catalogPath = url.searchParams.get("path") || "/catalog-info.yaml";
82
- let branch = url.searchParams.get("version") || "";
83
- if (branch.startsWith("GB")) {
84
- branch = branch.slice(2);
85
- }
86
- if (path.length === 2 && path[0].length && path[1].length) {
87
- return {
88
- baseUrl: url.origin,
89
- org: decodeURIComponent(path[0]),
90
- project: decodeURIComponent(path[1]),
91
- repo: "",
92
- catalogPath,
93
- branch
94
- };
95
- } else if (path.length === 4 && path[0].length && path[1].length && path[2].length && path[3].length) {
96
- return {
97
- baseUrl: url.origin,
98
- org: decodeURIComponent(path[0]),
99
- project: decodeURIComponent(path[1]),
100
- repo: decodeURIComponent(path[3]),
101
- catalogPath,
102
- branch
103
- };
104
- }
105
- throw new Error(`Failed to parse ${urlString}`);
106
- }
107
6
 
7
+
8
+ exports.AzureDevOpsDiscoveryProcessor = AzureDevOpsDiscoveryProcessor.AzureDevOpsDiscoveryProcessor;
108
9
  exports.AzureDevOpsEntityProvider = AzureDevOpsEntityProvider.AzureDevOpsEntityProvider;
109
- exports.AzureDevOpsDiscoveryProcessor = AzureDevOpsDiscoveryProcessor;
110
10
  //# sourceMappingURL=index.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/processors/AzureDevOpsDiscoveryProcessor.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 { Config } from '@backstage/config';\nimport {\n AzureDevOpsCredentialsProvider,\n DefaultAzureDevOpsCredentialsProvider,\n ScmIntegrationRegistry,\n ScmIntegrations,\n} from '@backstage/integration';\nimport {\n CatalogProcessor,\n CatalogProcessorEmit,\n processingResult,\n} from '@backstage/plugin-catalog-node';\nimport { LocationSpec } from '@backstage/plugin-catalog-common';\nimport { codeSearch } from '../lib';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\n/**\n * Extracts repositories out of an Azure DevOps org.\n *\n * The following will create locations for all projects which have a catalog-info.yaml\n * on the default branch. The first is shorthand for the second.\n *\n * target: \"https://dev.azure.com/org/project\"\n * or\n * target: https://dev.azure.com/org/project?path=/catalog-info.yaml\n *\n * You may also explicitly specify a single repo:\n *\n * target: https://dev.azure.com/org/project/_git/repo\n *\n * @public\n */\nexport class AzureDevOpsDiscoveryProcessor implements CatalogProcessor {\n private readonly integrations: ScmIntegrationRegistry;\n private readonly credentialsProvider: AzureDevOpsCredentialsProvider;\n private readonly logger: LoggerService;\n\n static fromConfig(config: Config, options: { logger: LoggerService }) {\n const integrations = ScmIntegrations.fromConfig(config);\n\n return new AzureDevOpsDiscoveryProcessor({\n ...options,\n integrations,\n });\n }\n\n constructor(options: {\n integrations: ScmIntegrationRegistry;\n logger: LoggerService;\n }) {\n this.integrations = options.integrations;\n this.logger = options.logger;\n this.credentialsProvider =\n DefaultAzureDevOpsCredentialsProvider.fromIntegrations(\n options.integrations,\n );\n }\n\n getProcessorName(): string {\n return 'AzureDevOpsDiscoveryProcessor';\n }\n\n async readLocation(\n location: LocationSpec,\n _optional: boolean,\n emit: CatalogProcessorEmit,\n ): Promise<boolean> {\n if (location.type !== 'azure-discovery') {\n return false;\n }\n\n const azureConfig = this.integrations.azure.byUrl(location.target)?.config;\n if (!azureConfig) {\n throw new Error(\n `There is no Azure integration that matches ${location.target}. Please add a configuration entry for it under integrations.azure`,\n );\n }\n\n const { baseUrl, org, project, repo, catalogPath, branch } = parseUrl(\n location.target,\n );\n this.logger.info(\n `Reading Azure DevOps repositories from ${location.target}`,\n );\n\n const files = await codeSearch(\n this.credentialsProvider,\n azureConfig,\n org,\n project,\n repo,\n catalogPath,\n branch,\n );\n\n this.logger.debug(\n `Found ${files.length} files in Azure DevOps from ${location.target}.`,\n );\n\n for (const file of files) {\n let target = `${baseUrl}/${org}/${project}/_git/${file.repository.name}?path=${file.path}`;\n\n if (branch) {\n target += `&version=GB${branch}`;\n }\n\n emit(\n processingResult.location({\n type: 'url',\n target,\n // Not all locations may actually exist, since the user defined them as a wildcard pattern.\n // Thus, we emit them as optional and let the downstream processor find them while not outputting\n // an error if it couldn't.\n presence: 'optional',\n }),\n );\n }\n\n return true;\n }\n}\n\n/**\n * parseUrl extracts segments from the Azure DevOps URL.\n */\nexport function parseUrl(urlString: string): {\n baseUrl: string;\n org: string;\n project: string;\n repo: string;\n catalogPath: string;\n branch: string;\n} {\n const url = new URL(urlString);\n const path = url.pathname.slice(1).split('/');\n\n const catalogPath = url.searchParams.get('path') || '/catalog-info.yaml';\n let branch = url.searchParams.get('version') || '';\n\n if (branch.startsWith('GB')) {\n // DevOps prefixes branch names with 'GB' in URLs\n branch = branch.slice(2);\n }\n\n if (path.length === 2 && path[0].length && path[1].length) {\n return {\n baseUrl: url.origin,\n org: decodeURIComponent(path[0]),\n project: decodeURIComponent(path[1]),\n repo: '',\n catalogPath,\n branch,\n };\n } else if (\n path.length === 4 &&\n path[0].length &&\n path[1].length &&\n path[2].length &&\n path[3].length\n ) {\n return {\n baseUrl: url.origin,\n org: decodeURIComponent(path[0]),\n project: decodeURIComponent(path[1]),\n repo: decodeURIComponent(path[3]),\n catalogPath,\n branch,\n };\n }\n\n throw new Error(`Failed to parse ${urlString}`);\n}\n"],"names":["ScmIntegrations","DefaultAzureDevOpsCredentialsProvider","codeSearch","processingResult"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
@@ -0,0 +1,55 @@
1
+ 'use strict';
2
+
3
+ var fetch = require('node-fetch');
4
+
5
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
6
+
7
+ var fetch__default = /*#__PURE__*/_interopDefaultCompat(fetch);
8
+
9
+ const isCloud = (host) => host === "dev.azure.com";
10
+ const PAGE_SIZE = 1e3;
11
+ async function codeSearch(credentialsProvider, azureConfig, org, project, repo, path, branch) {
12
+ const searchBaseUrl = isCloud(azureConfig.host) ? "https://almsearch.dev.azure.com" : `https://${azureConfig.host}`;
13
+ const searchUrl = `${searchBaseUrl}/${org}/_apis/search/codesearchresults?api-version=6.0-preview.1`;
14
+ let items = [];
15
+ let hasMorePages = true;
16
+ do {
17
+ const credentials = await credentialsProvider.getCredentials({
18
+ url: `https://${azureConfig.host}/${org}`
19
+ });
20
+ const searchRequestBody = {
21
+ searchText: `path:${path} repo:${repo || "*"} proj:${project || "*"}`,
22
+ $orderBy: [
23
+ {
24
+ field: "path",
25
+ sortOrder: "ASC"
26
+ }
27
+ ],
28
+ $skip: items.length,
29
+ $top: PAGE_SIZE
30
+ };
31
+ if (branch) {
32
+ searchRequestBody.filters = { Branch: [branch] };
33
+ }
34
+ const response = await fetch__default.default(searchUrl, {
35
+ headers: {
36
+ ...credentials?.headers,
37
+ "Content-Type": "application/json"
38
+ },
39
+ method: "POST",
40
+ body: JSON.stringify(searchRequestBody)
41
+ });
42
+ if (response.status !== 200) {
43
+ throw new Error(
44
+ `Azure DevOps search failed with response status ${response.status}`
45
+ );
46
+ }
47
+ const body = await response.json();
48
+ items = [...items, ...body.results];
49
+ hasMorePages = body.count > items.length;
50
+ } while (hasMorePages);
51
+ return items;
52
+ }
53
+
54
+ exports.codeSearch = codeSearch;
55
+ //# sourceMappingURL=azure.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"azure.cjs.js","sources":["../../src/lib/azure.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 fetch from 'node-fetch';\nimport {\n AzureDevOpsCredentialsProvider,\n AzureIntegrationConfig,\n} from '@backstage/integration';\n\nexport interface CodeSearchResponse {\n count: number;\n results: CodeSearchResultItem[];\n}\n\nexport interface CodeSearchResultItem {\n fileName: string;\n path: string;\n repository: {\n name: string;\n };\n project: {\n name: string;\n };\n branch?: string;\n}\n\ninterface CodeSearchRequest {\n searchText: string;\n $orderBy: Array<{ field: string; sortOrder: string }>;\n $skip: number;\n $top: number;\n filters?: {\n Branch: string[];\n };\n}\n\nconst isCloud = (host: string) => host === 'dev.azure.com';\nconst PAGE_SIZE = 1000;\n\n// codeSearch returns all files that matches the given search path.\nexport async function codeSearch(\n credentialsProvider: AzureDevOpsCredentialsProvider,\n azureConfig: AzureIntegrationConfig,\n org: string,\n project: string,\n repo: string,\n path: string,\n branch: string,\n): Promise<CodeSearchResultItem[]> {\n const searchBaseUrl = isCloud(azureConfig.host)\n ? 'https://almsearch.dev.azure.com'\n : `https://${azureConfig.host}`;\n const searchUrl = `${searchBaseUrl}/${org}/_apis/search/codesearchresults?api-version=6.0-preview.1`;\n\n let items: CodeSearchResultItem[] = [];\n let hasMorePages = true;\n\n do {\n const credentials = await credentialsProvider.getCredentials({\n url: `https://${azureConfig.host}/${org}`,\n });\n\n const searchRequestBody: CodeSearchRequest = {\n searchText: `path:${path} repo:${repo || '*'} proj:${project || '*'}`,\n $orderBy: [\n {\n field: 'path',\n sortOrder: 'ASC',\n },\n ],\n $skip: items.length,\n $top: PAGE_SIZE,\n };\n\n if (branch) {\n searchRequestBody.filters = { Branch: [branch] };\n }\n\n const response = await fetch(searchUrl, {\n headers: {\n ...credentials?.headers,\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n body: JSON.stringify(searchRequestBody),\n });\n\n if (response.status !== 200) {\n throw new Error(\n `Azure DevOps search failed with response status ${response.status}`,\n );\n }\n\n const body: CodeSearchResponse = await response.json();\n items = [...items, ...body.results];\n hasMorePages = body.count > items.length;\n } while (hasMorePages);\n\n return items;\n}\n"],"names":["fetch"],"mappings":";;;;;;;;AAiDA,MAAM,OAAA,GAAU,CAAC,IAAA,KAAiB,IAAS,KAAA,eAAA,CAAA;AAC3C,MAAM,SAAY,GAAA,GAAA,CAAA;AAGlB,eAAsB,WACpB,mBACA,EAAA,WAAA,EACA,KACA,OACA,EAAA,IAAA,EACA,MACA,MACiC,EAAA;AACjC,EAAM,MAAA,aAAA,GAAgB,QAAQ,WAAY,CAAA,IAAI,IAC1C,iCACA,GAAA,CAAA,QAAA,EAAW,YAAY,IAAI,CAAA,CAAA,CAAA;AAC/B,EAAA,MAAM,SAAY,GAAA,CAAA,EAAG,aAAa,CAAA,CAAA,EAAI,GAAG,CAAA,yDAAA,CAAA,CAAA;AAEzC,EAAA,IAAI,QAAgC,EAAC,CAAA;AACrC,EAAA,IAAI,YAAe,GAAA,IAAA,CAAA;AAEnB,EAAG,GAAA;AACD,IAAM,MAAA,WAAA,GAAc,MAAM,mBAAA,CAAoB,cAAe,CAAA;AAAA,MAC3D,GAAK,EAAA,CAAA,QAAA,EAAW,WAAY,CAAA,IAAI,IAAI,GAAG,CAAA,CAAA;AAAA,KACxC,CAAA,CAAA;AAED,IAAA,MAAM,iBAAuC,GAAA;AAAA,MAC3C,UAAA,EAAY,QAAQ,IAAI,CAAA,MAAA,EAAS,QAAQ,GAAG,CAAA,MAAA,EAAS,WAAW,GAAG,CAAA,CAAA;AAAA,MACnE,QAAU,EAAA;AAAA,QACR;AAAA,UACE,KAAO,EAAA,MAAA;AAAA,UACP,SAAW,EAAA,KAAA;AAAA,SACb;AAAA,OACF;AAAA,MACA,OAAO,KAAM,CAAA,MAAA;AAAA,MACb,IAAM,EAAA,SAAA;AAAA,KACR,CAAA;AAEA,IAAA,IAAI,MAAQ,EAAA;AACV,MAAA,iBAAA,CAAkB,OAAU,GAAA,EAAE,MAAQ,EAAA,CAAC,MAAM,CAAE,EAAA,CAAA;AAAA,KACjD;AAEA,IAAM,MAAA,QAAA,GAAW,MAAMA,sBAAA,CAAM,SAAW,EAAA;AAAA,MACtC,OAAS,EAAA;AAAA,QACP,GAAG,WAAa,EAAA,OAAA;AAAA,QAChB,cAAgB,EAAA,kBAAA;AAAA,OAClB;AAAA,MACA,MAAQ,EAAA,MAAA;AAAA,MACR,IAAA,EAAM,IAAK,CAAA,SAAA,CAAU,iBAAiB,CAAA;AAAA,KACvC,CAAA,CAAA;AAED,IAAI,IAAA,QAAA,CAAS,WAAW,GAAK,EAAA;AAC3B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,gDAAA,EAAmD,SAAS,MAAM,CAAA,CAAA;AAAA,OACpE,CAAA;AAAA,KACF;AAEA,IAAM,MAAA,IAAA,GAA2B,MAAM,QAAA,CAAS,IAAK,EAAA,CAAA;AACrD,IAAA,KAAA,GAAQ,CAAC,GAAG,KAAO,EAAA,GAAG,KAAK,OAAO,CAAA,CAAA;AAClC,IAAe,YAAA,GAAA,IAAA,CAAK,QAAQ,KAAM,CAAA,MAAA,CAAA;AAAA,GAC3B,QAAA,YAAA,EAAA;AAET,EAAO,OAAA,KAAA,CAAA;AACT;;;;"}
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ var backendPluginApi = require('@backstage/backend-plugin-api');
4
+ var alpha = require('@backstage/plugin-catalog-node/alpha');
5
+ var AzureDevOpsEntityProvider = require('../providers/AzureDevOpsEntityProvider.cjs.js');
6
+
7
+ const catalogModuleAzureDevOpsEntityProvider = backendPluginApi.createBackendModule({
8
+ pluginId: "catalog",
9
+ moduleId: "azure-dev-ops-entity-provider",
10
+ register(env) {
11
+ env.registerInit({
12
+ deps: {
13
+ config: backendPluginApi.coreServices.rootConfig,
14
+ catalog: alpha.catalogProcessingExtensionPoint,
15
+ logger: backendPluginApi.coreServices.logger,
16
+ scheduler: backendPluginApi.coreServices.scheduler
17
+ },
18
+ async init({ config, catalog, logger, scheduler }) {
19
+ catalog.addEntityProvider(
20
+ AzureDevOpsEntityProvider.AzureDevOpsEntityProvider.fromConfig(config, {
21
+ logger,
22
+ scheduler
23
+ })
24
+ );
25
+ }
26
+ });
27
+ }
28
+ });
29
+
30
+ exports.catalogModuleAzureDevOpsEntityProvider = catalogModuleAzureDevOpsEntityProvider;
31
+ //# sourceMappingURL=catalogModuleAzureDevOpsEntityProvider.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalogModuleAzureDevOpsEntityProvider.cjs.js","sources":["../../src/module/catalogModuleAzureDevOpsEntityProvider.ts"],"sourcesContent":["/*\n * Copyright 2022 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 createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';\nimport { AzureDevOpsEntityProvider } from '../providers';\n\n/**\n * Registers the AzureDevOpsEntityProvider with the catalog processing extension point.\n *\n * @alpha\n */\nexport const catalogModuleAzureDevOpsEntityProvider = createBackendModule({\n pluginId: 'catalog',\n moduleId: 'azure-dev-ops-entity-provider',\n register(env) {\n env.registerInit({\n deps: {\n config: coreServices.rootConfig,\n catalog: catalogProcessingExtensionPoint,\n logger: coreServices.logger,\n scheduler: coreServices.scheduler,\n },\n async init({ config, catalog, logger, scheduler }) {\n catalog.addEntityProvider(\n AzureDevOpsEntityProvider.fromConfig(config, {\n logger,\n scheduler,\n }),\n );\n },\n });\n },\n});\n"],"names":["createBackendModule","coreServices","catalogProcessingExtensionPoint","AzureDevOpsEntityProvider"],"mappings":";;;;;;AA4BO,MAAM,yCAAyCA,oCAAoB,CAAA;AAAA,EACxE,QAAU,EAAA,SAAA;AAAA,EACV,QAAU,EAAA,+BAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,QAAQC,6BAAa,CAAA,UAAA;AAAA,QACrB,OAAS,EAAAC,qCAAA;AAAA,QACT,QAAQD,6BAAa,CAAA,MAAA;AAAA,QACrB,WAAWA,6BAAa,CAAA,SAAA;AAAA,OAC1B;AAAA,MACA,MAAM,IAAK,CAAA,EAAE,QAAQ,OAAS,EAAA,MAAA,EAAQ,WAAa,EAAA;AACjD,QAAQ,OAAA,CAAA,iBAAA;AAAA,UACNE,mDAAA,CAA0B,WAAW,MAAQ,EAAA;AAAA,YAC3C,MAAA;AAAA,YACA,SAAA;AAAA,WACD,CAAA;AAAA,SACH,CAAA;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AACF,CAAC;;;;"}
@@ -0,0 +1,107 @@
1
+ 'use strict';
2
+
3
+ var integration = require('@backstage/integration');
4
+ var pluginCatalogNode = require('@backstage/plugin-catalog-node');
5
+ var azure = require('../lib/azure.cjs.js');
6
+
7
+ class AzureDevOpsDiscoveryProcessor {
8
+ integrations;
9
+ credentialsProvider;
10
+ logger;
11
+ static fromConfig(config, options) {
12
+ const integrations = integration.ScmIntegrations.fromConfig(config);
13
+ return new AzureDevOpsDiscoveryProcessor({
14
+ ...options,
15
+ integrations
16
+ });
17
+ }
18
+ constructor(options) {
19
+ this.integrations = options.integrations;
20
+ this.logger = options.logger;
21
+ this.credentialsProvider = integration.DefaultAzureDevOpsCredentialsProvider.fromIntegrations(
22
+ options.integrations
23
+ );
24
+ }
25
+ getProcessorName() {
26
+ return "AzureDevOpsDiscoveryProcessor";
27
+ }
28
+ async readLocation(location, _optional, emit) {
29
+ if (location.type !== "azure-discovery") {
30
+ return false;
31
+ }
32
+ const azureConfig = this.integrations.azure.byUrl(location.target)?.config;
33
+ if (!azureConfig) {
34
+ throw new Error(
35
+ `There is no Azure integration that matches ${location.target}. Please add a configuration entry for it under integrations.azure`
36
+ );
37
+ }
38
+ const { baseUrl, org, project, repo, catalogPath, branch } = parseUrl(
39
+ location.target
40
+ );
41
+ this.logger.info(
42
+ `Reading Azure DevOps repositories from ${location.target}`
43
+ );
44
+ const files = await azure.codeSearch(
45
+ this.credentialsProvider,
46
+ azureConfig,
47
+ org,
48
+ project,
49
+ repo,
50
+ catalogPath,
51
+ branch
52
+ );
53
+ this.logger.debug(
54
+ `Found ${files.length} files in Azure DevOps from ${location.target}.`
55
+ );
56
+ for (const file of files) {
57
+ let target = `${baseUrl}/${org}/${project}/_git/${file.repository.name}?path=${file.path}`;
58
+ if (branch) {
59
+ target += `&version=GB${branch}`;
60
+ }
61
+ emit(
62
+ pluginCatalogNode.processingResult.location({
63
+ type: "url",
64
+ target,
65
+ // Not all locations may actually exist, since the user defined them as a wildcard pattern.
66
+ // Thus, we emit them as optional and let the downstream processor find them while not outputting
67
+ // an error if it couldn't.
68
+ presence: "optional"
69
+ })
70
+ );
71
+ }
72
+ return true;
73
+ }
74
+ }
75
+ function parseUrl(urlString) {
76
+ const url = new URL(urlString);
77
+ const path = url.pathname.slice(1).split("/");
78
+ const catalogPath = url.searchParams.get("path") || "/catalog-info.yaml";
79
+ let branch = url.searchParams.get("version") || "";
80
+ if (branch.startsWith("GB")) {
81
+ branch = branch.slice(2);
82
+ }
83
+ if (path.length === 2 && path[0].length && path[1].length) {
84
+ return {
85
+ baseUrl: url.origin,
86
+ org: decodeURIComponent(path[0]),
87
+ project: decodeURIComponent(path[1]),
88
+ repo: "",
89
+ catalogPath,
90
+ branch
91
+ };
92
+ } else if (path.length === 4 && path[0].length && path[1].length && path[2].length && path[3].length) {
93
+ return {
94
+ baseUrl: url.origin,
95
+ org: decodeURIComponent(path[0]),
96
+ project: decodeURIComponent(path[1]),
97
+ repo: decodeURIComponent(path[3]),
98
+ catalogPath,
99
+ branch
100
+ };
101
+ }
102
+ throw new Error(`Failed to parse ${urlString}`);
103
+ }
104
+
105
+ exports.AzureDevOpsDiscoveryProcessor = AzureDevOpsDiscoveryProcessor;
106
+ exports.parseUrl = parseUrl;
107
+ //# sourceMappingURL=AzureDevOpsDiscoveryProcessor.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AzureDevOpsDiscoveryProcessor.cjs.js","sources":["../../src/processors/AzureDevOpsDiscoveryProcessor.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 { Config } from '@backstage/config';\nimport {\n AzureDevOpsCredentialsProvider,\n DefaultAzureDevOpsCredentialsProvider,\n ScmIntegrationRegistry,\n ScmIntegrations,\n} from '@backstage/integration';\nimport {\n CatalogProcessor,\n CatalogProcessorEmit,\n processingResult,\n} from '@backstage/plugin-catalog-node';\nimport { LocationSpec } from '@backstage/plugin-catalog-common';\nimport { codeSearch } from '../lib';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\n/**\n * Extracts repositories out of an Azure DevOps org.\n *\n * The following will create locations for all projects which have a catalog-info.yaml\n * on the default branch. The first is shorthand for the second.\n *\n * target: \"https://dev.azure.com/org/project\"\n * or\n * target: https://dev.azure.com/org/project?path=/catalog-info.yaml\n *\n * You may also explicitly specify a single repo:\n *\n * target: https://dev.azure.com/org/project/_git/repo\n *\n * @public\n */\nexport class AzureDevOpsDiscoveryProcessor implements CatalogProcessor {\n private readonly integrations: ScmIntegrationRegistry;\n private readonly credentialsProvider: AzureDevOpsCredentialsProvider;\n private readonly logger: LoggerService;\n\n static fromConfig(config: Config, options: { logger: LoggerService }) {\n const integrations = ScmIntegrations.fromConfig(config);\n\n return new AzureDevOpsDiscoveryProcessor({\n ...options,\n integrations,\n });\n }\n\n constructor(options: {\n integrations: ScmIntegrationRegistry;\n logger: LoggerService;\n }) {\n this.integrations = options.integrations;\n this.logger = options.logger;\n this.credentialsProvider =\n DefaultAzureDevOpsCredentialsProvider.fromIntegrations(\n options.integrations,\n );\n }\n\n getProcessorName(): string {\n return 'AzureDevOpsDiscoveryProcessor';\n }\n\n async readLocation(\n location: LocationSpec,\n _optional: boolean,\n emit: CatalogProcessorEmit,\n ): Promise<boolean> {\n if (location.type !== 'azure-discovery') {\n return false;\n }\n\n const azureConfig = this.integrations.azure.byUrl(location.target)?.config;\n if (!azureConfig) {\n throw new Error(\n `There is no Azure integration that matches ${location.target}. Please add a configuration entry for it under integrations.azure`,\n );\n }\n\n const { baseUrl, org, project, repo, catalogPath, branch } = parseUrl(\n location.target,\n );\n this.logger.info(\n `Reading Azure DevOps repositories from ${location.target}`,\n );\n\n const files = await codeSearch(\n this.credentialsProvider,\n azureConfig,\n org,\n project,\n repo,\n catalogPath,\n branch,\n );\n\n this.logger.debug(\n `Found ${files.length} files in Azure DevOps from ${location.target}.`,\n );\n\n for (const file of files) {\n let target = `${baseUrl}/${org}/${project}/_git/${file.repository.name}?path=${file.path}`;\n\n if (branch) {\n target += `&version=GB${branch}`;\n }\n\n emit(\n processingResult.location({\n type: 'url',\n target,\n // Not all locations may actually exist, since the user defined them as a wildcard pattern.\n // Thus, we emit them as optional and let the downstream processor find them while not outputting\n // an error if it couldn't.\n presence: 'optional',\n }),\n );\n }\n\n return true;\n }\n}\n\n/**\n * parseUrl extracts segments from the Azure DevOps URL.\n */\nexport function parseUrl(urlString: string): {\n baseUrl: string;\n org: string;\n project: string;\n repo: string;\n catalogPath: string;\n branch: string;\n} {\n const url = new URL(urlString);\n const path = url.pathname.slice(1).split('/');\n\n const catalogPath = url.searchParams.get('path') || '/catalog-info.yaml';\n let branch = url.searchParams.get('version') || '';\n\n if (branch.startsWith('GB')) {\n // DevOps prefixes branch names with 'GB' in URLs\n branch = branch.slice(2);\n }\n\n if (path.length === 2 && path[0].length && path[1].length) {\n return {\n baseUrl: url.origin,\n org: decodeURIComponent(path[0]),\n project: decodeURIComponent(path[1]),\n repo: '',\n catalogPath,\n branch,\n };\n } else if (\n path.length === 4 &&\n path[0].length &&\n path[1].length &&\n path[2].length &&\n path[3].length\n ) {\n return {\n baseUrl: url.origin,\n org: decodeURIComponent(path[0]),\n project: decodeURIComponent(path[1]),\n repo: decodeURIComponent(path[3]),\n catalogPath,\n branch,\n };\n }\n\n throw new Error(`Failed to parse ${urlString}`);\n}\n"],"names":["ScmIntegrations","DefaultAzureDevOpsCredentialsProvider","codeSearch","processingResult"],"mappings":"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"}
@@ -2,11 +2,9 @@
2
2
 
3
3
  var integration = require('@backstage/integration');
4
4
  var pluginCatalogNode = require('@backstage/plugin-catalog-node');
5
- var backendPluginApi = require('@backstage/backend-plugin-api');
5
+ var config = require('./config.cjs.js');
6
6
  var uuid = require('uuid');
7
- var fetch = require('node-fetch');
8
-
9
- function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
7
+ var azure = require('../lib/azure.cjs.js');
10
8
 
11
9
  function _interopNamespaceCompat(e) {
12
10
  if (e && typeof e === 'object' && 'default' in e) return e;
@@ -27,87 +25,6 @@ function _interopNamespaceCompat(e) {
27
25
  }
28
26
 
29
27
  var uuid__namespace = /*#__PURE__*/_interopNamespaceCompat(uuid);
30
- var fetch__default = /*#__PURE__*/_interopDefaultCompat(fetch);
31
-
32
- const isCloud = (host) => host === "dev.azure.com";
33
- const PAGE_SIZE = 1e3;
34
- async function codeSearch(credentialsProvider, azureConfig, org, project, repo, path, branch) {
35
- const searchBaseUrl = isCloud(azureConfig.host) ? "https://almsearch.dev.azure.com" : `https://${azureConfig.host}`;
36
- const searchUrl = `${searchBaseUrl}/${org}/_apis/search/codesearchresults?api-version=6.0-preview.1`;
37
- let items = [];
38
- let hasMorePages = true;
39
- do {
40
- const credentials = await credentialsProvider.getCredentials({
41
- url: `https://${azureConfig.host}/${org}`
42
- });
43
- const searchRequestBody = {
44
- searchText: `path:${path} repo:${repo || "*"} proj:${project || "*"}`,
45
- $orderBy: [
46
- {
47
- field: "path",
48
- sortOrder: "ASC"
49
- }
50
- ],
51
- $skip: items.length,
52
- $top: PAGE_SIZE
53
- };
54
- if (branch) {
55
- searchRequestBody.filters = { Branch: [branch] };
56
- }
57
- const response = await fetch__default.default(searchUrl, {
58
- headers: {
59
- ...credentials?.headers,
60
- "Content-Type": "application/json"
61
- },
62
- method: "POST",
63
- body: JSON.stringify(searchRequestBody)
64
- });
65
- if (response.status !== 200) {
66
- throw new Error(
67
- `Azure DevOps search failed with response status ${response.status}`
68
- );
69
- }
70
- const body = await response.json();
71
- items = [...items, ...body.results];
72
- hasMorePages = body.count > items.length;
73
- } while (hasMorePages);
74
- return items;
75
- }
76
-
77
- function readAzureDevOpsConfigs(config) {
78
- const configs = [];
79
- const providerConfigs = config.getOptionalConfig(
80
- "catalog.providers.azureDevOps"
81
- );
82
- if (!providerConfigs) {
83
- return configs;
84
- }
85
- for (const id of providerConfigs.keys()) {
86
- configs.push(readAzureDevOpsConfig(id, providerConfigs.getConfig(id)));
87
- }
88
- return configs;
89
- }
90
- function readAzureDevOpsConfig(id, config) {
91
- const organization = config.getString("organization");
92
- const project = config.getString("project");
93
- const host = config.getOptionalString("host") || "dev.azure.com";
94
- const repository = config.getOptionalString("repository") || "*";
95
- const branch = config.getOptionalString("branch");
96
- const path = config.getOptionalString("path") || "/catalog-info.yaml";
97
- const schedule = config.has("schedule") ? backendPluginApi.readSchedulerServiceTaskScheduleDefinitionFromConfig(
98
- config.getConfig("schedule")
99
- ) : void 0;
100
- return {
101
- id,
102
- host,
103
- organization,
104
- project,
105
- repository,
106
- branch,
107
- path,
108
- schedule
109
- };
110
- }
111
28
 
112
29
  class AzureDevOpsEntityProvider {
113
30
  constructor(config, integration, credentialsProvider, logger, taskRunner) {
@@ -123,7 +40,7 @@ class AzureDevOpsEntityProvider {
123
40
  scheduleFn;
124
41
  connection;
125
42
  static fromConfig(configRoot, options) {
126
- const providerConfigs = readAzureDevOpsConfigs(configRoot);
43
+ const providerConfigs = config.readAzureDevOpsConfigs(configRoot);
127
44
  const scmIntegrations = integration.ScmIntegrations.fromConfig(configRoot);
128
45
  const credentialsProvider = integration.DefaultAzureDevOpsCredentialsProvider.fromIntegrations(scmIntegrations);
129
46
  if (!options.schedule && !options.scheduler) {
@@ -190,7 +107,7 @@ class AzureDevOpsEntityProvider {
190
107
  throw new Error("Not initialized");
191
108
  }
192
109
  logger.info("Discovering Azure DevOps catalog files");
193
- const files = await codeSearch(
110
+ const files = await azure.codeSearch(
194
111
  this.credentialsProvider,
195
112
  this.integration.config,
196
113
  this.config.organization,
@@ -235,5 +152,4 @@ class AzureDevOpsEntityProvider {
235
152
  }
236
153
 
237
154
  exports.AzureDevOpsEntityProvider = AzureDevOpsEntityProvider;
238
- exports.codeSearch = codeSearch;
239
- //# sourceMappingURL=AzureDevOpsEntityProvider-BnGCzUOK.cjs.js.map
155
+ //# sourceMappingURL=AzureDevOpsEntityProvider.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AzureDevOpsEntityProvider.cjs.js","sources":["../../src/providers/AzureDevOpsEntityProvider.ts"],"sourcesContent":["/*\n * Copyright 2022 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 } from '@backstage/config';\nimport {\n AzureDevOpsCredentialsProvider,\n AzureIntegration,\n DefaultAzureDevOpsCredentialsProvider,\n ScmIntegrations,\n} from '@backstage/integration';\nimport {\n EntityProvider,\n EntityProviderConnection,\n locationSpecToLocationEntity,\n} from '@backstage/plugin-catalog-node';\nimport { LocationSpec } from '@backstage/plugin-catalog-common';\nimport { readAzureDevOpsConfigs } from './config';\nimport { AzureDevOpsConfig } from './types';\nimport * as uuid from 'uuid';\nimport { codeSearch, CodeSearchResultItem } from '../lib';\nimport {\n SchedulerService,\n SchedulerServiceTaskRunner,\n LoggerService,\n} from '@backstage/backend-plugin-api';\n\n/**\n * Provider which discovers catalog files within an Azure DevOps repositories.\n *\n * Use `AzureDevOpsEntityProvider.fromConfig(...)` to create instances.\n *\n * @public\n */\nexport class AzureDevOpsEntityProvider implements EntityProvider {\n private readonly logger: LoggerService;\n private readonly scheduleFn: () => Promise<void>;\n private connection?: EntityProviderConnection;\n\n static fromConfig(\n configRoot: Config,\n options: {\n logger: LoggerService;\n schedule?: SchedulerServiceTaskRunner;\n scheduler?: SchedulerService;\n },\n ): AzureDevOpsEntityProvider[] {\n const providerConfigs = readAzureDevOpsConfigs(configRoot);\n const scmIntegrations = ScmIntegrations.fromConfig(configRoot);\n const credentialsProvider =\n DefaultAzureDevOpsCredentialsProvider.fromIntegrations(scmIntegrations);\n\n if (!options.schedule && !options.scheduler) {\n throw new Error('Either schedule or scheduler must be provided.');\n }\n\n return providerConfigs.map(providerConfig => {\n const integration = ScmIntegrations.fromConfig(configRoot).azure.byHost(\n providerConfig.host,\n );\n\n if (!integration) {\n throw new Error(\n `There is no Azure integration for host ${providerConfig.host}. Please add a configuration entry for it under integrations.azure`,\n );\n }\n\n if (!options.schedule && !providerConfig.schedule) {\n throw new Error(\n `No schedule provided neither via code nor config for AzureDevOpsEntityProvider:${providerConfig.id}.`,\n );\n }\n\n const taskRunner =\n options.schedule ??\n options.scheduler!.createScheduledTaskRunner(providerConfig.schedule!);\n\n return new AzureDevOpsEntityProvider(\n providerConfig,\n integration,\n credentialsProvider,\n options.logger,\n taskRunner,\n );\n });\n }\n\n private constructor(\n private readonly config: AzureDevOpsConfig,\n private readonly integration: AzureIntegration,\n private readonly credentialsProvider: AzureDevOpsCredentialsProvider,\n logger: LoggerService,\n taskRunner: SchedulerServiceTaskRunner,\n ) {\n this.logger = logger.child({\n target: this.getProviderName(),\n });\n\n this.scheduleFn = this.createScheduleFn(taskRunner);\n }\n\n private createScheduleFn(\n taskRunner: SchedulerServiceTaskRunner,\n ): () => Promise<void> {\n return async () => {\n const taskId = `${this.getProviderName()}:refresh`;\n return taskRunner.run({\n id: taskId,\n fn: async () => {\n const logger = this.logger.child({\n class: AzureDevOpsEntityProvider.prototype.constructor.name,\n taskId,\n taskInstanceId: uuid.v4(),\n });\n\n try {\n await this.refresh(logger);\n } catch (error) {\n logger.error(\n `${this.getProviderName()} refresh failed, ${error}`,\n error,\n );\n }\n },\n });\n };\n }\n\n /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */\n getProviderName(): string {\n return `AzureDevOpsEntityProvider:${this.config.id}`;\n }\n\n /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */\n async connect(connection: EntityProviderConnection): Promise<void> {\n this.connection = connection;\n await this.scheduleFn();\n }\n\n async refresh(logger: LoggerService) {\n if (!this.connection) {\n throw new Error('Not initialized');\n }\n\n logger.info('Discovering Azure DevOps catalog files');\n\n const files = await codeSearch(\n this.credentialsProvider,\n this.integration.config,\n this.config.organization,\n this.config.project,\n this.config.repository,\n this.config.path,\n this.config.branch || '',\n );\n\n logger.info(`Discovered ${files.length} catalog files`);\n\n const targets = files.map(key => this.createObjectUrl(key));\n const locations = Array.from(new Set(targets)).map(key =>\n this.createLocationSpec(key),\n );\n\n await this.connection.applyMutation({\n type: 'full',\n entities: locations.map(location => {\n return {\n locationKey: this.getProviderName(),\n entity: locationSpecToLocationEntity({ location }),\n };\n }),\n });\n\n logger.info(\n `Committed ${locations.length} locations for AzureDevOps catalog files`,\n );\n }\n\n private createLocationSpec(target: string): LocationSpec {\n return {\n type: 'url',\n target: target,\n presence: 'required',\n };\n }\n\n private createObjectUrl(file: CodeSearchResultItem): string {\n const baseUrl = `https://${this.config.host}/${this.config.organization}/${file.project.name}`;\n\n let fullUrl = `${baseUrl}/_git/${file.repository.name}?path=${file.path}`;\n if (this.config.branch) {\n fullUrl += `&version=GB${this.config.branch}`;\n }\n\n return encodeURI(fullUrl);\n }\n}\n"],"names":["readAzureDevOpsConfigs","ScmIntegrations","DefaultAzureDevOpsCredentialsProvider","integration","uuid","codeSearch","locationSpecToLocationEntity"],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ var backendPluginApi = require('@backstage/backend-plugin-api');
4
+
5
+ function readAzureDevOpsConfigs(config) {
6
+ const configs = [];
7
+ const providerConfigs = config.getOptionalConfig(
8
+ "catalog.providers.azureDevOps"
9
+ );
10
+ if (!providerConfigs) {
11
+ return configs;
12
+ }
13
+ for (const id of providerConfigs.keys()) {
14
+ configs.push(readAzureDevOpsConfig(id, providerConfigs.getConfig(id)));
15
+ }
16
+ return configs;
17
+ }
18
+ function readAzureDevOpsConfig(id, config) {
19
+ const organization = config.getString("organization");
20
+ const project = config.getString("project");
21
+ const host = config.getOptionalString("host") || "dev.azure.com";
22
+ const repository = config.getOptionalString("repository") || "*";
23
+ const branch = config.getOptionalString("branch");
24
+ const path = config.getOptionalString("path") || "/catalog-info.yaml";
25
+ const schedule = config.has("schedule") ? backendPluginApi.readSchedulerServiceTaskScheduleDefinitionFromConfig(
26
+ config.getConfig("schedule")
27
+ ) : void 0;
28
+ return {
29
+ id,
30
+ host,
31
+ organization,
32
+ project,
33
+ repository,
34
+ branch,
35
+ path,
36
+ schedule
37
+ };
38
+ }
39
+
40
+ exports.readAzureDevOpsConfigs = readAzureDevOpsConfigs;
41
+ //# sourceMappingURL=config.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.cjs.js","sources":["../../src/providers/config.ts"],"sourcesContent":["/*\n * Copyright 2022 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 { readSchedulerServiceTaskScheduleDefinitionFromConfig } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { AzureDevOpsConfig } from './types';\n\nexport function readAzureDevOpsConfigs(config: Config): AzureDevOpsConfig[] {\n const configs: AzureDevOpsConfig[] = [];\n\n const providerConfigs = config.getOptionalConfig(\n 'catalog.providers.azureDevOps',\n );\n\n if (!providerConfigs) {\n return configs;\n }\n\n for (const id of providerConfigs.keys()) {\n configs.push(readAzureDevOpsConfig(id, providerConfigs.getConfig(id)));\n }\n\n return configs;\n}\n\nfunction readAzureDevOpsConfig(id: string, config: Config): AzureDevOpsConfig {\n const organization = config.getString('organization');\n const project = config.getString('project');\n const host = config.getOptionalString('host') || 'dev.azure.com';\n const repository = config.getOptionalString('repository') || '*';\n const branch = config.getOptionalString('branch');\n const path = config.getOptionalString('path') || '/catalog-info.yaml';\n\n const schedule = config.has('schedule')\n ? readSchedulerServiceTaskScheduleDefinitionFromConfig(\n config.getConfig('schedule'),\n )\n : undefined;\n\n return {\n id,\n host,\n organization,\n project,\n repository,\n branch,\n path,\n schedule,\n };\n}\n"],"names":["readSchedulerServiceTaskScheduleDefinitionFromConfig"],"mappings":";;;;AAoBO,SAAS,uBAAuB,MAAqC,EAAA;AAC1E,EAAA,MAAM,UAA+B,EAAC,CAAA;AAEtC,EAAA,MAAM,kBAAkB,MAAO,CAAA,iBAAA;AAAA,IAC7B,+BAAA;AAAA,GACF,CAAA;AAEA,EAAA,IAAI,CAAC,eAAiB,EAAA;AACpB,IAAO,OAAA,OAAA,CAAA;AAAA,GACT;AAEA,EAAW,KAAA,MAAA,EAAA,IAAM,eAAgB,CAAA,IAAA,EAAQ,EAAA;AACvC,IAAA,OAAA,CAAQ,KAAK,qBAAsB,CAAA,EAAA,EAAI,gBAAgB,SAAU,CAAA,EAAE,CAAC,CAAC,CAAA,CAAA;AAAA,GACvE;AAEA,EAAO,OAAA,OAAA,CAAA;AACT,CAAA;AAEA,SAAS,qBAAA,CAAsB,IAAY,MAAmC,EAAA;AAC5E,EAAM,MAAA,YAAA,GAAe,MAAO,CAAA,SAAA,CAAU,cAAc,CAAA,CAAA;AACpD,EAAM,MAAA,OAAA,GAAU,MAAO,CAAA,SAAA,CAAU,SAAS,CAAA,CAAA;AAC1C,EAAA,MAAM,IAAO,GAAA,MAAA,CAAO,iBAAkB,CAAA,MAAM,CAAK,IAAA,eAAA,CAAA;AACjD,EAAA,MAAM,UAAa,GAAA,MAAA,CAAO,iBAAkB,CAAA,YAAY,CAAK,IAAA,GAAA,CAAA;AAC7D,EAAM,MAAA,MAAA,GAAS,MAAO,CAAA,iBAAA,CAAkB,QAAQ,CAAA,CAAA;AAChD,EAAA,MAAM,IAAO,GAAA,MAAA,CAAO,iBAAkB,CAAA,MAAM,CAAK,IAAA,oBAAA,CAAA;AAEjD,EAAA,MAAM,QAAW,GAAA,MAAA,CAAO,GAAI,CAAA,UAAU,CAClC,GAAAA,qEAAA;AAAA,IACE,MAAA,CAAO,UAAU,UAAU,CAAA;AAAA,GAE7B,GAAA,KAAA,CAAA,CAAA;AAEJ,EAAO,OAAA;AAAA,IACL,EAAA;AAAA,IACA,IAAA;AAAA,IACA,YAAA;AAAA,IACA,OAAA;AAAA,IACA,UAAA;AAAA,IACA,MAAA;AAAA,IACA,IAAA;AAAA,IACA,QAAA;AAAA,GACF,CAAA;AACF;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-backend-module-azure",
3
- "version": "0.2.3-next.0",
3
+ "version": "0.2.3-next.2",
4
4
  "description": "A Backstage catalog backend module that helps integrate towards Azure",
5
5
  "backstage": {
6
6
  "role": "backend-plugin-module",
@@ -51,17 +51,17 @@
51
51
  "test": "backstage-cli package test"
52
52
  },
53
53
  "dependencies": {
54
- "@backstage/backend-plugin-api": "^1.0.1-next.0",
55
- "@backstage/config": "^1.2.0",
56
- "@backstage/integration": "^1.15.0",
57
- "@backstage/plugin-catalog-common": "^1.1.0",
58
- "@backstage/plugin-catalog-node": "^1.13.1-next.0",
54
+ "@backstage/backend-plugin-api": "1.0.1-next.1",
55
+ "@backstage/config": "1.2.0",
56
+ "@backstage/integration": "1.15.1-next.1",
57
+ "@backstage/plugin-catalog-common": "1.1.0",
58
+ "@backstage/plugin-catalog-node": "1.13.1-next.1",
59
59
  "node-fetch": "^2.7.0",
60
60
  "uuid": "^9.0.0"
61
61
  },
62
62
  "devDependencies": {
63
- "@backstage/backend-test-utils": "^1.0.1-next.0",
64
- "@backstage/cli": "^0.28.0-next.0",
63
+ "@backstage/backend-test-utils": "1.0.1-next.2",
64
+ "@backstage/cli": "0.28.0-next.2",
65
65
  "luxon": "^3.0.0",
66
66
  "msw": "^1.0.0"
67
67
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"AzureDevOpsEntityProvider-BnGCzUOK.cjs.js","sources":["../../src/lib/azure.ts","../../src/providers/config.ts","../../src/providers/AzureDevOpsEntityProvider.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 fetch from 'node-fetch';\nimport {\n AzureDevOpsCredentialsProvider,\n AzureIntegrationConfig,\n} from '@backstage/integration';\n\nexport interface CodeSearchResponse {\n count: number;\n results: CodeSearchResultItem[];\n}\n\nexport interface CodeSearchResultItem {\n fileName: string;\n path: string;\n repository: {\n name: string;\n };\n project: {\n name: string;\n };\n branch?: string;\n}\n\ninterface CodeSearchRequest {\n searchText: string;\n $orderBy: Array<{ field: string; sortOrder: string }>;\n $skip: number;\n $top: number;\n filters?: {\n Branch: string[];\n };\n}\n\nconst isCloud = (host: string) => host === 'dev.azure.com';\nconst PAGE_SIZE = 1000;\n\n// codeSearch returns all files that matches the given search path.\nexport async function codeSearch(\n credentialsProvider: AzureDevOpsCredentialsProvider,\n azureConfig: AzureIntegrationConfig,\n org: string,\n project: string,\n repo: string,\n path: string,\n branch: string,\n): Promise<CodeSearchResultItem[]> {\n const searchBaseUrl = isCloud(azureConfig.host)\n ? 'https://almsearch.dev.azure.com'\n : `https://${azureConfig.host}`;\n const searchUrl = `${searchBaseUrl}/${org}/_apis/search/codesearchresults?api-version=6.0-preview.1`;\n\n let items: CodeSearchResultItem[] = [];\n let hasMorePages = true;\n\n do {\n const credentials = await credentialsProvider.getCredentials({\n url: `https://${azureConfig.host}/${org}`,\n });\n\n const searchRequestBody: CodeSearchRequest = {\n searchText: `path:${path} repo:${repo || '*'} proj:${project || '*'}`,\n $orderBy: [\n {\n field: 'path',\n sortOrder: 'ASC',\n },\n ],\n $skip: items.length,\n $top: PAGE_SIZE,\n };\n\n if (branch) {\n searchRequestBody.filters = { Branch: [branch] };\n }\n\n const response = await fetch(searchUrl, {\n headers: {\n ...credentials?.headers,\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n body: JSON.stringify(searchRequestBody),\n });\n\n if (response.status !== 200) {\n throw new Error(\n `Azure DevOps search failed with response status ${response.status}`,\n );\n }\n\n const body: CodeSearchResponse = await response.json();\n items = [...items, ...body.results];\n hasMorePages = body.count > items.length;\n } while (hasMorePages);\n\n return items;\n}\n","/*\n * Copyright 2022 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 { readSchedulerServiceTaskScheduleDefinitionFromConfig } from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport { AzureDevOpsConfig } from './types';\n\nexport function readAzureDevOpsConfigs(config: Config): AzureDevOpsConfig[] {\n const configs: AzureDevOpsConfig[] = [];\n\n const providerConfigs = config.getOptionalConfig(\n 'catalog.providers.azureDevOps',\n );\n\n if (!providerConfigs) {\n return configs;\n }\n\n for (const id of providerConfigs.keys()) {\n configs.push(readAzureDevOpsConfig(id, providerConfigs.getConfig(id)));\n }\n\n return configs;\n}\n\nfunction readAzureDevOpsConfig(id: string, config: Config): AzureDevOpsConfig {\n const organization = config.getString('organization');\n const project = config.getString('project');\n const host = config.getOptionalString('host') || 'dev.azure.com';\n const repository = config.getOptionalString('repository') || '*';\n const branch = config.getOptionalString('branch');\n const path = config.getOptionalString('path') || '/catalog-info.yaml';\n\n const schedule = config.has('schedule')\n ? readSchedulerServiceTaskScheduleDefinitionFromConfig(\n config.getConfig('schedule'),\n )\n : undefined;\n\n return {\n id,\n host,\n organization,\n project,\n repository,\n branch,\n path,\n schedule,\n };\n}\n","/*\n * Copyright 2022 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 } from '@backstage/config';\nimport {\n AzureDevOpsCredentialsProvider,\n AzureIntegration,\n DefaultAzureDevOpsCredentialsProvider,\n ScmIntegrations,\n} from '@backstage/integration';\nimport {\n EntityProvider,\n EntityProviderConnection,\n locationSpecToLocationEntity,\n} from '@backstage/plugin-catalog-node';\nimport { LocationSpec } from '@backstage/plugin-catalog-common';\nimport { readAzureDevOpsConfigs } from './config';\nimport { AzureDevOpsConfig } from './types';\nimport * as uuid from 'uuid';\nimport { codeSearch, CodeSearchResultItem } from '../lib';\nimport {\n SchedulerService,\n SchedulerServiceTaskRunner,\n LoggerService,\n} from '@backstage/backend-plugin-api';\n\n/**\n * Provider which discovers catalog files within an Azure DevOps repositories.\n *\n * Use `AzureDevOpsEntityProvider.fromConfig(...)` to create instances.\n *\n * @public\n */\nexport class AzureDevOpsEntityProvider implements EntityProvider {\n private readonly logger: LoggerService;\n private readonly scheduleFn: () => Promise<void>;\n private connection?: EntityProviderConnection;\n\n static fromConfig(\n configRoot: Config,\n options: {\n logger: LoggerService;\n schedule?: SchedulerServiceTaskRunner;\n scheduler?: SchedulerService;\n },\n ): AzureDevOpsEntityProvider[] {\n const providerConfigs = readAzureDevOpsConfigs(configRoot);\n const scmIntegrations = ScmIntegrations.fromConfig(configRoot);\n const credentialsProvider =\n DefaultAzureDevOpsCredentialsProvider.fromIntegrations(scmIntegrations);\n\n if (!options.schedule && !options.scheduler) {\n throw new Error('Either schedule or scheduler must be provided.');\n }\n\n return providerConfigs.map(providerConfig => {\n const integration = ScmIntegrations.fromConfig(configRoot).azure.byHost(\n providerConfig.host,\n );\n\n if (!integration) {\n throw new Error(\n `There is no Azure integration for host ${providerConfig.host}. Please add a configuration entry for it under integrations.azure`,\n );\n }\n\n if (!options.schedule && !providerConfig.schedule) {\n throw new Error(\n `No schedule provided neither via code nor config for AzureDevOpsEntityProvider:${providerConfig.id}.`,\n );\n }\n\n const taskRunner =\n options.schedule ??\n options.scheduler!.createScheduledTaskRunner(providerConfig.schedule!);\n\n return new AzureDevOpsEntityProvider(\n providerConfig,\n integration,\n credentialsProvider,\n options.logger,\n taskRunner,\n );\n });\n }\n\n private constructor(\n private readonly config: AzureDevOpsConfig,\n private readonly integration: AzureIntegration,\n private readonly credentialsProvider: AzureDevOpsCredentialsProvider,\n logger: LoggerService,\n taskRunner: SchedulerServiceTaskRunner,\n ) {\n this.logger = logger.child({\n target: this.getProviderName(),\n });\n\n this.scheduleFn = this.createScheduleFn(taskRunner);\n }\n\n private createScheduleFn(\n taskRunner: SchedulerServiceTaskRunner,\n ): () => Promise<void> {\n return async () => {\n const taskId = `${this.getProviderName()}:refresh`;\n return taskRunner.run({\n id: taskId,\n fn: async () => {\n const logger = this.logger.child({\n class: AzureDevOpsEntityProvider.prototype.constructor.name,\n taskId,\n taskInstanceId: uuid.v4(),\n });\n\n try {\n await this.refresh(logger);\n } catch (error) {\n logger.error(\n `${this.getProviderName()} refresh failed, ${error}`,\n error,\n );\n }\n },\n });\n };\n }\n\n /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */\n getProviderName(): string {\n return `AzureDevOpsEntityProvider:${this.config.id}`;\n }\n\n /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */\n async connect(connection: EntityProviderConnection): Promise<void> {\n this.connection = connection;\n await this.scheduleFn();\n }\n\n async refresh(logger: LoggerService) {\n if (!this.connection) {\n throw new Error('Not initialized');\n }\n\n logger.info('Discovering Azure DevOps catalog files');\n\n const files = await codeSearch(\n this.credentialsProvider,\n this.integration.config,\n this.config.organization,\n this.config.project,\n this.config.repository,\n this.config.path,\n this.config.branch || '',\n );\n\n logger.info(`Discovered ${files.length} catalog files`);\n\n const targets = files.map(key => this.createObjectUrl(key));\n const locations = Array.from(new Set(targets)).map(key =>\n this.createLocationSpec(key),\n );\n\n await this.connection.applyMutation({\n type: 'full',\n entities: locations.map(location => {\n return {\n locationKey: this.getProviderName(),\n entity: locationSpecToLocationEntity({ location }),\n };\n }),\n });\n\n logger.info(\n `Committed ${locations.length} locations for AzureDevOps catalog files`,\n );\n }\n\n private createLocationSpec(target: string): LocationSpec {\n return {\n type: 'url',\n target: target,\n presence: 'required',\n };\n }\n\n private createObjectUrl(file: CodeSearchResultItem): string {\n const baseUrl = `https://${this.config.host}/${this.config.organization}/${file.project.name}`;\n\n let fullUrl = `${baseUrl}/_git/${file.repository.name}?path=${file.path}`;\n if (this.config.branch) {\n fullUrl += `&version=GB${this.config.branch}`;\n }\n\n return encodeURI(fullUrl);\n }\n}\n"],"names":["fetch","readSchedulerServiceTaskScheduleDefinitionFromConfig","ScmIntegrations","DefaultAzureDevOpsCredentialsProvider","integration","uuid","locationSpecToLocationEntity"],"mappings":"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"}