Merge pull request #14847 from MarcBruins/feature/MakeAzureDevOpsMultiProject

Make Azure DevOps Multiproject
This commit is contained in:
Patrik Oldsberg
2023-01-31 12:42:53 +01:00
committed by GitHub
7 changed files with 100 additions and 16 deletions
+30
View File
@@ -0,0 +1,30 @@
---
'@backstage/plugin-catalog-backend-module-azure': patch
---
This will add the ability to use Azure DevOps with multi project with a single value which is a new feature as previously this had to be done manually for each project that you wanted to add.
Right now you would have to fill in multiple values in the config to use multiple projects:
```
yourFirstProviderId: # identifies your dataset / provider independent of config changes
organization: myorg
project: 'firstProject' # this will match the firstProject project
repository: '*' # this will match all repos
path: /catalog-info.yaml
yourSecondProviderId: # identifies your dataset / provider independent of config changes
organization: myorg
project: 'secondProject' # this will match the secondProject project
repository: '*' # this will match all repos
path: /catalog-info.yaml
```
With this change you can actually have all projects available where your PAT determines which you have access to, so that includes multiple projects:
```
yourFirstProviderId: # identifies your dataset / provider independent of config changes
organization: myorg
project: '*' # this will match all projects where your PAT has access to
repository: '*' # this will match all repos
path: /catalog-info.yaml
```
+7 -2
View File
@@ -47,6 +47,11 @@ catalog:
frequency: { minutes: 30 }
# supports ISO duration, "human duration" as used in code
timeout: { minutes: 3 }
yourSecondProviderId: # identifies your dataset / provider independent of config changes
organization: myorg
project: '*' # this will match all projects
repository: '*' # this will match all repos
path: /catalog-info.yaml
anotherProviderId: # another identifier
organization: myorg
project: myproject
@@ -62,7 +67,7 @@ The parameters available are:
- **`host:`** _(optional)_ Leave empty for Cloud hosted, otherwise set to your self-hosted instance host.
- **`organization:`** Your Organization slug (or Collection for on-premise users). Required.
- **`project:`** Your project slug. Required.
- **`project:`** _(optional)_ Your project slug. Wildcards are supported as show on the examples above. If not set, all projects will be searched.
- **`repository:`** _(optional)_ The repository name. Wildcards are supported as show on the examples above. If not set, all repositories will be searched.
- **`path:`** _(optional)_ Where to find catalog-info.yaml files. Defaults to /catalog-info.yaml.
- **`schedule`** _(optional)_:
@@ -144,7 +149,7 @@ When using a custom pattern, the target is composed of five parts:
- The base instance URL, `https://dev.azure.com` in this case
- The organization name which is required, `myorg` in this case
- The project name which is required, `myproject` in this case
- The project name which is optional, `myproject` in this case. This defaults to \*, which scans all the projects where the token has access to.
- The repository blob to scan, which accepts \* wildcard tokens and must be
added after `_git/`. This can simply be `*` to scan all repositories in the
project.
@@ -29,11 +29,11 @@ describe('azure', () => {
server.use(
rest.post(
`https://almsearch.dev.azure.com/shopify/engineering/_apis/search/codesearchresults`,
`https://almsearch.dev.azure.com/shopify/_apis/search/codesearchresults`,
(req, res, ctx) => {
expect(req.headers.get('Authorization')).toBe('Basic OkFCQw==');
expect(req.body).toEqual({
searchText: 'path:/catalog-info.yaml repo:*',
searchText: 'path:/catalog-info.yaml repo:* proj:engineering',
$skip: 0,
$top: 1000,
});
@@ -64,6 +64,9 @@ describe('azure', () => {
repository: {
name: 'backstage',
},
project: {
name: 'backstage',
},
},
{
fileName: 'catalog-info.yaml',
@@ -71,17 +74,20 @@ describe('azure', () => {
repository: {
name: 'ios-app',
},
project: {
name: 'backstage',
},
},
],
};
server.use(
rest.post(
`https://almsearch.dev.azure.com/shopify/engineering/_apis/search/codesearchresults`,
`https://almsearch.dev.azure.com/shopify/_apis/search/codesearchresults`,
(req, res, ctx) => {
expect(req.headers.get('Authorization')).toBe('Basic OkFCQw==');
expect(req.body).toEqual({
searchText: 'path:/catalog-info.yaml repo:*',
searchText: 'path:/catalog-info.yaml repo:* proj:engineering',
$skip: 0,
$top: 1000,
});
@@ -108,6 +114,9 @@ describe('azure', () => {
{
fileName: 'catalog-info.yaml',
path: '/catalog-info.yaml',
project: {
name: '*',
},
repository: {
name: 'backstage',
},
@@ -117,11 +126,12 @@ describe('azure', () => {
server.use(
rest.post(
`https://almsearch.dev.azure.com/shopify/engineering/_apis/search/codesearchresults`,
`https://almsearch.dev.azure.com/shopify/_apis/search/codesearchresults`,
(req, res, ctx) => {
expect(req.headers.get('Authorization')).toBe('Basic OkFCQw==');
expect(req.body).toEqual({
searchText: 'path:/catalog-info.yaml repo:backstage',
searchText:
'path:/catalog-info.yaml repo:backstage proj:engineering',
$skip: 0,
$top: 1000,
});
@@ -151,17 +161,20 @@ describe('azure', () => {
repository: {
name: 'backstage',
},
project: {
name: '*',
},
},
],
};
server.use(
rest.post(
`https://azuredevops.mycompany.com/shopify/engineering/_apis/search/codesearchresults`,
`https://azuredevops.mycompany.com/shopify/_apis/search/codesearchresults`,
(req, res, ctx) => {
expect(req.headers.get('Authorization')).toBe('Basic OkFCQw==');
expect(req.body).toEqual({
searchText: 'path:/catalog-info.yaml repo:*',
searchText: 'path:/catalog-info.yaml repo:* proj:engineering',
$skip: 0,
$top: 1000,
});
@@ -190,16 +203,20 @@ describe('azure', () => {
repository: {
name: 'backstage',
},
project: {
name: 'engineering',
},
}));
};
server.use(
rest.post(
`https://almsearch.dev.azure.com/shopify/engineering/_apis/search/codesearchresults`,
`https://almsearch.dev.azure.com/shopify/_apis/search/codesearchresults`,
(req, res, ctx) => {
expect(req.headers.get('Authorization')).toBe('Basic OkFCQw==');
expect(req.body).toMatchObject({
searchText: 'path:/catalog-info.yaml repo:backstage',
searchText:
'path:/catalog-info.yaml repo:backstage proj:engineering',
$top: 1000,
});
@@ -31,6 +31,9 @@ export interface CodeSearchResultItem {
repository: {
name: string;
};
project: {
name: string;
};
}
const isCloud = (host: string) => host === 'dev.azure.com';
@@ -47,7 +50,7 @@ export async function codeSearch(
const searchBaseUrl = isCloud(azureConfig.host)
? 'https://almsearch.dev.azure.com'
: `https://${azureConfig.host}`;
const searchUrl = `${searchBaseUrl}/${org}/${project}/_apis/search/codesearchresults?api-version=6.0-preview.1`;
const searchUrl = `${searchBaseUrl}/${org}/_apis/search/codesearchresults?api-version=6.0-preview.1`;
let items: CodeSearchResultItem[] = [];
let hasMorePages = true;
@@ -59,7 +62,7 @@ export async function codeSearch(
}),
method: 'POST',
body: JSON.stringify({
searchText: `path:${path} repo:${repo || '*'}`,
searchText: `path:${path} repo:${repo || '*'} proj:${project || '*'}`,
$skip: items.length,
$top: PAGE_SIZE,
}),
@@ -135,6 +135,9 @@ describe('AzureDevOpsDiscoveryProcessor', () => {
{
fileName: 'catalog-info.yaml',
path: '/catalog-info.yaml',
project: {
name: '*',
},
repository: {
name: 'backstage',
},
@@ -142,6 +145,9 @@ describe('AzureDevOpsDiscoveryProcessor', () => {
{
fileName: 'catalog-info.yaml',
path: '/src/catalog-info.yaml',
project: {
name: '*',
},
repository: {
name: 'ios-app',
},
@@ -191,6 +197,9 @@ describe('AzureDevOpsDiscoveryProcessor', () => {
repository: {
name: 'backstage',
},
project: {
name: '*',
},
},
]);
const emitter = jest.fn();
@@ -229,6 +238,9 @@ describe('AzureDevOpsDiscoveryProcessor', () => {
repository: {
name: 'backstage',
},
project: {
name: '*',
},
},
]);
const emitter = jest.fn();
@@ -164,6 +164,9 @@ describe('AzureDevOpsEntityProvider', () => {
repository: {
name: 'myrepo',
},
project: {
name: 'myproject',
},
},
],
'https://dev.azure.com/myorganization/myproject',
@@ -189,6 +192,9 @@ describe('AzureDevOpsEntityProvider', () => {
repository: {
name: 'myrepo',
},
project: {
name: 'myproject',
},
},
{
fileName: 'catalog-info.yaml',
@@ -196,6 +202,9 @@ describe('AzureDevOpsEntityProvider', () => {
repository: {
name: 'myotherrepo',
},
project: {
name: 'myproject',
},
},
],
'https://dev.azure.com/myorganization/myproject',
@@ -277,6 +286,9 @@ describe('AzureDevOpsEntityProvider', () => {
repository: {
name: 'myrepo',
},
project: {
name: 'myproject',
},
},
{
fileName: 'catalog-info.yaml',
@@ -284,6 +296,9 @@ describe('AzureDevOpsEntityProvider', () => {
repository: {
name: 'myotherrepo',
},
project: {
name: 'myproject',
},
},
],
'https://dev.azure.com/myorganization/myproject',
@@ -166,15 +166,17 @@ export class AzureDevOpsEntityProvider implements EntityProvider {
}
private createLocationSpec(file: CodeSearchResultItem): LocationSpec {
const target = this.createObjectUrl(file);
return {
type: 'url',
target: this.createObjectUrl(file),
target: target,
presence: 'required',
};
}
private createObjectUrl(file: CodeSearchResultItem): string {
const baseUrl = `https://${this.config.host}/${this.config.organization}/${this.config.project}`;
const baseUrl = `https://${this.config.host}/${this.config.organization}/${file.project.name}`;
return encodeURI(
`${baseUrl}/_git/${file.repository.name}?path=${file.path}`,
);