feat: add readme card plugin on azure devops

Signed-off-by: Alisson Fabiano <afabiano@eshopworld.com>
This commit is contained in:
Alisson Fabiano
2022-08-11 16:07:10 +01:00
parent af1e11b744
commit bd34a1e7d4
10 changed files with 333 additions and 2 deletions
@@ -21,6 +21,8 @@ import {
GitTag,
PullRequest,
PullRequestOptions,
Readme,
ReadmeConfig,
RepoBuild,
RepoBuildOptions,
Team,
@@ -64,4 +66,6 @@ export interface AzureDevOpsApi {
definitionName?: string,
options?: BuildRunOptions,
): Promise<{ items: BuildRun[] }>;
getReadme(opts: ReadmeConfig): Promise<Readme | undefined>;
}
@@ -21,6 +21,8 @@ import {
GitTag,
PullRequest,
PullRequestOptions,
Readme,
ReadmeConfig,
RepoBuild,
RepoBuildOptions,
Team,
@@ -30,6 +32,10 @@ import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api';
import { AzureDevOpsApi } from './AzureDevOpsApi';
import { ResponseError } from '@backstage/errors';
function isNotFoundError(error: any): error is ResponseError {
return error.response.status === 404;
}
export class AzureDevOpsClient implements AzureDevOpsApi {
private readonly discoveryApi: DiscoveryApi;
private readonly identityApi: IdentityApi;
@@ -130,6 +136,23 @@ export class AzureDevOpsClient implements AzureDevOpsApi {
return { items };
}
public getReadme = async (
opts: ReadmeConfig,
): Promise<Readme | undefined> => {
try {
return await this.get(
`readme/${encodeURIComponent(opts.project)}/${encodeURIComponent(
opts.repo,
)}`,
);
} catch (e) {
if (isNotFoundError(e)) {
return undefined;
}
return e;
}
};
private async get<T>(path: string): Promise<T> {
const baseUrl = `${await this.discoveryApi.getBaseUrl('azure-devops')}/`;
const url = new URL(path, baseUrl);
@@ -0,0 +1,132 @@
/*
* Copyright 2022 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 { Button, makeStyles } from '@material-ui/core';
import { Alert } from '@material-ui/lab';
import {
InfoCard,
Progress,
MarkdownContent,
EmptyState,
} 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';
const useStyles = makeStyles(theme => ({
infoCard: {
marginBottom: theme.spacing(3),
'& + .MuiAlert-root': {
marginTop: theme.spacing(3),
},
'& .MuiCardContent-root': {
padding: theme.spacing(2, 1, 2, 2),
},
},
readMe: {
overflowY: 'auto',
paddingRight: theme.spacing(1),
'&::-webkit-scrollbar-track': {
backgroundColor: '#F5F5F5',
borderRadius: '5px',
},
'&::-webkit-scrollbar': {
width: '5px',
backgroundColor: '#F5F5F5',
borderRadius: '5px',
},
'&::-webkit-scrollbar-thumb': {
border: '1px solid #555555',
backgroundColor: '#555',
borderRadius: '4px',
},
},
}));
type Props = {
maxHeight?: number;
};
export const ReadMeCard = (props: Props) => {
const classes = useStyles();
const api = useApi(azureDevOpsApiRef);
const { entity } = useEntity();
const { project, repo } = useProjectRepoFromEntity(entity);
const { loading, error, value } = useAsync(
() =>
api.getReadme({
project,
repo,
}),
[api, project, repo, entity],
);
if (loading) {
return <Progress />;
} else if (error) {
return (
<Alert severity="error" className={classes.infoCard}>
{error.message}
</Alert>
);
}
if (!value)
return (
<EmptyState
title="No README available for this entity"
missing="field"
description="You can add a README to your entity by following the Azure DevOps documentation."
action={
<Button
variant="contained"
color="primary"
href="https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops"
>
Read more
</Button>
}
/>
);
return (
<InfoCard
title="Readme"
className={classes.infoCard}
deepLink={{
link: value!.url,
title: 'Readme',
onClick: e => {
e.preventDefault();
window.open(value?.url);
},
}}
>
<div
className={classes.readMe}
style={{
maxHeight: `${props.maxHeight}px`,
}}
>
<MarkdownContent content={value?.content ?? ''} />
</div>
</InfoCard>
);
};
@@ -0,0 +1,17 @@
/*
* Copyright 2022 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.
*/
export { ReadMeCard } from './ReadMeCard';
+1
View File
@@ -19,6 +19,7 @@ export {
EntityAzurePipelinesContent,
EntityAzureGitTagsContent,
EntityAzurePullRequestsContent,
EntityAzureReadMeContent,
isAzureDevOpsAvailable,
isAzurePipelinesAvailable,
AzurePullRequestsPage,