Made changes requested in the pull request
Signed-off-by: Alisson Fabiano <afabiano@eshopworld.com>
This commit is contained in:
@@ -1,7 +1,76 @@
|
||||
---
|
||||
'@backstage/plugin-azure-devops': patch
|
||||
'@backstage/plugin-azure-devops-backend': patch
|
||||
'@backstage/plugin-azure-devops-common': patch
|
||||
'@backstage/plugin-azure-devops': minor
|
||||
'@backstage/plugin-azure-devops-backend': minor
|
||||
'@backstage/plugin-azure-devops-common': minor
|
||||
---
|
||||
|
||||
Added README card for Azure Devops
|
||||
|
||||
**Before:**
|
||||
|
||||
Remember to check if you have already provided these settings previously, as we will need them for the reame card to work.
|
||||
|
||||
#### [Azure DevOps](https://github.com/backstage/backstage/blob/master/app-config.yaml#L48L51:~:text=azureDevOps%3A,%3A%20my%2Dcompany)
|
||||
|
||||
```yaml
|
||||
# app-config.yaml
|
||||
azureDevOps:
|
||||
host: dev.azure.com
|
||||
token: my-token
|
||||
organization: my-company
|
||||
```
|
||||
|
||||
#### [Azure Integrations](https://github.com/backstage/backstage/blob/master/app-config.yaml#L48L51:~:text=azure%3A,%3A%20%24%7BAZURE_TOKEN%7D)
|
||||
|
||||
```yaml
|
||||
# app-config.yaml
|
||||
integrations:
|
||||
azure:
|
||||
- host: dev.azure.com
|
||||
token: ${AZURE_TOKEN}
|
||||
```
|
||||
|
||||
**After:**
|
||||
|
||||
To get the README component working you'll need to do the following two steps:
|
||||
|
||||
1. First we need to add the @backstage/plugin-azure-devops package to your frontend app:
|
||||
|
||||
```bash
|
||||
# From your Backstage root directory
|
||||
yarn add --cwd packages/app @backstage/plugin-azure-devops
|
||||
```
|
||||
|
||||
2. Second we need to add the `EntityAzureReadmeCard` extension to the entity page in your app:
|
||||
|
||||
```tsx
|
||||
// In packages/app/src/components/catalog/EntityPage.tsx
|
||||
import {
|
||||
EntityAzureReadmeCard,
|
||||
isAzureDevOpsAvailable,
|
||||
} from '@backstage/plugin-azure-devops';
|
||||
|
||||
// As it is a card, you can customize it the way you prefer
|
||||
// For example in the Service section
|
||||
|
||||
const overviewContent = (
|
||||
<Grid container spacing={3} alignItems="stretch">
|
||||
<EntitySwitch>
|
||||
<EntitySwitch.Case if={isAzureDevOpsAvailable}>
|
||||
<Grid item md={6}>
|
||||
...
|
||||
</Grid>
|
||||
<Grid item md={6}>
|
||||
<EntityAzureReadmeCard maxHeight={350} />
|
||||
</Grid>
|
||||
</EntitySwitch.Case>
|
||||
</EntitySwitch>
|
||||
</Grid>
|
||||
);
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
- You'll need to add the `EntitySwitch.Case` above from step 2 to all the entity sections you want to see Readme in. For example if you wanted to see Readme when looking at Website entities then you would need to add this to the `websiteEntityPage` section.
|
||||
- The `if` prop is optional on the `EntitySwitch.Case`, you can remove it if you always want to see the tab even if the entity being viewed does not have the needed annotation
|
||||
- The `maxHeight` property on the `EntityAzureReadmeCard` will set the maximum screen size you would like to see, if not set it will default to 100%
|
||||
|
||||
@@ -24,5 +24,6 @@ export default async function createPlugin(
|
||||
return createRouter({
|
||||
logger: env.logger,
|
||||
config: env.config,
|
||||
reader: env.reader,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@
|
||||
"express-promise-router": "^4.1.0",
|
||||
"p-limit": "^3.1.0",
|
||||
"winston": "^3.2.1",
|
||||
"yn": "^4.0.0"
|
||||
"yn": "^4.0.0",
|
||||
"mime-types": "^2.1.27"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.18.1-next.0",
|
||||
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
convertPolicy,
|
||||
getArtifactId,
|
||||
replaceReadme,
|
||||
buildEncodedUrl,
|
||||
} from '../utils';
|
||||
|
||||
import { TeamMember as AdoTeamMember } from 'azure-devops-node-api/interfaces/common/VSSInterfaces';
|
||||
@@ -399,32 +400,24 @@ export class AzureDevOpsApi {
|
||||
|
||||
public async getReadme(
|
||||
host: string,
|
||||
organization: string,
|
||||
projectName: string,
|
||||
repoName: string,
|
||||
org: string,
|
||||
project: string,
|
||||
repo: string,
|
||||
): Promise<{
|
||||
url: string;
|
||||
content: string;
|
||||
}> {
|
||||
const getFileContent = async (
|
||||
path: string,
|
||||
encoding?: BufferEncoding,
|
||||
): Promise<{
|
||||
url: string;
|
||||
content: string;
|
||||
}> => {
|
||||
const url = `https://${host}/${organization}/${projectName}/_git/${repoName}?path=${path}`;
|
||||
const response = await this.urlReader.read(url);
|
||||
return {
|
||||
url,
|
||||
content: Buffer.from(response).toString(encoding),
|
||||
};
|
||||
};
|
||||
const { url, content } = await getFileContent('README.md');
|
||||
return {
|
||||
url,
|
||||
content: await replaceReadme(content, getFileContent),
|
||||
};
|
||||
const url = buildEncodedUrl(host, org, project, repo, 'README.md');
|
||||
const response = await this.urlReader.read(url);
|
||||
const content = await replaceReadme(
|
||||
this.urlReader,
|
||||
host,
|
||||
org,
|
||||
project,
|
||||
repo,
|
||||
response.toString(),
|
||||
);
|
||||
return { url, content };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ import { ConfigReader } from '@backstage/config';
|
||||
import { GitRepository } from 'azure-devops-node-api/interfaces/GitInterfaces';
|
||||
import { createRouter } from './router';
|
||||
import express from 'express';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { getVoidLogger, UrlReaders } from '@backstage/backend-common';
|
||||
import request from 'supertest';
|
||||
|
||||
describe('createRouter', () => {
|
||||
@@ -55,18 +55,28 @@ describe('createRouter', () => {
|
||||
getTeamMembers: jest.fn(),
|
||||
getReadme: jest.fn(),
|
||||
} as any;
|
||||
|
||||
const config = new ConfigReader({
|
||||
azureDevOps: {
|
||||
token: 'foo',
|
||||
host: 'host.com',
|
||||
organization: 'myOrg',
|
||||
top: 5,
|
||||
},
|
||||
});
|
||||
|
||||
const logger = getVoidLogger();
|
||||
|
||||
const router = await createRouter({
|
||||
config,
|
||||
logger,
|
||||
azureDevOpsApi,
|
||||
logger: getVoidLogger(),
|
||||
config: new ConfigReader({
|
||||
azureDevOps: {
|
||||
token: 'foo',
|
||||
host: 'host.com',
|
||||
organization: 'myOrg',
|
||||
top: 5,
|
||||
},
|
||||
reader: UrlReaders.default({
|
||||
config,
|
||||
logger,
|
||||
}),
|
||||
});
|
||||
|
||||
app = express().use(router);
|
||||
});
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import { Config } from '@backstage/config';
|
||||
import { Logger } from 'winston';
|
||||
import { PullRequestsDashboardProvider } from '../api/PullRequestsDashboardProvider';
|
||||
import Router from 'express-promise-router';
|
||||
import { errorHandler, UrlReaders } from '@backstage/backend-common';
|
||||
import { errorHandler, UrlReader } from '@backstage/backend-common';
|
||||
import express from 'express';
|
||||
|
||||
const DEFAULT_TOP = 10;
|
||||
@@ -35,18 +35,18 @@ export interface RouterOptions {
|
||||
azureDevOpsApi?: AzureDevOpsApi;
|
||||
logger: Logger;
|
||||
config: Config;
|
||||
reader: UrlReader;
|
||||
}
|
||||
|
||||
export async function createRouter(
|
||||
options: RouterOptions,
|
||||
): Promise<express.Router> {
|
||||
const { logger } = options;
|
||||
const { logger, reader } = options;
|
||||
const config = options.config.getConfig('azureDevOps');
|
||||
|
||||
const token = config.getString('token');
|
||||
const host = config.getString('host');
|
||||
const organization = config.getString('organization');
|
||||
const reader = UrlReaders.default(options);
|
||||
|
||||
const authHandler = getPersonalAccessTokenHandler(token);
|
||||
const webApi = new WebApi(`https://${host}/${organization}`, authHandler);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import {
|
||||
createServiceBuilder,
|
||||
loadBackendConfig,
|
||||
UrlReaders,
|
||||
} from '@backstage/backend-common';
|
||||
import { Server } from 'http';
|
||||
import { Logger } from 'winston';
|
||||
@@ -39,6 +40,7 @@ export async function startStandaloneServer(
|
||||
const router = await createRouter({
|
||||
logger,
|
||||
config,
|
||||
reader: UrlReaders.default({ logger, config }),
|
||||
});
|
||||
|
||||
let service = createServiceBuilder(module)
|
||||
|
||||
@@ -25,12 +25,11 @@ import {
|
||||
extractPartsFromAsset,
|
||||
getArtifactId,
|
||||
getAvatarUrl,
|
||||
getMimeByExtension,
|
||||
getPullRequestLink,
|
||||
replaceReadme,
|
||||
} from './azure-devops-utils';
|
||||
|
||||
import { GitPullRequest } from 'azure-devops-node-api/interfaces/GitInterfaces';
|
||||
import { UrlReader } from '@backstage/backend-common';
|
||||
|
||||
describe('convertDashboardPullRequest', () => {
|
||||
it('should return DashboardPullRequest', () => {
|
||||
@@ -236,16 +235,6 @@ describe('extractPartsFromAsset', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMimeByExtension', () => {
|
||||
it('should return mime type', () => {
|
||||
expect(getMimeByExtension('.png')).toBe('image/png');
|
||||
expect(getMimeByExtension('.jpg')).toBe('image/jpeg');
|
||||
expect(getMimeByExtension('.jpeg')).toBe('image/jpeg');
|
||||
expect(getMimeByExtension('.webp')).toBe('image/webp');
|
||||
expect(getMimeByExtension('.gif')).toBe('image/gif');
|
||||
});
|
||||
});
|
||||
|
||||
describe('replaceReadme', () => {
|
||||
it('should return mime type', async () => {
|
||||
const readme = `
|
||||
@@ -257,31 +246,29 @@ describe('replaceReadme', () => {
|
||||
mple.gif)
|
||||
`;
|
||||
|
||||
async function mockFileContent(
|
||||
path: string,
|
||||
encoding?: BufferEncoding,
|
||||
): Promise<{
|
||||
url: string;
|
||||
content: string;
|
||||
}> {
|
||||
expect(encoding).toBe('base64');
|
||||
return new Promise(resolve =>
|
||||
resolve({
|
||||
url: '',
|
||||
content: Buffer.from(path).toString(encoding),
|
||||
}),
|
||||
);
|
||||
}
|
||||
const reader: UrlReader = {
|
||||
read: url => new Promise<Buffer>(resolve => resolve(Buffer.from(url))),
|
||||
readTree: jest.fn(),
|
||||
search: jest.fn(),
|
||||
readUrl: jest.fn(),
|
||||
};
|
||||
|
||||
const result = await replaceReadme(readme, mockFileContent);
|
||||
const result = await replaceReadme(
|
||||
reader,
|
||||
'host',
|
||||
'org',
|
||||
'project',
|
||||
'repo',
|
||||
readme,
|
||||
);
|
||||
|
||||
const expected = `
|
||||
## Images
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
`;
|
||||
|
||||
expect(expected).toBe(result);
|
||||
|
||||
@@ -30,9 +30,11 @@ import {
|
||||
GitRepository,
|
||||
IdentityRefWithVote,
|
||||
} from 'azure-devops-node-api/interfaces/GitInterfaces';
|
||||
import mime from 'mime-types';
|
||||
|
||||
import { IdentityRef } from 'azure-devops-node-api/interfaces/common/VSSInterfaces';
|
||||
import { PolicyEvaluationRecord } from 'azure-devops-node-api/interfaces/PolicyInterfaces';
|
||||
import { UrlReader } from '@backstage/backend-common';
|
||||
|
||||
export function convertDashboardPullRequest(
|
||||
pullRequest: GitPullRequest,
|
||||
@@ -206,31 +208,47 @@ export function convertPolicy(
|
||||
}
|
||||
|
||||
export async function replaceReadme(
|
||||
readme: string,
|
||||
getFileContent: (
|
||||
path: string,
|
||||
encoding?: BufferEncoding,
|
||||
) => Promise<{
|
||||
url: string;
|
||||
content: string;
|
||||
}>,
|
||||
urlReader: UrlReader,
|
||||
host: string,
|
||||
org: string,
|
||||
project: string,
|
||||
repo: string,
|
||||
readmeContent: string,
|
||||
) {
|
||||
const filesPath = extractAssets(readme);
|
||||
const filesPath = extractAssets(readmeContent);
|
||||
if (!filesPath) return readmeContent;
|
||||
return await filesPath.reduce(
|
||||
async (promise: Promise<string>, filePath: string) =>
|
||||
promise.then(async content => {
|
||||
const { label, path, ext } = extractPartsFromAsset(filePath);
|
||||
const mime = getMimeByExtension(ext);
|
||||
const file = await getFileContent(path + ext, 'base64');
|
||||
const data = mime.lookup(ext);
|
||||
const url = buildEncodedUrl(host, org, project, repo, path + ext);
|
||||
const buffer = await urlReader.read(url);
|
||||
const file = await buffer.toString('base64');
|
||||
return content.replace(
|
||||
filePath,
|
||||
`[${label}](data:${mime};base64,${file.content})`,
|
||||
`[${label}](data:${data};base64,${file})`,
|
||||
);
|
||||
}),
|
||||
Promise.resolve(readme),
|
||||
Promise.resolve(readmeContent),
|
||||
);
|
||||
}
|
||||
|
||||
export function buildEncodedUrl(
|
||||
host: string,
|
||||
org: string,
|
||||
project: string,
|
||||
repo: string,
|
||||
path: string,
|
||||
): string {
|
||||
const encodedHost = encodeURIComponent(host);
|
||||
const encodedOrg = encodeURIComponent(org);
|
||||
const encodedProject = encodeURIComponent(project);
|
||||
const encodedRepo = encodeURIComponent(repo);
|
||||
const encodedPath = encodeURIComponent(path);
|
||||
return `https://${encodedHost}/${encodedOrg}/${encodedProject}/_git/${encodedRepo}?path=${encodedPath}`;
|
||||
}
|
||||
|
||||
function convertReviewer(
|
||||
identityRef?: IdentityRefWithVote,
|
||||
): Reviewer | undefined {
|
||||
@@ -293,7 +311,7 @@ function hasAutoComplete(pullRequest: GitPullRequest): boolean {
|
||||
export function extractAssets(content: string) {
|
||||
const regExp =
|
||||
/\[([^\[\]]*)\]\((?!https?:\/\/)(.*?)(\.png|\.jpg|\.jpeg|\.gif|\.webp)(.*)\)/gim;
|
||||
return content.match(regExp) || [];
|
||||
return content.match(regExp);
|
||||
}
|
||||
|
||||
export function extractPartsFromAsset(content: string): {
|
||||
@@ -310,15 +328,3 @@ export function extractPartsFromAsset(content: string): {
|
||||
path: path.startsWith('.') ? path.substring(1, path.length) : path,
|
||||
};
|
||||
}
|
||||
|
||||
export function getMimeByExtension(ext: string): string {
|
||||
return (
|
||||
{
|
||||
'.png': 'image/png',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.jpeg': 'image/jpeg',
|
||||
'.gif': 'image/gif',
|
||||
'.webp': 'image/webp',
|
||||
}[ext] || ''
|
||||
);
|
||||
}
|
||||
|
||||
@@ -67,5 +67,5 @@ export interface AzureDevOpsApi {
|
||||
options?: BuildRunOptions,
|
||||
): Promise<{ items: BuildRun[] }>;
|
||||
|
||||
getReadme(opts: ReadmeConfig): Promise<Readme | undefined>;
|
||||
getReadme(opts: ReadmeConfig): Promise<Readme>;
|
||||
}
|
||||
|
||||
@@ -32,10 +32,6 @@ 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;
|
||||
@@ -136,22 +132,13 @@ 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;
|
||||
}
|
||||
};
|
||||
public async getReadme(opts: ReadmeConfig): Promise<Readme> {
|
||||
return await this.get(
|
||||
`readme/${encodeURIComponent(opts.project)}/${encodeURIComponent(
|
||||
opts.repo,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
|
||||
private async get<T>(path: string): Promise<T> {
|
||||
const baseUrl = `${await this.discoveryApi.getBaseUrl('azure-devops')}/`;
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, makeStyles } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import { Box, Button, makeStyles } from '@material-ui/core';
|
||||
import {
|
||||
InfoCard,
|
||||
Progress,
|
||||
MarkdownContent,
|
||||
EmptyState,
|
||||
ErrorPanel,
|
||||
} from '@backstage/core-components';
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
import { useProjectRepoFromEntity } from '../../hooks';
|
||||
@@ -30,15 +30,6 @@ 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),
|
||||
@@ -63,6 +54,35 @@ type Props = {
|
||||
maxHeight?: number;
|
||||
};
|
||||
|
||||
type ErrorProps = {
|
||||
error: Error;
|
||||
};
|
||||
|
||||
function isNotFoundError(error: any): boolean {
|
||||
return error?.response?.status === 404;
|
||||
}
|
||||
|
||||
const ReadmeCardError = ({ error }: ErrorProps) => {
|
||||
if (isNotFoundError(error))
|
||||
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 <ErrorPanel title={error.message} error={error} />;
|
||||
};
|
||||
|
||||
export const ReadmeCard = (props: Props) => {
|
||||
const classes = useStyles();
|
||||
const api = useApi(azureDevOpsApiRef);
|
||||
@@ -81,52 +101,20 @@ export const ReadmeCard = (props: Props) => {
|
||||
if (loading) {
|
||||
return <Progress />;
|
||||
} else if (error) {
|
||||
return (
|
||||
<Alert severity="error" className={classes.infoCard}>
|
||||
{error.message}
|
||||
</Alert>
|
||||
);
|
||||
return <ReadmeCardError error={error} />;
|
||||
}
|
||||
|
||||
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`,
|
||||
}}
|
||||
>
|
||||
<Box className={classes.readMe} sx={{ maxHeight: props.maxHeight }}>
|
||||
<MarkdownContent content={value?.content ?? ''} />
|
||||
</div>
|
||||
</Box>
|
||||
</InfoCard>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -24,12 +24,12 @@ import {
|
||||
azurePullRequestDashboardRouteRef,
|
||||
azureGitTagsEntityContentRouteRef,
|
||||
azurePullRequestsEntityContentRouteRef,
|
||||
azureReadmeEntityCardRouteRef,
|
||||
} from './routes';
|
||||
import {
|
||||
createApiFactory,
|
||||
createPlugin,
|
||||
createRoutableExtension,
|
||||
createComponentExtension,
|
||||
discoveryApiRef,
|
||||
identityApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
@@ -103,9 +103,10 @@ export const EntityAzurePullRequestsContent = azureDevOpsPlugin.provide(
|
||||
);
|
||||
|
||||
export const EntityAzureReadmeCard = azureDevOpsPlugin.provide(
|
||||
createRoutableExtension({
|
||||
createComponentExtension({
|
||||
name: 'EntityAzureReadmeCard',
|
||||
component: () => import('./components/ReadmeCard').then(m => m.ReadmeCard),
|
||||
mountPoint: azureReadmeEntityCardRouteRef,
|
||||
component: {
|
||||
lazy: () => import('./components/ReadmeCard').then(m => m.ReadmeCard),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -31,7 +31,3 @@ export const azureGitTagsEntityContentRouteRef = createRouteRef({
|
||||
export const azurePullRequestsEntityContentRouteRef = createRouteRef({
|
||||
id: 'azure-pull-requests-entity-content',
|
||||
});
|
||||
|
||||
export const azureReadmeEntityCardRouteRef = createRouteRef({
|
||||
id: 'azure-readme-entity-card',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user