@backstage/plugin-catalog-backend-module-azure 0.1.19 → 0.1.21-next.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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @backstage/plugin-catalog-backend-module-azure
2
2
 
3
+ ## 0.1.21-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 94f96508491d: Improve consistency of results from the `AzureDevOpsEntityProvider`.
8
+ - 5f1a92b9f19f: Use `DefaultAzureDevOpsCredentialsProvider` to retrieve credentials for Azure DevOps.
9
+ - 044b4f2fb1e3: Remove duplications from Azure search before committing the new locations to the catalog.
10
+ - Updated dependencies
11
+ - @backstage/backend-common@0.19.4-next.0
12
+ - @backstage/integration@1.7.0-next.0
13
+ - @backstage/backend-tasks@0.5.7-next.0
14
+ - @backstage/backend-plugin-api@0.6.2-next.0
15
+ - @backstage/catalog-model@1.4.1
16
+ - @backstage/config@1.0.8
17
+ - @backstage/errors@1.2.1
18
+ - @backstage/types@1.1.0
19
+ - @backstage/plugin-catalog-common@1.0.15
20
+ - @backstage/plugin-catalog-node@1.4.3-next.0
21
+
3
22
  ## 0.1.19
4
23
 
5
24
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-backend-module-azure",
3
- "version": "0.1.19",
3
+ "version": "0.1.21-next.0",
4
4
  "main": "../dist/alpha.cjs.js",
5
5
  "types": "../dist/alpha.d.ts"
6
6
  }
package/dist/alpha.cjs.js CHANGED
@@ -5,7 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var backendCommon = require('@backstage/backend-common');
6
6
  var backendPluginApi = require('@backstage/backend-plugin-api');
7
7
  var alpha = require('@backstage/plugin-catalog-node/alpha');
8
- var AzureDevOpsEntityProvider = require('./cjs/AzureDevOpsEntityProvider-0ed23619.cjs.js');
8
+ var AzureDevOpsEntityProvider = require('./cjs/AzureDevOpsEntityProvider-6fe9022c.cjs.js');
9
9
  require('@backstage/integration');
10
10
  require('@backstage/plugin-catalog-node');
11
11
  require('@backstage/backend-tasks');
@@ -31,19 +31,29 @@ var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
31
31
 
32
32
  const isCloud = (host) => host === "dev.azure.com";
33
33
  const PAGE_SIZE = 1e3;
34
- async function codeSearch(azureConfig, org, project, repo, path) {
34
+ async function codeSearch(credentialsProvider, azureConfig, org, project, repo, path) {
35
35
  const searchBaseUrl = isCloud(azureConfig.host) ? "https://almsearch.dev.azure.com" : `https://${azureConfig.host}`;
36
36
  const searchUrl = `${searchBaseUrl}/${org}/_apis/search/codesearchresults?api-version=6.0-preview.1`;
37
37
  let items = [];
38
38
  let hasMorePages = true;
39
39
  do {
40
+ const credentials = await credentialsProvider.getCredentials({
41
+ url: `https://${azureConfig.host}/${org}`
42
+ });
40
43
  const response = await fetch__default["default"](searchUrl, {
41
- ...await integration.getAzureRequestOptions(azureConfig, {
44
+ headers: {
45
+ ...credentials == null ? void 0 : credentials.headers,
42
46
  "Content-Type": "application/json"
43
- }),
47
+ },
44
48
  method: "POST",
45
49
  body: JSON.stringify({
46
50
  searchText: `path:${path} repo:${repo || "*"} proj:${project || "*"}`,
51
+ $orderBy: [
52
+ {
53
+ field: "path",
54
+ sortOrder: "ASC"
55
+ }
56
+ ],
47
57
  $skip: items.length,
48
58
  $top: PAGE_SIZE
49
59
  })
@@ -100,9 +110,10 @@ var __publicField = (obj, key, value) => {
100
110
  return value;
101
111
  };
102
112
  class AzureDevOpsEntityProvider {
103
- constructor(config, integration, logger, taskRunner) {
113
+ constructor(config, integration, credentialsProvider, logger, taskRunner) {
104
114
  this.config = config;
105
115
  this.integration = integration;
116
+ this.credentialsProvider = credentialsProvider;
106
117
  __publicField(this, "logger");
107
118
  __publicField(this, "scheduleFn");
108
119
  __publicField(this, "connection");
@@ -113,6 +124,8 @@ class AzureDevOpsEntityProvider {
113
124
  }
114
125
  static fromConfig(configRoot, options) {
115
126
  const providerConfigs = readAzureDevOpsConfigs(configRoot);
127
+ const scmIntegrations = integration.ScmIntegrations.fromConfig(configRoot);
128
+ const credentialsProvider = integration.DefaultAzureDevOpsCredentialsProvider.fromIntegrations(scmIntegrations);
116
129
  if (!options.schedule && !options.scheduler) {
117
130
  throw new Error("Either schedule or scheduler must be provided.");
118
131
  }
@@ -135,6 +148,7 @@ class AzureDevOpsEntityProvider {
135
148
  return new AzureDevOpsEntityProvider(
136
149
  providerConfig,
137
150
  integration$1,
151
+ credentialsProvider,
138
152
  options.logger,
139
153
  taskRunner
140
154
  );
@@ -175,6 +189,7 @@ class AzureDevOpsEntityProvider {
175
189
  }
176
190
  logger.info("Discovering Azure DevOps catalog files");
177
191
  const files = await codeSearch(
192
+ this.credentialsProvider,
178
193
  this.integration.config,
179
194
  this.config.organization,
180
195
  this.config.project,
@@ -182,7 +197,10 @@ class AzureDevOpsEntityProvider {
182
197
  this.config.path
183
198
  );
184
199
  logger.info(`Discovered ${files.length} catalog files`);
185
- const locations = files.map((key) => this.createLocationSpec(key));
200
+ const targets = files.map((key) => this.createObjectUrl(key));
201
+ const locations = Array.from(new Set(targets)).map(
202
+ (key) => this.createLocationSpec(key)
203
+ );
186
204
  await this.connection.applyMutation({
187
205
  type: "full",
188
206
  entities: locations.map((location) => {
@@ -196,8 +214,7 @@ class AzureDevOpsEntityProvider {
196
214
  `Committed ${locations.length} locations for AzureDevOps catalog files`
197
215
  );
198
216
  }
199
- createLocationSpec(file) {
200
- const target = this.createObjectUrl(file);
217
+ createLocationSpec(target) {
201
218
  return {
202
219
  type: "url",
203
220
  target,
@@ -216,4 +233,4 @@ class AzureDevOpsEntityProvider {
216
233
 
217
234
  exports.AzureDevOpsEntityProvider = AzureDevOpsEntityProvider;
218
235
  exports.codeSearch = codeSearch;
219
- //# sourceMappingURL=AzureDevOpsEntityProvider-0ed23619.cjs.js.map
236
+ //# sourceMappingURL=AzureDevOpsEntityProvider-6fe9022c.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AzureDevOpsEntityProvider-6fe9022c.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\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): 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 response = await fetch(searchUrl, {\n headers: {\n ...credentials?.headers,\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n body: JSON.stringify({\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\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 { readTaskScheduleDefinitionFromConfig } from '@backstage/backend-tasks';\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 ? readTaskScheduleDefinitionFromConfig(config.getConfig('schedule'))\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 { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks';\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 { Logger } from 'winston';\nimport { AzureDevOpsConfig } from './types';\nimport * as uuid from 'uuid';\nimport { codeSearch, CodeSearchResultItem } from '../lib';\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: Logger;\n private readonly scheduleFn: () => Promise<void>;\n private connection?: EntityProviderConnection;\n\n static fromConfig(\n configRoot: Config,\n options: {\n logger: Logger;\n schedule?: TaskRunner;\n scheduler?: PluginTaskScheduler;\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: Logger,\n taskRunner: TaskRunner,\n ) {\n this.logger = logger.child({\n target: this.getProviderName(),\n });\n\n this.scheduleFn = this.createScheduleFn(taskRunner);\n }\n\n private createScheduleFn(taskRunner: TaskRunner): () => 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(`${this.getProviderName()} refresh failed`, error);\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: Logger) {\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 );\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","readTaskScheduleDefinitionFromConfig","ScmIntegrations","DefaultAzureDevOpsCredentialsProvider","integration","uuid","locationSpecToLocationEntity"],"mappings":"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"}
package/dist/index.cjs.js CHANGED
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var integration = require('@backstage/integration');
6
6
  var pluginCatalogNode = require('@backstage/plugin-catalog-node');
7
- var AzureDevOpsEntityProvider = require('./cjs/AzureDevOpsEntityProvider-0ed23619.cjs.js');
7
+ var AzureDevOpsEntityProvider = require('./cjs/AzureDevOpsEntityProvider-6fe9022c.cjs.js');
8
8
  require('@backstage/backend-tasks');
9
9
  require('uuid');
10
10
  require('node-fetch');
@@ -18,9 +18,13 @@ var __publicField = (obj, key, value) => {
18
18
  class AzureDevOpsDiscoveryProcessor {
19
19
  constructor(options) {
20
20
  __publicField(this, "integrations");
21
+ __publicField(this, "credentialsProvider");
21
22
  __publicField(this, "logger");
22
23
  this.integrations = options.integrations;
23
24
  this.logger = options.logger;
25
+ this.credentialsProvider = integration.DefaultAzureDevOpsCredentialsProvider.fromIntegrations(
26
+ options.integrations
27
+ );
24
28
  }
25
29
  static fromConfig(config, options) {
26
30
  const integrations = integration.ScmIntegrations.fromConfig(config);
@@ -50,6 +54,7 @@ class AzureDevOpsDiscoveryProcessor {
50
54
  `Reading Azure DevOps repositories from ${location.target}`
51
55
  );
52
56
  const files = await AzureDevOpsEntityProvider.codeSearch(
57
+ this.credentialsProvider,
53
58
  azureConfig,
54
59
  org,
55
60
  project,
@@ -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 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 { Logger } from 'winston';\nimport { codeSearch } from '../lib';\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 logger: Logger;\n\n static fromConfig(config: Config, options: { logger: Logger }) {\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: Logger;\n }) {\n this.integrations = options.integrations;\n this.logger = options.logger;\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 } = 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 azureConfig,\n org,\n project,\n repo,\n catalogPath,\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 emit(\n processingResult.location({\n type: 'url',\n target: `${baseUrl}/${org}/${project}/_git/${file.repository.name}?path=${file.path}`,\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} {\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\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 };\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 };\n }\n\n throw new Error(`Failed to parse ${urlString}`);\n}\n"],"names":["ScmIntegrations","codeSearch","processingResult"],"mappings":"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"}
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 { Logger } from 'winston';\nimport { codeSearch } from '../lib';\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: Logger;\n\n static fromConfig(config: Config, options: { logger: Logger }) {\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: Logger;\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 } = 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 );\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 emit(\n processingResult.location({\n type: 'url',\n target: `${baseUrl}/${org}/${project}/_git/${file.repository.name}?path=${file.path}`,\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} {\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\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 };\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 };\n }\n\n throw new Error(`Failed to parse ${urlString}`);\n}\n"],"names":["DefaultAzureDevOpsCredentialsProvider","ScmIntegrations","codeSearch","processingResult"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -23,6 +23,7 @@ import { TaskRunner, PluginTaskScheduler } from '@backstage/backend-tasks';
23
23
  */
24
24
  declare class AzureDevOpsDiscoveryProcessor implements CatalogProcessor {
25
25
  private readonly integrations;
26
+ private readonly credentialsProvider;
26
27
  private readonly logger;
27
28
  static fromConfig(config: Config, options: {
28
29
  logger: Logger;
@@ -45,6 +46,7 @@ declare class AzureDevOpsDiscoveryProcessor implements CatalogProcessor {
45
46
  declare class AzureDevOpsEntityProvider implements EntityProvider {
46
47
  private readonly config;
47
48
  private readonly integration;
49
+ private readonly credentialsProvider;
48
50
  private readonly logger;
49
51
  private readonly scheduleFn;
50
52
  private connection?;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-backend-module-azure",
3
3
  "description": "A Backstage catalog backend module that helps integrate towards Azure",
4
- "version": "0.1.19",
4
+ "version": "0.1.21-next.0",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "license": "Apache-2.0",
@@ -43,15 +43,15 @@
43
43
  "clean": "backstage-cli package clean"
44
44
  },
45
45
  "dependencies": {
46
- "@backstage/backend-common": "^0.19.2",
47
- "@backstage/backend-plugin-api": "^0.6.0",
48
- "@backstage/backend-tasks": "^0.5.5",
46
+ "@backstage/backend-common": "^0.19.4-next.0",
47
+ "@backstage/backend-plugin-api": "^0.6.2-next.0",
48
+ "@backstage/backend-tasks": "^0.5.7-next.0",
49
49
  "@backstage/catalog-model": "^1.4.1",
50
50
  "@backstage/config": "^1.0.8",
51
51
  "@backstage/errors": "^1.2.1",
52
- "@backstage/integration": "^1.6.0",
52
+ "@backstage/integration": "^1.7.0-next.0",
53
53
  "@backstage/plugin-catalog-common": "^1.0.15",
54
- "@backstage/plugin-catalog-node": "^1.4.1",
54
+ "@backstage/plugin-catalog-node": "^1.4.3-next.0",
55
55
  "@backstage/types": "^1.1.0",
56
56
  "lodash": "^4.17.21",
57
57
  "node-fetch": "^2.6.7",
@@ -59,8 +59,8 @@
59
59
  "winston": "^3.2.1"
60
60
  },
61
61
  "devDependencies": {
62
- "@backstage/backend-test-utils": "^0.2.0",
63
- "@backstage/cli": "^0.22.10",
62
+ "@backstage/backend-test-utils": "^0.2.2-next.0",
63
+ "@backstage/cli": "^0.22.12-next.0",
64
64
  "@types/lodash": "^4.14.151",
65
65
  "luxon": "^3.0.0",
66
66
  "msw": "^1.0.0"
@@ -1 +0,0 @@
1
- {"version":3,"file":"AzureDevOpsEntityProvider-0ed23619.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 AzureIntegrationConfig,\n getAzureRequestOptions,\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\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 azureConfig: AzureIntegrationConfig,\n org: string,\n project: string,\n repo: string,\n path: 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 response = await fetch(searchUrl, {\n ...(await getAzureRequestOptions(azureConfig, {\n 'Content-Type': 'application/json',\n })),\n method: 'POST',\n body: JSON.stringify({\n searchText: `path:${path} repo:${repo || '*'} proj:${project || '*'}`,\n $skip: items.length,\n $top: PAGE_SIZE,\n }),\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 { readTaskScheduleDefinitionFromConfig } from '@backstage/backend-tasks';\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 ? readTaskScheduleDefinitionFromConfig(config.getConfig('schedule'))\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 { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks';\nimport { Config } from '@backstage/config';\nimport { AzureIntegration, ScmIntegrations } 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 { Logger } from 'winston';\nimport { AzureDevOpsConfig } from './types';\nimport * as uuid from 'uuid';\nimport { codeSearch, CodeSearchResultItem } from '../lib';\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: Logger;\n private readonly scheduleFn: () => Promise<void>;\n private connection?: EntityProviderConnection;\n\n static fromConfig(\n configRoot: Config,\n options: {\n logger: Logger;\n schedule?: TaskRunner;\n scheduler?: PluginTaskScheduler;\n },\n ): AzureDevOpsEntityProvider[] {\n const providerConfigs = readAzureDevOpsConfigs(configRoot);\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 options.logger,\n taskRunner,\n );\n });\n }\n\n private constructor(\n private readonly config: AzureDevOpsConfig,\n private readonly integration: AzureIntegration,\n logger: Logger,\n taskRunner: TaskRunner,\n ) {\n this.logger = logger.child({\n target: this.getProviderName(),\n });\n\n this.scheduleFn = this.createScheduleFn(taskRunner);\n }\n\n private createScheduleFn(taskRunner: TaskRunner): () => 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(`${this.getProviderName()} refresh failed`, error);\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: Logger) {\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.integration.config,\n this.config.organization,\n this.config.project,\n this.config.repository,\n this.config.path,\n );\n\n logger.info(`Discovered ${files.length} catalog files`);\n\n const locations = files.map(key => this.createLocationSpec(key));\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(file: CodeSearchResultItem): LocationSpec {\n const target = this.createObjectUrl(file);\n\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","getAzureRequestOptions","readTaskScheduleDefinitionFromConfig","integration","ScmIntegrations","uuid","locationSpecToLocationEntity"],"mappings":"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"}