Refactored annotation values

Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com>
This commit is contained in:
Andre Wanlin
2023-08-25 14:03:17 -05:00
committed by Andre Wanlin
parent 5001ce8873
commit 361bb34d8e
12 changed files with 292 additions and 79 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-azure-devops': patch
---
Consolidated getting the annotation values into a single function to help with future changes
@@ -16,14 +16,14 @@
import { BuildTable } from '../BuildTable/BuildTable';
import React from 'react';
import { getAnnotationFromEntity } from '../../utils/getAnnotationFromEntity';
import { getAnnotationValuesFromEntity } from '../../utils/getAnnotationValuesFromEntity';
import { useBuildRuns } from '../../hooks/useBuildRuns';
import { useEntity } from '@backstage/plugin-catalog-react';
export const EntityPageAzurePipelines = (props: { defaultLimit?: number }) => {
const { entity } = useEntity();
const { project, repo, definition } = getAnnotationFromEntity(entity);
const { project, repo, definition } = getAnnotationValuesFromEntity(entity);
const { items, loading, error } = useBuildRuns(
project,
@@ -23,11 +23,11 @@ import {
ErrorPanel,
} from '@backstage/core-components';
import { useEntity } from '@backstage/plugin-catalog-react';
import { useProjectRepoFromEntity } from '../../hooks';
import { useApi } from '@backstage/core-plugin-api';
import React from 'react';
import { azureDevOpsApiRef } from '../../api';
import useAsync from 'react-use/lib/useAsync';
import { getAnnotationValuesFromEntity } from '../../utils';
const useStyles = makeStyles(theme => ({
readMe: {
@@ -87,16 +87,14 @@ export const ReadmeCard = (props: Props) => {
const classes = useStyles();
const api = useApi(azureDevOpsApiRef);
const { entity } = useEntity();
const { project, repo } = useProjectRepoFromEntity(entity);
const { project, repo } = getAnnotationValuesFromEntity(entity);
const { loading, error, value } = useAsync(
() =>
api.getReadme({
project,
repo,
}),
[api, project, repo, entity],
);
const { loading, error, value } = useAsync(async () => {
if (repo) {
return await api.getReadme({ project, repo });
}
return undefined;
}, [api, project, repo, entity]);
if (loading) {
return <Progress />;
+2
View File
@@ -16,6 +16,8 @@
export const AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION =
'dev.azure.com/build-definition';
export const AZURE_DEVOPS_ORGANIZATION_ANNOTATION =
'dev.azure.com/organization';
export const AZURE_DEVOPS_PROJECT_ANNOTATION = 'dev.azure.com/project';
export const AZURE_DEVOPS_REPO_ANNOTATION = 'dev.azure.com/project-repo';
export const AZURE_DEVOPS_DEFAULT_TOP: number = 10;
-1
View File
@@ -16,7 +16,6 @@
export * from './useAllTeams';
export * from './useDashboardPullRequests';
export * from './useProjectRepoFromEntity';
export * from './usePullRequests';
export * from './useRepoBuilds';
export * from './useUserEmail';
+7 -4
View File
@@ -20,7 +20,7 @@ import { Entity } from '@backstage/catalog-model';
import { azureDevOpsApiRef } from '../api';
import { useApi } from '@backstage/core-plugin-api';
import useAsync from 'react-use/lib/useAsync';
import { useProjectRepoFromEntity } from './useProjectRepoFromEntity';
import { getAnnotationValuesFromEntity } from '../utils';
export function useGitTags(entity: Entity): {
items?: GitTag[];
@@ -28,10 +28,13 @@ export function useGitTags(entity: Entity): {
error?: Error;
} {
const api = useApi(azureDevOpsApiRef);
const { project, repo } = useProjectRepoFromEntity(entity);
const { project, repo } = getAnnotationValuesFromEntity(entity);
const { value, loading, error } = useAsync(() => {
return api.getGitTags(project, repo);
const { value, loading, error } = useAsync(async () => {
if (repo) {
return await api.getGitTags(project, repo);
}
return undefined;
}, [api, project, repo]);
return {
@@ -1,47 +0,0 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Entity } from '@backstage/catalog-model';
import { AZURE_DEVOPS_REPO_ANNOTATION } from '../constants';
export function useProjectRepoFromEntity(entity: Entity): {
project: string;
repo: string;
} {
const [project, repo] = (
entity.metadata.annotations?.[AZURE_DEVOPS_REPO_ANNOTATION] ?? ''
).split('/');
if (!project && !repo) {
throw new Error(
'Value for annotation dev.azure.com/project-repo was not in the correct format: <project-name>/<repo-name>',
);
}
if (!project) {
throw new Error(
'Project Name for annotation dev.azure.com/project-repo was not found; expected format is: <project-name>/<repo-name>',
);
}
if (!repo) {
throw new Error(
'Repo Name for annotation dev.azure.com/project-repo was not found; expected format is: <project-name>/<repo-name>',
);
}
return { project, repo };
}
@@ -25,7 +25,7 @@ import { Entity } from '@backstage/catalog-model';
import { azureDevOpsApiRef } from '../api';
import { useApi } from '@backstage/core-plugin-api';
import useAsync from 'react-use/lib/useAsync';
import { useProjectRepoFromEntity } from './useProjectRepoFromEntity';
import { getAnnotationValuesFromEntity } from '../utils';
export function usePullRequests(
entity: Entity,
@@ -44,10 +44,13 @@ export function usePullRequests(
};
const api = useApi(azureDevOpsApiRef);
const { project, repo } = useProjectRepoFromEntity(entity);
const { project, repo } = getAnnotationValuesFromEntity(entity);
const { value, loading, error } = useAsync(() => {
return api.getPullRequests(project, repo, options);
const { value, loading, error } = useAsync(async () => {
if (repo) {
return await api.getPullRequests(project, repo, options);
}
return undefined;
}, [api, project, repo, top, status]);
return {
@@ -24,7 +24,7 @@ import { Entity } from '@backstage/catalog-model';
import { azureDevOpsApiRef } from '../api';
import { useApi } from '@backstage/core-plugin-api';
import useAsync from 'react-use/lib/useAsync';
import { useProjectRepoFromEntity } from './useProjectRepoFromEntity';
import { getAnnotationValuesFromEntity } from '../utils';
export function useRepoBuilds(
entity: Entity,
@@ -40,10 +40,13 @@ export function useRepoBuilds(
};
const api = useApi(azureDevOpsApiRef);
const { project, repo } = useProjectRepoFromEntity(entity);
const { project, repo } = getAnnotationValuesFromEntity(entity);
const { value, loading, error } = useAsync(() => {
return api.getRepoBuilds(project, repo, options);
const { value, loading, error } = useAsync(async () => {
if (repo) {
return await api.getRepoBuilds(project, repo, options);
}
return undefined;
}, [api, project, repo, entity]);
return {
@@ -0,0 +1,230 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Entity } from '@backstage/catalog-model';
import { getAnnotationValuesFromEntity } from './getAnnotationValuesFromEntity';
describe('getAnnotationValuesFromEntity', () => {
describe('with valid project-repo annotation', () => {
it('should return project and repo', () => {
const entity: Entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
namespace: 'default',
name: 'project-repo',
annotations: {
'dev.azure.com/project-repo': 'projectName/repoName',
},
},
};
const { project, repo, definition, org } =
getAnnotationValuesFromEntity(entity);
expect(project).toEqual('projectName');
expect(repo).toEqual('repoName');
expect(definition).toEqual(undefined);
expect(org).toEqual(undefined);
});
});
describe('with invalid project-repo annotation', () => {
it('should throw incorrect format error', () => {
const entity: Entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
namespace: 'default',
name: 'project-repo',
annotations: {
'dev.azure.com/project-repo': 'project',
},
},
};
const test = () => {
return getAnnotationValuesFromEntity(entity);
};
expect(test).toThrow(
'Value for annotation dev.azure.com/project-repo was not in the correct format: <project-name>/<repo-name>',
);
});
});
describe('with project-repo annotation missing project', () => {
it('should throw missing project error', () => {
const entity: Entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
namespace: 'default',
name: 'project-repo',
annotations: {
'dev.azure.com/project-repo': '/repo',
},
},
};
const test = () => {
return getAnnotationValuesFromEntity(entity);
};
expect(test).toThrow(
'Project Name for annotation dev.azure.com/project-repo was not found; expected format is: <project-name>/<repo-name>',
);
});
});
describe('with project-repo annotation missing repo', () => {
it('should throw missing repo error', () => {
const entity: Entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
namespace: 'default',
name: 'project-repo',
annotations: {
'dev.azure.com/project-repo': 'project/',
},
},
};
const test = () => {
return getAnnotationValuesFromEntity(entity);
};
expect(test).toThrow(
'Repo Name for annotation dev.azure.com/project-repo was not found; expected format is: <project-name>/<repo-name>',
);
});
});
describe('with valid project and build-definition annotations', () => {
it('should return project and definition', () => {
const entity: Entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
namespace: 'default',
name: 'project-build-definition',
annotations: {
'dev.azure.com/project': 'projectName',
'dev.azure.com/build-definition': 'buildDefinitionName',
},
},
};
const { project, repo, definition, org } =
getAnnotationValuesFromEntity(entity);
expect(project).toEqual('projectName');
expect(repo).toEqual(undefined);
expect(definition).toEqual('buildDefinitionName');
expect(org).toEqual(undefined);
});
});
describe('with only project annotation', () => {
it('should return project and definition', () => {
const entity: Entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
namespace: 'default',
name: 'project',
annotations: {
'dev.azure.com/project': 'projectName',
},
},
};
const test = () => {
return getAnnotationValuesFromEntity(entity);
};
expect(test).toThrow(
'Value for annotation dev.azure.com/build-definition was not found',
);
});
});
describe('with only build-definition annotation', () => {
it('should return project and definition', () => {
const entity: Entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
namespace: 'default',
name: 'build-definition',
annotations: {
'dev.azure.com/build-definition': 'buildDefinitionName',
},
},
};
const test = () => {
return getAnnotationValuesFromEntity(entity);
};
expect(test).toThrow(
'Value for annotation dev.azure.com/project was not found',
);
});
});
describe('with valid project-repo and org annotations', () => {
it('should return project, repo, and org', () => {
const entity: Entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
namespace: 'default',
name: 'project-repo',
annotations: {
'dev.azure.com/project-repo': 'projectName/repoName',
'dev.azure.com/organization': 'organizationName',
},
},
};
const { project, repo, definition, org } =
getAnnotationValuesFromEntity(entity);
expect(project).toEqual('projectName');
expect(repo).toEqual('repoName');
expect(definition).toEqual(undefined);
expect(org).toEqual('organizationName');
});
});
describe('with valid project, build-definition, and org annotations', () => {
it('should return project, definition, and org', () => {
const entity: Entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
namespace: 'default',
name: 'project-build-definition',
annotations: {
'dev.azure.com/project': 'projectName',
'dev.azure.com/build-definition': 'buildDefinitionName',
'dev.azure.com/organization': 'organizationName',
},
},
};
const { project, repo, definition, org } =
getAnnotationValuesFromEntity(entity);
expect(project).toEqual('projectName');
expect(repo).toEqual(undefined);
expect(definition).toEqual('buildDefinitionName');
expect(org).toEqual('organizationName');
});
});
});
@@ -16,23 +16,28 @@
import {
AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION,
AZURE_DEVOPS_ORGANIZATION_ANNOTATION,
AZURE_DEVOPS_PROJECT_ANNOTATION,
AZURE_DEVOPS_REPO_ANNOTATION,
} from '../constants';
import { Entity } from '@backstage/catalog-model';
export function getAnnotationFromEntity(entity: Entity): {
export function getAnnotationValuesFromEntity(entity: Entity): {
project: string;
repo?: string;
definition?: string;
org?: string;
} {
const org =
entity.metadata.annotations?.[AZURE_DEVOPS_ORGANIZATION_ANNOTATION];
const annotation =
entity.metadata.annotations?.[AZURE_DEVOPS_REPO_ANNOTATION];
if (annotation) {
const { project, repo } = getProjectRepo(annotation);
const definition = undefined;
return { project, repo, definition };
return { project, repo, definition, org };
}
const project =
@@ -50,20 +55,32 @@ export function getAnnotationFromEntity(entity: Entity): {
}
const repo = undefined;
return { project, repo, definition };
return { project, repo, definition, org };
}
function getProjectRepo(annotation: string): {
project: string;
repo: string;
} {
const [project, repo] = annotation.split('/');
if (!project && !repo) {
if (!annotation.includes('/')) {
throw new Error(
'Value for annotation dev.azure.com/project-repo was not in the correct format: <project-name>/<repo-name>',
);
}
const [project, repo] = annotation.split('/');
if (!project) {
throw new Error(
'Project Name for annotation dev.azure.com/project-repo was not found; expected format is: <project-name>/<repo-name>',
);
}
if (!repo) {
throw new Error(
'Repo Name for annotation dev.azure.com/project-repo was not found; expected format is: <project-name>/<repo-name>',
);
}
return { project, repo };
}
+1 -1
View File
@@ -17,4 +17,4 @@
export * from './arrayHas';
export * from './equalsIgnoreCase';
export * from './getDurationFromDates';
export * from './getAnnotationFromEntity';
export * from './getAnnotationValuesFromEntity';