@@ -1 +0,0 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
# Deprecated
|
||||
|
||||
This package has been moved to the [backstage-community/plugins](https://github.com/backstage/community-plugins) repository. Migrate to using `@backstage-community/plugin-adr-backend` instead.
|
||||
@@ -1,87 +0,0 @@
|
||||
## API Report File for "@backstage/plugin-adr-backend"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
/// <reference types="node" />
|
||||
|
||||
import { AdrDocument } from '@backstage/plugin-adr-common';
|
||||
import { AdrFilePathFilterFn } from '@backstage/plugin-adr-common';
|
||||
import { AuthService } from '@backstage/backend-plugin-api';
|
||||
import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { CacheClient } from '@backstage/backend-common';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { Config } from '@backstage/config';
|
||||
import { DocumentCollatorFactory } from '@backstage/plugin-search-common';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import express from 'express';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { PluginCacheManager } from '@backstage/backend-common';
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import { Readable } from 'stream';
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
import { UrlReader } from '@backstage/backend-common';
|
||||
|
||||
// @public
|
||||
export type AdrCollatorFactoryOptions = {
|
||||
adrFilePathFilterFn?: AdrFilePathFilterFn;
|
||||
cache: PluginCacheManager;
|
||||
config: Config;
|
||||
catalogClient?: CatalogApi;
|
||||
discovery: PluginEndpointDiscovery;
|
||||
logger: LoggerService;
|
||||
parser?: AdrParser;
|
||||
reader: UrlReader;
|
||||
tokenManager: TokenManager;
|
||||
auth?: AuthService;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AdrParser = (ctx: AdrParserContext) => Promise<AdrDocument>;
|
||||
|
||||
// @public
|
||||
export type AdrParserContext = {
|
||||
entity: Entity;
|
||||
content: string;
|
||||
path: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
const adrPlugin: () => BackendFeature;
|
||||
export default adrPlugin;
|
||||
|
||||
// @public (undocumented)
|
||||
export type AdrRouterOptions = {
|
||||
reader: UrlReader;
|
||||
cacheClient: CacheClient;
|
||||
logger: LoggerService;
|
||||
};
|
||||
|
||||
// @public
|
||||
export const createMadrParser: (options?: MadrParserOptions) => AdrParser;
|
||||
|
||||
// @public (undocumented)
|
||||
export function createRouter(
|
||||
options: AdrRouterOptions,
|
||||
): Promise<express.Router>;
|
||||
|
||||
// @public
|
||||
export class DefaultAdrCollatorFactory implements DocumentCollatorFactory {
|
||||
// (undocumented)
|
||||
execute(): AsyncGenerator<AdrDocument>;
|
||||
// (undocumented)
|
||||
static fromConfig(
|
||||
options: AdrCollatorFactoryOptions,
|
||||
): DefaultAdrCollatorFactory;
|
||||
// (undocumented)
|
||||
getCollator(): Promise<Readable>;
|
||||
// (undocumented)
|
||||
readonly type: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type MadrParserOptions = {
|
||||
locationTemplate?: string;
|
||||
dateFormat?: string;
|
||||
};
|
||||
```
|
||||
@@ -1,9 +0,0 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: backstage-plugin-adr-backend
|
||||
title: '@backstage/plugin-adr-backend'
|
||||
spec:
|
||||
lifecycle: experimental
|
||||
type: backstage-backend-plugin
|
||||
owner: kuangp
|
||||
@@ -1,9 +0,0 @@
|
||||
# Knip report
|
||||
|
||||
## Unused dependencies (2)
|
||||
|
||||
| Name | Location | Severity |
|
||||
| :--------- | :----------- | :------- |
|
||||
| node-fetch | package.json | error |
|
||||
| yn | package.json | error |
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
{
|
||||
"name": "@backstage/plugin-adr-backend",
|
||||
"version": "0.4.14",
|
||||
"backstage": {
|
||||
"role": "backend-plugin",
|
||||
"moved": "@backstage-community/plugin-adr-backend"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "plugins/adr-backend"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
"clean": "backstage-cli package clean",
|
||||
"lint": "backstage-cli package lint",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack",
|
||||
"start": "backstage-cli package start",
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "workspace:^",
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/catalog-client": "workspace:^",
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
"@backstage/config": "workspace:^",
|
||||
"@backstage/errors": "workspace:^",
|
||||
"@backstage/integration": "workspace:^",
|
||||
"@backstage/plugin-adr-common": "workspace:^",
|
||||
"@backstage/plugin-search-common": "workspace:^",
|
||||
"@types/express": "^4.17.6",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"luxon": "^3.0.0",
|
||||
"marked": "^12.0.0",
|
||||
"node-fetch": "^2.6.5",
|
||||
"yn": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"supertest": "^6.1.3"
|
||||
},
|
||||
"deprecated": "This package has been moved to the backstage/community-plugins repository. You should migrate to using @backstage-community/plugin-adr-backend instead."
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Architecture Decision Records backend plugin.
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export * from './search';
|
||||
export * from './service';
|
||||
export { adrPlugin as default } from './plugin';
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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 {
|
||||
coreServices,
|
||||
createBackendPlugin,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { createRouter } from './service/router';
|
||||
|
||||
/**
|
||||
* ADR backend plugin
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const adrPlugin = createBackendPlugin({
|
||||
pluginId: 'adr',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: {
|
||||
logger: coreServices.logger,
|
||||
reader: coreServices.urlReader,
|
||||
cache: coreServices.cache,
|
||||
httpRouter: coreServices.httpRouter,
|
||||
},
|
||||
async init({ httpRouter, logger, reader, cache }) {
|
||||
httpRouter.use(
|
||||
await createRouter({
|
||||
logger,
|
||||
reader,
|
||||
cacheClient: cache,
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -1,228 +0,0 @@
|
||||
/*
|
||||
* 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 { Readable } from 'stream';
|
||||
import {
|
||||
CacheClient,
|
||||
createLegacyAuthAdapters,
|
||||
PluginCacheManager,
|
||||
PluginEndpointDiscovery,
|
||||
TokenManager,
|
||||
UrlReader,
|
||||
} from '@backstage/backend-common';
|
||||
import {
|
||||
CATALOG_FILTER_EXISTS,
|
||||
CatalogApi,
|
||||
CatalogClient,
|
||||
} from '@backstage/catalog-client';
|
||||
import { stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
import { NotModifiedError, stringifyError } from '@backstage/errors';
|
||||
import {
|
||||
ScmIntegrationRegistry,
|
||||
ScmIntegrations,
|
||||
} from '@backstage/integration';
|
||||
import {
|
||||
AdrDocument,
|
||||
AdrFilePathFilterFn,
|
||||
ANNOTATION_ADR_LOCATION,
|
||||
getAdrLocationUrl,
|
||||
madrFilePathFilter,
|
||||
} from '@backstage/plugin-adr-common';
|
||||
import { DocumentCollatorFactory } from '@backstage/plugin-search-common';
|
||||
|
||||
import { createMadrParser } from './createMadrParser';
|
||||
import { AdrParser } from './types';
|
||||
import { AuthService, LoggerService } from '@backstage/backend-plugin-api';
|
||||
|
||||
/**
|
||||
* Options to configure the AdrCollatorFactory
|
||||
* @public
|
||||
*/
|
||||
export type AdrCollatorFactoryOptions = {
|
||||
/**
|
||||
* Function used to filter ADR file paths.
|
||||
* Defaults to filtering paths following MADR filename format.
|
||||
*/
|
||||
adrFilePathFilterFn?: AdrFilePathFilterFn;
|
||||
/**
|
||||
* Plugin cache manager
|
||||
*/
|
||||
cache: PluginCacheManager;
|
||||
/**
|
||||
* App Config
|
||||
*/
|
||||
config: Config;
|
||||
/**
|
||||
* Catalog API client. Defaults to CatalogClient.
|
||||
*/
|
||||
catalogClient?: CatalogApi;
|
||||
/**
|
||||
* Plugin Endpoint Discovery client
|
||||
*/
|
||||
discovery: PluginEndpointDiscovery;
|
||||
/**
|
||||
* Logger
|
||||
*/
|
||||
logger: LoggerService;
|
||||
/**
|
||||
* ADR content parser. Defaults to built in MADR parser.
|
||||
*/
|
||||
parser?: AdrParser;
|
||||
/**
|
||||
* URL Reader
|
||||
*/
|
||||
reader: UrlReader;
|
||||
/**
|
||||
* Token Manager
|
||||
*/
|
||||
tokenManager: TokenManager;
|
||||
/**
|
||||
* Auth Service
|
||||
*/
|
||||
auth?: AuthService;
|
||||
};
|
||||
|
||||
/**
|
||||
* Default collator to index ADR documents for Backstage search.
|
||||
* @public
|
||||
*/
|
||||
export class DefaultAdrCollatorFactory implements DocumentCollatorFactory {
|
||||
public readonly type: string = 'adr';
|
||||
private readonly adrFilePathFilterFn: AdrFilePathFilterFn;
|
||||
private readonly cacheClient: CacheClient;
|
||||
private readonly catalogClient: CatalogApi;
|
||||
private readonly logger: LoggerService;
|
||||
private readonly parser: AdrParser;
|
||||
private readonly reader: UrlReader;
|
||||
private readonly auth: AuthService;
|
||||
private readonly scmIntegrations: ScmIntegrationRegistry;
|
||||
|
||||
private constructor(options: AdrCollatorFactoryOptions) {
|
||||
this.adrFilePathFilterFn =
|
||||
options.adrFilePathFilterFn ?? madrFilePathFilter;
|
||||
this.cacheClient = options.cache.getClient();
|
||||
this.catalogClient =
|
||||
options.catalogClient ??
|
||||
new CatalogClient({ discoveryApi: options.discovery });
|
||||
this.logger = options.logger.child({ documentType: this.type });
|
||||
this.parser = options.parser ?? createMadrParser();
|
||||
this.reader = options.reader;
|
||||
this.scmIntegrations = ScmIntegrations.fromConfig(options.config);
|
||||
|
||||
this.auth = createLegacyAuthAdapters(options).auth;
|
||||
}
|
||||
|
||||
static fromConfig(options: AdrCollatorFactoryOptions) {
|
||||
return new DefaultAdrCollatorFactory(options);
|
||||
}
|
||||
|
||||
async getCollator() {
|
||||
return Readable.from(this.execute());
|
||||
}
|
||||
|
||||
async *execute(): AsyncGenerator<AdrDocument> {
|
||||
const { token } = await this.auth.getPluginRequestToken({
|
||||
onBehalfOf: await this.auth.getOwnServiceCredentials(),
|
||||
targetPluginId: 'catalog',
|
||||
});
|
||||
|
||||
const entities = (
|
||||
await this.catalogClient.getEntities(
|
||||
{
|
||||
filter: {
|
||||
[`metadata.annotations.${ANNOTATION_ADR_LOCATION}`]:
|
||||
CATALOG_FILTER_EXISTS,
|
||||
},
|
||||
fields: [
|
||||
'kind',
|
||||
'metadata.annotations',
|
||||
'metadata.name',
|
||||
'metadata.namespace',
|
||||
'metadata.title',
|
||||
],
|
||||
},
|
||||
{ token },
|
||||
)
|
||||
).items;
|
||||
|
||||
for (const ent of entities) {
|
||||
let adrsUrl: string;
|
||||
try {
|
||||
adrsUrl = getAdrLocationUrl(ent, this.scmIntegrations);
|
||||
} catch (e: any) {
|
||||
this.logger.error(
|
||||
`Unable to get ADR location URL for ${stringifyEntityRef(
|
||||
ent,
|
||||
)}: ${stringifyError(e)}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const cacheItem = (await this.cacheClient.get(adrsUrl)) as {
|
||||
adrFiles: {
|
||||
path: string;
|
||||
content: string;
|
||||
}[];
|
||||
etag: string;
|
||||
};
|
||||
|
||||
let adrFiles = cacheItem?.adrFiles;
|
||||
try {
|
||||
const tree = await this.reader.readTree(adrsUrl, {
|
||||
etag: cacheItem?.etag,
|
||||
filter: filePath => this.adrFilePathFilterFn(filePath),
|
||||
});
|
||||
|
||||
adrFiles = await Promise.all(
|
||||
(
|
||||
await tree.files()
|
||||
).map(async f => ({
|
||||
path: f.path,
|
||||
content: (await f.content()).toString('utf8'),
|
||||
})),
|
||||
);
|
||||
|
||||
await this.cacheClient.set(adrsUrl, { adrFiles, etag: tree.etag });
|
||||
} catch (error: any) {
|
||||
// Ignore error if we're able to use cached response
|
||||
if (!cacheItem || error.name !== NotModifiedError.name) {
|
||||
this.logger.error(
|
||||
`Unable to fetch ADRs from ${adrsUrl}: ${stringifyError(error)}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (const { content, path } of adrFiles) {
|
||||
try {
|
||||
const adrDoc = await this.parser({
|
||||
entity: ent,
|
||||
path,
|
||||
content,
|
||||
});
|
||||
yield adrDoc;
|
||||
} catch (e: any) {
|
||||
this.logger.error(
|
||||
`Unable to parse ADR ${path}: ${stringifyError(e)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.info(`Indexed ${adrFiles.length} ADRs from ${adrsUrl}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* 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 { stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { MADR_DATE_FORMAT } from '@backstage/plugin-adr-common';
|
||||
|
||||
import { AdrParser } from './types';
|
||||
import { madrParser } from './madrParser';
|
||||
|
||||
const applyArgsToFormat = (
|
||||
format: string,
|
||||
args: Record<string, string>,
|
||||
): string => {
|
||||
let formatted = format;
|
||||
for (const [key, value] of Object.entries(args)) {
|
||||
formatted = formatted.replace(`:${key}`, value);
|
||||
}
|
||||
return formatted.toLowerCase();
|
||||
};
|
||||
|
||||
const DEFAULT_LOCATION_TEMPLATE =
|
||||
'/catalog/:namespace/:kind/:name/adrs?record=:record';
|
||||
|
||||
/**
|
||||
*
|
||||
* Options for the default MADR content parser
|
||||
* @public
|
||||
*/
|
||||
export type MadrParserOptions = {
|
||||
/**
|
||||
* Location template for the route of the frontend plugin
|
||||
* Defaults to '/catalog/:namespace/:kind/:name/adrs?record=:record'
|
||||
*/
|
||||
locationTemplate?: string;
|
||||
/**
|
||||
* luxon DateTime format string to parse ADR dates with.
|
||||
* Defaults to 'yyyy-MM-dd'
|
||||
*/
|
||||
dateFormat?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Default content parser for ADRs following the MADR template (https://adr.github.io/madr/)
|
||||
* @public
|
||||
*/
|
||||
export const createMadrParser = (
|
||||
options: MadrParserOptions = {},
|
||||
): AdrParser => {
|
||||
const locationTemplate =
|
||||
options.locationTemplate ?? DEFAULT_LOCATION_TEMPLATE;
|
||||
const dateFormat = options.dateFormat ?? MADR_DATE_FORMAT;
|
||||
|
||||
return async ({ entity, content, path }) => {
|
||||
const madr = madrParser(content, dateFormat);
|
||||
return {
|
||||
title: madr.title ?? path.replace(/\.md$/, ''),
|
||||
text: content,
|
||||
status: madr.status,
|
||||
date: madr.date,
|
||||
entityRef: stringifyEntityRef(entity),
|
||||
entityTitle: entity.metadata.title,
|
||||
location: applyArgsToFormat(locationTemplate, {
|
||||
namespace: entity.metadata.namespace || 'default',
|
||||
kind: entity.kind,
|
||||
name: entity.metadata.name,
|
||||
record: path,
|
||||
}),
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* 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 * from './DefaultAdrCollatorFactory';
|
||||
export * from './createMadrParser';
|
||||
export * from './types';
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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 { DateTime } from 'luxon';
|
||||
import { marked, Tokens, TokensList } from 'marked';
|
||||
import {
|
||||
MADR_DATE_FORMAT,
|
||||
parseMadrWithFrontmatter,
|
||||
} from '@backstage/plugin-adr-common';
|
||||
|
||||
const getTitle = (tokens: TokensList): string | undefined => {
|
||||
return (
|
||||
tokens.find(t => t.type === 'heading' && t.depth === 1) as Tokens.Heading
|
||||
)?.text;
|
||||
};
|
||||
|
||||
const getStatusForV2Format = (tokens: TokensList): string | undefined =>
|
||||
(tokens.find(t => t.type === 'list') as Tokens.List)?.items
|
||||
?.find(t => /^status:/i.test(t.text))
|
||||
?.text.replace(/^status:/i, '')
|
||||
.trim()
|
||||
.toLocaleLowerCase('en-US');
|
||||
|
||||
const getDateForV2Format = (tokens: TokensList): string | undefined => {
|
||||
const listTokens = (tokens.find(t => t.type === 'list') as Tokens.List)
|
||||
?.items;
|
||||
const adrDateTime = listTokens
|
||||
?.find(t => /^date:/i.test(t.text))
|
||||
?.text.replace(/^date:/i, '')
|
||||
.trim();
|
||||
return adrDateTime;
|
||||
};
|
||||
|
||||
const getStatus = (
|
||||
tokens: TokensList,
|
||||
frontMatterStatus?: string,
|
||||
): string | undefined => {
|
||||
return frontMatterStatus ?? getStatusForV2Format(tokens);
|
||||
};
|
||||
const getDate = (
|
||||
tokens: TokensList,
|
||||
dateFormat: string,
|
||||
frontMatterDate?: string,
|
||||
): string | undefined => {
|
||||
const dateString = frontMatterDate ?? getDateForV2Format(tokens);
|
||||
if (!dateString) {
|
||||
return undefined;
|
||||
}
|
||||
const date = DateTime.fromFormat(dateString, dateFormat);
|
||||
return date?.isValid ? date.toFormat(MADR_DATE_FORMAT) : undefined;
|
||||
};
|
||||
|
||||
export const madrParser = (content: string, dateFormat = MADR_DATE_FORMAT) => {
|
||||
const preparsed = parseMadrWithFrontmatter(content);
|
||||
const tokens = marked.lexer(preparsed.content);
|
||||
if (!tokens.length) {
|
||||
throw new Error('ADR has no content');
|
||||
}
|
||||
|
||||
return {
|
||||
title: getTitle(tokens),
|
||||
status: getStatus(tokens, preparsed.status),
|
||||
date: getDate(tokens, dateFormat, preparsed.date),
|
||||
};
|
||||
};
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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 { Entity } from '@backstage/catalog-model';
|
||||
import { AdrDocument } from '@backstage/plugin-adr-common';
|
||||
|
||||
/**
|
||||
* Context passed to a AdrParser.
|
||||
* @public
|
||||
*/
|
||||
export type AdrParserContext = {
|
||||
/**
|
||||
* The entity associated with the ADR.
|
||||
*/
|
||||
entity: Entity;
|
||||
/**
|
||||
* The ADR content string.
|
||||
*/
|
||||
content: string;
|
||||
/**
|
||||
* The ADR file path.
|
||||
*/
|
||||
path: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* ADR parser function type.
|
||||
* @public
|
||||
*/
|
||||
export type AdrParser = (ctx: AdrParserContext) => Promise<AdrDocument>;
|
||||
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* 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 { createRouter } from './router';
|
||||
export type { AdrRouterOptions } from './router';
|
||||
@@ -1,231 +0,0 @@
|
||||
/*
|
||||
* 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 {
|
||||
CacheClient,
|
||||
ReadTreeResponse,
|
||||
ReadTreeResponseFile,
|
||||
ReadUrlResponse,
|
||||
UrlReader,
|
||||
} from '@backstage/backend-common';
|
||||
import express from 'express';
|
||||
import request from 'supertest';
|
||||
import { createRouter } from './router';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
|
||||
const listEndpointName = '/list';
|
||||
const fileEndpointName = '/file';
|
||||
|
||||
const makeBufferFromString = (string: string) => async () =>
|
||||
Buffer.from(string);
|
||||
|
||||
const testingUrlFakeFileTree: ReadTreeResponseFile[] = [
|
||||
{
|
||||
path: 'folder/testFile001.txt',
|
||||
content: makeBufferFromString('folder/testFile001.txt content'),
|
||||
},
|
||||
{
|
||||
path: 'testFile001.txt',
|
||||
content: makeBufferFromString('testFile002.txt content'),
|
||||
},
|
||||
{
|
||||
path: 'testFile002.txt',
|
||||
content: makeBufferFromString('testFile001.txt content'),
|
||||
},
|
||||
];
|
||||
|
||||
const makeFileContent = async (fileContent: string) => {
|
||||
const result: ReadUrlResponse = {
|
||||
buffer: makeBufferFromString(fileContent),
|
||||
};
|
||||
return result;
|
||||
};
|
||||
|
||||
const testFileOneContent = 'testFileOne content';
|
||||
const testFileTwoContent = 'testFileTwo content';
|
||||
const genericFileContent = 'file content';
|
||||
|
||||
const mockUrlReader: UrlReader = {
|
||||
readUrl(url: string) {
|
||||
switch (url) {
|
||||
case 'testFileOne':
|
||||
return makeFileContent(testFileOneContent);
|
||||
case 'testFileTwo':
|
||||
return makeFileContent(testFileTwoContent);
|
||||
default:
|
||||
return makeFileContent(genericFileContent);
|
||||
}
|
||||
},
|
||||
readTree() {
|
||||
const result: ReadTreeResponse = {
|
||||
files: async () => testingUrlFakeFileTree,
|
||||
archive() {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
dir() {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
etag: '',
|
||||
};
|
||||
|
||||
const resultPromise = async () => result;
|
||||
return resultPromise();
|
||||
},
|
||||
search() {
|
||||
throw new Error('search not implemented.');
|
||||
},
|
||||
};
|
||||
|
||||
class MockCacheClient implements CacheClient {
|
||||
private itemRegistry: { [key: string]: any };
|
||||
|
||||
constructor() {
|
||||
this.itemRegistry = {};
|
||||
}
|
||||
|
||||
async get(key: string) {
|
||||
return this.itemRegistry[key];
|
||||
}
|
||||
|
||||
async set(key: string, value: any) {
|
||||
this.itemRegistry[key] = value;
|
||||
}
|
||||
|
||||
async delete(key: string) {
|
||||
delete this.itemRegistry[key];
|
||||
}
|
||||
|
||||
withOptions = () => this;
|
||||
}
|
||||
|
||||
describe('createRouter', () => {
|
||||
let app: express.Express;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.resetAllMocks();
|
||||
|
||||
const router = await createRouter({
|
||||
reader: mockUrlReader,
|
||||
cacheClient: new MockCacheClient(),
|
||||
logger: {
|
||||
error: (message: any) => message,
|
||||
} as LoggerService,
|
||||
});
|
||||
app = express().use(router);
|
||||
});
|
||||
|
||||
describe(`GET ${listEndpointName}`, () => {
|
||||
it('returns bad request (400) when no url is provided', async () => {
|
||||
const urlNotSpecifiedRequest = await request(app).get(listEndpointName);
|
||||
const urlNotSpecifiedStatus = urlNotSpecifiedRequest.status;
|
||||
const urlNotSpecifiedMessage = urlNotSpecifiedRequest.body.message;
|
||||
|
||||
const urlNotFilledRequest = await request(app).get(
|
||||
`${listEndpointName}?url=`,
|
||||
);
|
||||
const urlNotFilledStatus = urlNotFilledRequest.status;
|
||||
const urlNotFilledMessage = urlNotFilledRequest.body.message;
|
||||
|
||||
const expectedStatusCode = 400;
|
||||
const expectedErrorMessage = 'No URL provided';
|
||||
|
||||
expect(urlNotSpecifiedStatus).toBe(expectedStatusCode);
|
||||
expect(urlNotSpecifiedMessage).toBe(expectedErrorMessage);
|
||||
|
||||
expect(urlNotFilledStatus).toBe(expectedStatusCode);
|
||||
expect(urlNotFilledMessage).toBe(expectedErrorMessage);
|
||||
});
|
||||
|
||||
it('returns the correct listing when reading a url', async () => {
|
||||
const result = await request(app).get(`${listEndpointName}?url=testing`);
|
||||
const { status, body, error } = result;
|
||||
|
||||
const expectedStatusCode = 200;
|
||||
const expectedBody = {
|
||||
data: [
|
||||
{
|
||||
type: 'file',
|
||||
name: 'testFile002.txt',
|
||||
path: 'testFile002.txt',
|
||||
},
|
||||
{
|
||||
type: 'file',
|
||||
name: 'testFile001.txt',
|
||||
path: 'testFile001.txt',
|
||||
},
|
||||
{
|
||||
type: 'file',
|
||||
name: 'testFile001.txt',
|
||||
path: 'folder/testFile001.txt',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(error).toBeFalsy();
|
||||
expect(status).toBe(expectedStatusCode);
|
||||
expect(body).toEqual(expectedBody);
|
||||
});
|
||||
});
|
||||
|
||||
describe(`GET ${fileEndpointName}`, () => {
|
||||
it('returns bad request (400) when no url is provided', async () => {
|
||||
const urlNotSpecifiedRequest = await request(app).get(fileEndpointName);
|
||||
const urlNotSpecifiedStatus = urlNotSpecifiedRequest.status;
|
||||
const urlNotSpecifiedMessage = urlNotSpecifiedRequest.body.message;
|
||||
|
||||
const urlNotFilledRequest = await request(app).get(
|
||||
`${fileEndpointName}?url=`,
|
||||
);
|
||||
const urlNotFilledStatus = urlNotFilledRequest.status;
|
||||
const urlNotFilledMessage = urlNotFilledRequest.body.message;
|
||||
|
||||
const expectedStatusCode = 400;
|
||||
const expectedErrorMessage = 'No URL provided';
|
||||
|
||||
expect(urlNotSpecifiedStatus).toBe(expectedStatusCode);
|
||||
expect(urlNotSpecifiedMessage).toBe(expectedErrorMessage);
|
||||
|
||||
expect(urlNotFilledStatus).toBe(expectedStatusCode);
|
||||
expect(urlNotFilledMessage).toBe(expectedErrorMessage);
|
||||
});
|
||||
|
||||
it('returns the correct file contents when reading a url', async () => {
|
||||
const fileOneResponse = await request(app).get(
|
||||
`${fileEndpointName}?url=testFileOne`,
|
||||
);
|
||||
const fileOneStatus = fileOneResponse.status;
|
||||
const fileOneBody = fileOneResponse.body;
|
||||
const fileOneError = fileOneResponse.error;
|
||||
|
||||
const fileTwoResponse = await request(app).get(
|
||||
`${fileEndpointName}?url=testFileTwo`,
|
||||
);
|
||||
const fileTwoStatus = fileTwoResponse.status;
|
||||
const fileTwoBody = fileTwoResponse.body;
|
||||
const fileTwoError = fileTwoResponse.error;
|
||||
|
||||
const expectedStatusCode = 200;
|
||||
|
||||
expect(fileOneError).toBeFalsy();
|
||||
expect(fileOneStatus).toBe(expectedStatusCode);
|
||||
expect(fileOneBody.data).toBe(testFileOneContent);
|
||||
|
||||
expect(fileTwoError).toBeFalsy();
|
||||
expect(fileTwoStatus).toBe(expectedStatusCode);
|
||||
expect(fileTwoBody.data).toBe(testFileTwoContent);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* 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 { CacheClient, UrlReader } from '@backstage/backend-common';
|
||||
import { NotModifiedError, stringifyError } from '@backstage/errors';
|
||||
import express from 'express';
|
||||
import Router from 'express-promise-router';
|
||||
import { madrParser } from '../search/madrParser';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
|
||||
/** @public */
|
||||
export type AdrRouterOptions = {
|
||||
reader: UrlReader;
|
||||
cacheClient: CacheClient;
|
||||
logger: LoggerService;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export async function createRouter(
|
||||
options: AdrRouterOptions,
|
||||
): Promise<express.Router> {
|
||||
const { reader, cacheClient, logger } = options;
|
||||
|
||||
const router = Router();
|
||||
router.use(express.json());
|
||||
|
||||
router.get('/list', async (req, res) => {
|
||||
const urlToProcess = req.query.url as string;
|
||||
if (!urlToProcess) {
|
||||
res.statusCode = 400;
|
||||
res.json({ message: 'No URL provided' });
|
||||
return;
|
||||
}
|
||||
|
||||
const cachedTree = (await cacheClient.get(urlToProcess)) as {
|
||||
data: {
|
||||
type: string;
|
||||
name: string;
|
||||
path: string;
|
||||
}[];
|
||||
etag: string;
|
||||
};
|
||||
const cachedData = cachedTree?.data;
|
||||
|
||||
try {
|
||||
const treeGetResponse = await reader.readTree(urlToProcess, {
|
||||
etag: cachedTree?.etag,
|
||||
});
|
||||
const files = await treeGetResponse.files();
|
||||
const data = await Promise.all(
|
||||
files
|
||||
.map(async file => {
|
||||
const fileContent = await file.content();
|
||||
const adrInfo = madrParser(fileContent.toString());
|
||||
return {
|
||||
type: 'file',
|
||||
name: file.path.substring(file.path.lastIndexOf('/') + 1),
|
||||
path: file.path,
|
||||
...adrInfo,
|
||||
};
|
||||
})
|
||||
.reverse(),
|
||||
);
|
||||
|
||||
await cacheClient.set(urlToProcess, {
|
||||
data,
|
||||
etag: treeGetResponse.etag,
|
||||
});
|
||||
|
||||
res.json({ data });
|
||||
} catch (error: any) {
|
||||
if (cachedData && error.name === NotModifiedError.name) {
|
||||
res.json({ data: cachedData });
|
||||
return;
|
||||
}
|
||||
|
||||
const message = stringifyError(error);
|
||||
logger.error(`Unable to fetch ADRs from ${urlToProcess}: ${message}`);
|
||||
res.statusCode = 500;
|
||||
res.json({ message });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/file', async (req, res) => {
|
||||
const urlToProcess = req.query.url as string;
|
||||
if (!urlToProcess) {
|
||||
res.statusCode = 400;
|
||||
res.json({ message: 'No URL provided' });
|
||||
return;
|
||||
}
|
||||
|
||||
const cachedFileContent = (await cacheClient.get(urlToProcess)) as {
|
||||
data: string;
|
||||
etag: string;
|
||||
};
|
||||
|
||||
try {
|
||||
const fileGetResponse = await reader.readUrl(urlToProcess, {
|
||||
etag: cachedFileContent?.etag,
|
||||
});
|
||||
const fileBuffer = await fileGetResponse.buffer();
|
||||
const data = fileBuffer.toString();
|
||||
|
||||
await cacheClient.set(urlToProcess, {
|
||||
data,
|
||||
etag: fileGetResponse.etag,
|
||||
});
|
||||
|
||||
res.json({ data });
|
||||
} catch (error) {
|
||||
if (cachedFileContent && error.name === NotModifiedError.name) {
|
||||
res.json({ data: cachedFileContent.data });
|
||||
return;
|
||||
}
|
||||
|
||||
const message = stringifyError(error);
|
||||
logger.error(`Unable to fetch ADRs from ${urlToProcess}: ${message}`);
|
||||
res.statusCode = 500;
|
||||
res.json({ message });
|
||||
}
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -1,636 +0,0 @@
|
||||
# @backstage/plugin-adr-common
|
||||
|
||||
## 0.2.22
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.10.0
|
||||
- @backstage/catalog-model@1.4.5
|
||||
- @backstage/plugin-search-common@1.2.11
|
||||
|
||||
## 0.2.22-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.10.0-next.0
|
||||
- @backstage/catalog-model@1.4.5
|
||||
- @backstage/plugin-search-common@1.2.11
|
||||
|
||||
## 0.2.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 5335634: Fixed Azure DevOps ADR file path reading
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.9.1
|
||||
- @backstage/plugin-search-common@1.2.11
|
||||
- @backstage/catalog-model@1.4.5
|
||||
|
||||
## 0.2.21-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.9.1-next.2
|
||||
- @backstage/catalog-model@1.4.5-next.0
|
||||
- @backstage/plugin-search-common@1.2.11-next.1
|
||||
|
||||
## 0.2.21-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.9.1-next.1
|
||||
- @backstage/catalog-model@1.4.5-next.0
|
||||
- @backstage/plugin-search-common@1.2.11-next.1
|
||||
|
||||
## 0.2.21-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 5335634: Fixed Azure DevOps ADR file path reading
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-search-common@1.2.11-next.0
|
||||
- @backstage/catalog-model@1.4.5-next.0
|
||||
- @backstage/integration@1.9.1-next.0
|
||||
|
||||
## 0.2.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.4.4
|
||||
- @backstage/integration@1.9.0
|
||||
- @backstage/plugin-search-common@1.2.10
|
||||
|
||||
## 0.2.20-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.9.0-next.1
|
||||
- @backstage/catalog-model@1.4.4-next.0
|
||||
- @backstage/plugin-search-common@1.2.10
|
||||
|
||||
## 0.2.20-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.4.4-next.0
|
||||
- @backstage/integration@1.9.0-next.0
|
||||
- @backstage/plugin-search-common@1.2.10
|
||||
|
||||
## 0.2.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.4.3
|
||||
- @backstage/integration@1.8.0
|
||||
- @backstage/plugin-search-common@1.2.10
|
||||
|
||||
## 0.2.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.8.0
|
||||
- @backstage/catalog-model@1.4.3
|
||||
- @backstage/plugin-search-common@1.2.9
|
||||
|
||||
## 0.2.18-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.8.0-next.1
|
||||
- @backstage/catalog-model@1.4.3
|
||||
- @backstage/plugin-search-common@1.2.8
|
||||
|
||||
## 0.2.18-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.8.0-next.0
|
||||
- @backstage/catalog-model@1.4.3
|
||||
- @backstage/plugin-search-common@1.2.8
|
||||
|
||||
## 0.2.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.7.2
|
||||
- @backstage/catalog-model@1.4.3
|
||||
- @backstage/plugin-search-common@1.2.8
|
||||
|
||||
## 0.2.17-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.7.2-next.0
|
||||
- @backstage/catalog-model@1.4.3
|
||||
- @backstage/plugin-search-common@1.2.7
|
||||
|
||||
## 0.2.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.7.1
|
||||
- @backstage/catalog-model@1.4.3
|
||||
- @backstage/plugin-search-common@1.2.7
|
||||
|
||||
## 0.2.16-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.4.3-next.0
|
||||
- @backstage/integration@1.7.1-next.1
|
||||
- @backstage/plugin-search-common@1.2.7-next.0
|
||||
|
||||
## 0.2.16-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.7.1-next.0
|
||||
- @backstage/catalog-model@1.4.2
|
||||
- @backstage/plugin-search-common@1.2.6
|
||||
|
||||
## 0.2.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 406b786a2a2c: Mark package as being free of side effects, allowing more optimized Webpack builds.
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.4.2
|
||||
- @backstage/integration@1.7.0
|
||||
- @backstage/plugin-search-common@1.2.6
|
||||
|
||||
## 0.2.15-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 406b786a2a2c: Mark package as being free of side effects, allowing more optimized Webpack builds.
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.4.2-next.2
|
||||
- @backstage/integration@1.7.0-next.3
|
||||
- @backstage/plugin-search-common@1.2.6-next.2
|
||||
|
||||
## 0.2.15-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.7.0-next.2
|
||||
- @backstage/catalog-model@1.4.2-next.1
|
||||
- @backstage/plugin-search-common@1.2.6-next.1
|
||||
|
||||
## 0.2.15-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.7.0-next.1
|
||||
- @backstage/catalog-model@1.4.2-next.0
|
||||
- @backstage/plugin-search-common@1.2.6-next.0
|
||||
|
||||
## 0.2.14-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.7.0-next.0
|
||||
- @backstage/catalog-model@1.4.1
|
||||
- @backstage/plugin-search-common@1.2.5
|
||||
|
||||
## 0.2.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.6.0
|
||||
- @backstage/catalog-model@1.4.1
|
||||
- @backstage/plugin-search-common@1.2.5
|
||||
|
||||
## 0.2.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 2b4513abb784: fixed error with date parsing.
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.4.1
|
||||
- @backstage/integration@1.5.1
|
||||
- @backstage/plugin-search-common@1.2.5
|
||||
|
||||
## 0.2.11-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 2b4513abb784: fixed error with date parsing.
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.4.1-next.0
|
||||
- @backstage/integration@1.5.1-next.0
|
||||
- @backstage/plugin-search-common@1.2.5-next.0
|
||||
|
||||
## 0.2.11-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.4.1-next.0
|
||||
- @backstage/integration@1.5.1-next.0
|
||||
- @backstage/plugin-search-common@1.2.5-next.0
|
||||
|
||||
## 0.2.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 58524588448c: Add utility function for parsing MADR front matter
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.5.0
|
||||
- @backstage/catalog-model@1.4.0
|
||||
- @backstage/plugin-search-common@1.2.4
|
||||
|
||||
## 0.2.10-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.4.0-next.1
|
||||
- @backstage/integration@1.5.0-next.0
|
||||
- @backstage/plugin-search-common@1.2.4-next.0
|
||||
|
||||
## 0.2.10-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 58524588448c: Add utility function for parsing MADR front matter
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.5.0-next.0
|
||||
- @backstage/catalog-model@1.4.0-next.0
|
||||
- @backstage/plugin-search-common@1.2.4-next.0
|
||||
|
||||
## 0.2.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.4.5
|
||||
- @backstage/catalog-model@1.3.0
|
||||
- @backstage/plugin-search-common@1.2.3
|
||||
|
||||
## 0.2.9-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.4.5-next.0
|
||||
- @backstage/catalog-model@1.3.0
|
||||
- @backstage/plugin-search-common@1.2.3
|
||||
|
||||
## 0.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 77aa3a4d47e: use path attribute to fetch files instead of name and update the UI to navigate over subdirectories
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.3.0
|
||||
- @backstage/integration@1.4.4
|
||||
- @backstage/plugin-search-common@1.2.3
|
||||
|
||||
## 0.2.8-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 77aa3a4d47e: use path attribute to fetch files instead of name and update the UI to navigate over subdirectories
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.3.0-next.0
|
||||
- @backstage/integration@1.4.4-next.0
|
||||
- @backstage/plugin-search-common@1.2.3-next.0
|
||||
|
||||
## 0.2.8-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.4.4-next.0
|
||||
- @backstage/catalog-model@1.2.1
|
||||
- @backstage/plugin-search-common@1.2.3-next.0
|
||||
|
||||
## 0.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.2.1
|
||||
- @backstage/integration@1.4.3
|
||||
- @backstage/plugin-search-common@1.2.2
|
||||
|
||||
## 0.2.7-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.4.3-next.0
|
||||
- @backstage/catalog-model@1.2.1-next.1
|
||||
- @backstage/plugin-search-common@1.2.2-next.0
|
||||
|
||||
## 0.2.7-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.2.1-next.0
|
||||
- @backstage/integration@1.4.2
|
||||
- @backstage/plugin-search-common@1.2.1
|
||||
|
||||
## 0.2.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.2.0
|
||||
- @backstage/integration@1.4.2
|
||||
- @backstage/plugin-search-common@1.2.1
|
||||
|
||||
## 0.2.6-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.2.0-next.1
|
||||
- @backstage/integration@1.4.2
|
||||
- @backstage/plugin-search-common@1.2.1
|
||||
|
||||
## 0.2.6-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.1.6-next.0
|
||||
|
||||
## 0.2.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.1.5
|
||||
- @backstage/integration@1.4.2
|
||||
- @backstage/plugin-search-common@1.2.1
|
||||
|
||||
## 0.2.5-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.1.5-next.1
|
||||
- @backstage/integration@1.4.2-next.0
|
||||
- @backstage/plugin-search-common@1.2.1-next.0
|
||||
|
||||
## 0.2.5-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.1.5-next.0
|
||||
- @backstage/integration@1.4.1
|
||||
- @backstage/plugin-search-common@1.2.0
|
||||
|
||||
## 0.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.4.1
|
||||
- @backstage/plugin-search-common@1.2.0
|
||||
- @backstage/catalog-model@1.1.4
|
||||
|
||||
## 0.2.4-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.1.4-next.1
|
||||
- @backstage/integration@1.4.1-next.1
|
||||
- @backstage/plugin-search-common@1.2.0-next.3
|
||||
|
||||
## 0.2.4-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-search-common@1.2.0-next.2
|
||||
- @backstage/catalog-model@1.1.4-next.1
|
||||
- @backstage/integration@1.4.1-next.1
|
||||
|
||||
## 0.2.4-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.4.1-next.1
|
||||
- @backstage/catalog-model@1.1.4-next.1
|
||||
- @backstage/plugin-search-common@1.1.2-next.1
|
||||
|
||||
## 0.2.4-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.4.1-next.0
|
||||
- @backstage/catalog-model@1.1.4-next.0
|
||||
- @backstage/plugin-search-common@1.1.2-next.0
|
||||
|
||||
## 0.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.4.0
|
||||
- @backstage/catalog-model@1.1.3
|
||||
- @backstage/plugin-search-common@1.1.1
|
||||
|
||||
## 0.2.3-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.4.0-next.0
|
||||
- @backstage/catalog-model@1.1.3-next.0
|
||||
- @backstage/plugin-search-common@1.1.1-next.0
|
||||
|
||||
## 0.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.1.2
|
||||
- @backstage/plugin-search-common@1.1.0
|
||||
- @backstage/integration@1.3.2
|
||||
|
||||
## 0.2.2-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-search-common@1.1.0-next.2
|
||||
- @backstage/catalog-model@1.1.2-next.2
|
||||
- @backstage/integration@1.3.2-next.2
|
||||
|
||||
## 0.2.2-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-search-common@1.1.0-next.1
|
||||
- @backstage/catalog-model@1.1.2-next.1
|
||||
- @backstage/integration@1.3.2-next.1
|
||||
|
||||
## 0.2.2-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.1.2-next.0
|
||||
- @backstage/integration@1.3.2-next.0
|
||||
- @backstage/plugin-search-common@1.0.2-next.0
|
||||
|
||||
## 0.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.3.1
|
||||
- @backstage/catalog-model@1.1.1
|
||||
- @backstage/plugin-search-common@1.0.1
|
||||
|
||||
## 0.2.1-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.1.1-next.0
|
||||
- @backstage/integration@1.3.1-next.2
|
||||
|
||||
## 0.2.1-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.3.1-next.0
|
||||
- @backstage/plugin-search-common@1.0.1-next.0
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- bfc7c50a09: Display associated entity as a chip in `AdrSearchResultListItem`
|
||||
|
||||
BREAKING: `AdrDocument` now includes a `entityRef` property, if you have a custom `AdrParser` you will have to supply this property in your returned documents
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.3.0
|
||||
|
||||
## 0.2.0-next.1
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- bfc7c50a09: Display associated entity as a chip in `AdrSearchResultListItem`
|
||||
|
||||
BREAKING: `AdrDocument` now includes a `entityRef` property, if you have a custom `AdrParser` you will have to supply this property in your returned documents
|
||||
|
||||
## 0.1.3-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.3.0-next.0
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.1.0
|
||||
- @backstage/plugin-search-common@1.0.0
|
||||
- @backstage/integration@1.2.2
|
||||
|
||||
## 0.1.2-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.1.0-next.1
|
||||
- @backstage/integration@1.2.2-next.1
|
||||
- @backstage/plugin-search-common@0.3.6-next.0
|
||||
|
||||
## 0.1.2-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/catalog-model@1.1.0-next.0
|
||||
- @backstage/integration@1.2.2-next.0
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-search-common@0.3.5
|
||||
- @backstage/integration@1.2.1
|
||||
- @backstage/catalog-model@1.0.3
|
||||
|
||||
## 0.1.1-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.2.1-next.1
|
||||
- @backstage/catalog-model@1.0.3-next.0
|
||||
- @backstage/plugin-search-common@0.3.5-next.0
|
||||
|
||||
## 0.1.1-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.2.1-next.0
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- e73075a301: Implement ADR plugin
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@1.2.0
|
||||
- @backstage/plugin-search-common@0.3.4
|
||||
- @backstage/catalog-model@1.0.2
|
||||
|
||||
## 0.1.0-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-search-common@0.3.4-next.0
|
||||
- @backstage/catalog-model@1.0.2-next.0
|
||||
- @backstage/integration@1.2.0-next.1
|
||||
|
||||
## 0.1.0-next.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- e73075a301: Implement ADR plugin
|
||||
@@ -1,3 +0,0 @@
|
||||
# Deprecated
|
||||
|
||||
This package has been moved to the [backstage-community/plugins](https://github.com/backstage/community-plugins) repository. Migrate to using `@backstage-community/plugin-adr-common` instead.
|
||||
@@ -1,50 +0,0 @@
|
||||
## API Report File for "@backstage/plugin-adr-common"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { IndexableDocument } from '@backstage/plugin-search-common';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
|
||||
// @public
|
||||
export interface AdrDocument extends IndexableDocument {
|
||||
date?: string;
|
||||
entityRef: string;
|
||||
entityTitle?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type AdrFilePathFilterFn = (path: string) => boolean;
|
||||
|
||||
// @public
|
||||
export const ANNOTATION_ADR_LOCATION = 'backstage.io/adr-location';
|
||||
|
||||
// @public
|
||||
export const getAdrLocationUrl: (
|
||||
entity: Entity,
|
||||
scmIntegration: ScmIntegrationRegistry,
|
||||
adrFilePath?: String,
|
||||
) => string;
|
||||
|
||||
// @public
|
||||
export const isAdrAvailable: (entity: Entity) => boolean;
|
||||
|
||||
// @public
|
||||
export const MADR_DATE_FORMAT = 'yyyy-MM-dd';
|
||||
|
||||
// @public
|
||||
export const madrFilePathFilter: AdrFilePathFilterFn;
|
||||
|
||||
// @public
|
||||
export interface ParsedMadr {
|
||||
attributes: Record<string, unknown>;
|
||||
content: string;
|
||||
date?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const parseMadrWithFrontmatter: (content: string) => ParsedMadr;
|
||||
```
|
||||
@@ -1,10 +0,0 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: backstage-plugin-adr-common
|
||||
title: '@backstage/plugin-adr-common'
|
||||
description: Common functionalities for the adr plugin
|
||||
spec:
|
||||
lifecycle: experimental
|
||||
type: backstage-common-library
|
||||
owner: kuangp
|
||||
@@ -1,2 +0,0 @@
|
||||
# Knip report
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
{
|
||||
"name": "@backstage/plugin-adr-common",
|
||||
"version": "0.2.22",
|
||||
"description": "Common functionalities for the adr plugin",
|
||||
"backstage": {
|
||||
"role": "common-library",
|
||||
"moved": "@backstage-community/plugin-adr-common"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.cjs.js",
|
||||
"module": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "plugins/adr-common"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"sideEffects": false,
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
"clean": "backstage-cli package clean",
|
||||
"lint": "backstage-cli package lint",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack",
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
"@backstage/integration": "workspace:^",
|
||||
"@backstage/plugin-search-common": "workspace:^",
|
||||
"front-matter": "^4.0.2",
|
||||
"luxon": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^"
|
||||
},
|
||||
"deprecated": "This package has been moved to the backstage/community-plugins repository. You should migrate to using @backstage-community/plugin-adr-common instead."
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
* Common types and functionalities for the ADR plugin.
|
||||
* @packageDocumentation
|
||||
*/
|
||||
import { Entity, getEntitySourceLocation } from '@backstage/catalog-model';
|
||||
import { IndexableDocument } from '@backstage/plugin-search-common';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import frontMatter from 'front-matter';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
/**
|
||||
* ADR plugin annotation.
|
||||
* @public
|
||||
*/
|
||||
export const ANNOTATION_ADR_LOCATION = 'backstage.io/adr-location';
|
||||
|
||||
/**
|
||||
* Standard luxon DateTime format string for MADR dates.
|
||||
* @public
|
||||
*/
|
||||
export const MADR_DATE_FORMAT = 'yyyy-MM-dd';
|
||||
|
||||
/**
|
||||
* Utility function to get the value of an entity ADR annotation.
|
||||
* @public
|
||||
*/
|
||||
const getAdrLocationDir = (entity: Entity) =>
|
||||
entity.metadata.annotations?.[ANNOTATION_ADR_LOCATION]?.trim();
|
||||
|
||||
/**
|
||||
* Utility function to determine if the given entity has ADRs.
|
||||
* @public
|
||||
*/
|
||||
export const isAdrAvailable = (entity: Entity) =>
|
||||
Boolean(getAdrLocationDir(entity));
|
||||
|
||||
/**
|
||||
* Utility function to extract the ADR location URL from an entity based off
|
||||
* its ADR annotation and relative to the entity source location.
|
||||
* @public
|
||||
*/
|
||||
export const getAdrLocationUrl = (
|
||||
entity: Entity,
|
||||
scmIntegration: ScmIntegrationRegistry,
|
||||
adrFilePath?: String,
|
||||
) => {
|
||||
if (!isAdrAvailable(entity)) {
|
||||
throw new Error(`Missing ADR annotation: ${ANNOTATION_ADR_LOCATION}`);
|
||||
}
|
||||
|
||||
let url = getAdrLocationDir(entity)!.replace(/\/$/, '');
|
||||
|
||||
if (adrFilePath) {
|
||||
url = `${url}/${adrFilePath}`;
|
||||
}
|
||||
|
||||
return scmIntegration.resolveUrl({
|
||||
url,
|
||||
base: getEntitySourceLocation(entity).target,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* File path filter function type for ADR filenames
|
||||
* @public
|
||||
*/
|
||||
export type AdrFilePathFilterFn = (path: string) => boolean;
|
||||
|
||||
/**
|
||||
* File path filter for MADR filename formats
|
||||
* @public
|
||||
*/
|
||||
export const madrFilePathFilter: AdrFilePathFilterFn = (path: string) =>
|
||||
/^(?:.*\/)?\d{4}-.+\.md$/.test(path);
|
||||
|
||||
/**
|
||||
* ADR indexable document interface
|
||||
* @public
|
||||
*/
|
||||
export interface AdrDocument extends IndexableDocument {
|
||||
/**
|
||||
* Ref of the entity associated with this ADR
|
||||
*/
|
||||
entityRef: string;
|
||||
/**
|
||||
* Title of the entity associated with this ADR
|
||||
*/
|
||||
entityTitle?: string;
|
||||
/**
|
||||
* ADR status label
|
||||
*/
|
||||
status?: string;
|
||||
/**
|
||||
* ADR date
|
||||
*/
|
||||
date?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsed MADR document with front matter (if present) parsed and extracted from the main markdown content.
|
||||
* @public
|
||||
*/
|
||||
export interface ParsedMadr {
|
||||
/**
|
||||
* Main body of ADR content (with any front matter removed)
|
||||
*/
|
||||
content: string;
|
||||
/**
|
||||
* ADR status
|
||||
*/
|
||||
status?: string;
|
||||
/**
|
||||
* ADR date
|
||||
*/
|
||||
date?: string;
|
||||
/**
|
||||
* All attributes parsed from front matter
|
||||
*/
|
||||
attributes: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to parse raw markdown content for an ADR and extract any metadata found as "front matter" at the top of the Markdown document.
|
||||
* @param content - Raw markdown content which may (optionally) include front matter
|
||||
* @public
|
||||
*/
|
||||
export const parseMadrWithFrontmatter = (content: string): ParsedMadr => {
|
||||
const parsed = frontMatter<Record<string, unknown>>(content);
|
||||
const status = parsed.attributes.status;
|
||||
const date = parsed.attributes.date;
|
||||
const luxdate = DateTime.fromJSDate(new Date(`${date}`));
|
||||
const formattedDate = luxdate.toISODate();
|
||||
return {
|
||||
content: parsed.body,
|
||||
status: status ? String(status) : undefined,
|
||||
date: date ? String(formattedDate) : undefined,
|
||||
attributes: parsed.attributes,
|
||||
};
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, {
|
||||
rules: {
|
||||
'@backstage/no-top-level-material-ui-4-imports': 'error',
|
||||
},
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
# Deprecated
|
||||
|
||||
This package has been moved to the [backstage-community/plugins](https://github.com/backstage/community-plugins) repository. Migrate to using `@backstage-community/plugin-adr` instead.
|
||||
@@ -1,31 +0,0 @@
|
||||
## API Report File for "@backstage/plugin-adr"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { BackstagePlugin } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
import { TranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const adrSearchResultListItemExtension: ExtensionDefinition<{
|
||||
lineClamp: number;
|
||||
noTrack: boolean;
|
||||
}>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const adrTranslationRef: TranslationRef<
|
||||
'adr',
|
||||
{
|
||||
readonly contentHeaderTitle: 'Architecture Decision Records';
|
||||
readonly failedToFetch: 'Failed to fetch ADRs';
|
||||
readonly notFound: 'No ADRs found';
|
||||
}
|
||||
>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
const _default: BackstagePlugin<{}, {}>;
|
||||
export default _default;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
@@ -1,117 +0,0 @@
|
||||
## API Report File for "@backstage/plugin-adr"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
/// <reference types="react" />
|
||||
|
||||
import { AdrDocument } from '@backstage/plugin-adr-common';
|
||||
import { AdrFilePathFilterFn } from '@backstage/plugin-adr-common';
|
||||
import { ApiRef } from '@backstage/core-plugin-api';
|
||||
import { BackstagePlugin } from '@backstage/core-plugin-api';
|
||||
import { DiscoveryApi } from '@backstage/core-plugin-api';
|
||||
import { FetchApi } from '@backstage/core-plugin-api';
|
||||
import { isAdrAvailable } from '@backstage/plugin-adr-common';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { default as React_2 } from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import { ResultHighlight } from '@backstage/plugin-search-common';
|
||||
import { RouteRef } from '@backstage/core-plugin-api';
|
||||
|
||||
// @public
|
||||
export interface AdrApi {
|
||||
listAdrs(url: string): Promise<AdrListResult>;
|
||||
readAdr(url: string): Promise<AdrReadResult>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const adrApiRef: ApiRef<AdrApi>;
|
||||
|
||||
// @public
|
||||
export class AdrClient implements AdrApi {
|
||||
constructor(options: AdrClientOptions);
|
||||
// (undocumented)
|
||||
listAdrs(url: string): Promise<AdrListResult>;
|
||||
// (undocumented)
|
||||
readAdr(url: string): Promise<AdrReadResult>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface AdrClientOptions {
|
||||
// (undocumented)
|
||||
discoveryApi: DiscoveryApi;
|
||||
// (undocumented)
|
||||
fetchApi: FetchApi;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type AdrContentDecorator = (adrInfo: {
|
||||
baseUrl: string;
|
||||
content: string;
|
||||
}) => {
|
||||
content: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AdrFileInfo = {
|
||||
type: string;
|
||||
path: string;
|
||||
name: string;
|
||||
title?: string;
|
||||
status?: string;
|
||||
date?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AdrListResult = {
|
||||
data: AdrFileInfo[];
|
||||
};
|
||||
|
||||
// @public
|
||||
export const adrPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{}
|
||||
>;
|
||||
|
||||
// @public
|
||||
export const AdrReader: {
|
||||
(props: {
|
||||
adr: string;
|
||||
decorators?: AdrContentDecorator[];
|
||||
}): React_2.JSX.Element;
|
||||
decorators: Readonly<{
|
||||
createRewriteRelativeLinksDecorator(): AdrContentDecorator;
|
||||
createRewriteRelativeEmbedsDecorator(): AdrContentDecorator;
|
||||
createFrontMatterFormatterDecorator(): AdrContentDecorator;
|
||||
}>;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AdrReadResult = {
|
||||
data: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export const AdrSearchResultListItem: (
|
||||
props: AdrSearchResultListItemProps,
|
||||
) => JSX.Element | null;
|
||||
|
||||
// @public (undocumented)
|
||||
export type AdrSearchResultListItemProps = {
|
||||
lineClamp?: number;
|
||||
highlight?: ResultHighlight;
|
||||
icon?: ReactNode;
|
||||
rank?: number;
|
||||
result?: AdrDocument;
|
||||
};
|
||||
|
||||
// @public
|
||||
export const EntityAdrContent: (props: {
|
||||
contentDecorators?: AdrContentDecorator[] | undefined;
|
||||
filePathFilterFn?: AdrFilePathFilterFn | undefined;
|
||||
}) => JSX_2.Element;
|
||||
|
||||
export { isAdrAvailable };
|
||||
```
|
||||
@@ -1,9 +0,0 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: backstage-plugin-adr
|
||||
title: '@backstage/plugin-adr'
|
||||
spec:
|
||||
lifecycle: experimental
|
||||
type: backstage-frontend-plugin
|
||||
owner: kuangp
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 756 KiB |
@@ -1,13 +0,0 @@
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: component-with-adrs
|
||||
title: Component With ADRs
|
||||
description: A Service with ADRs registerd
|
||||
annotations:
|
||||
backstage.io/adr-location: https://github.com/adr/madr/tree/develop/docs/decisions
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
owner: user:guest
|
||||
@@ -1,9 +0,0 @@
|
||||
# Knip report
|
||||
|
||||
## Unused devDependencies (2)
|
||||
|
||||
| Name | Location | Severity |
|
||||
| :--------------------- | :----------- | :------- |
|
||||
| @testing-library/react | package.json | error |
|
||||
| @testing-library/dom | package.json | error |
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
{
|
||||
"name": "@backstage/plugin-adr",
|
||||
"version": "0.6.17",
|
||||
"backstage": {
|
||||
"role": "frontend-plugin",
|
||||
"moved": "@backstage-community/plugin-adr"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "plugins/adr"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./alpha": "./src/alpha.tsx",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"alpha": [
|
||||
"src/alpha.tsx"
|
||||
],
|
||||
"package.json": [
|
||||
"package.json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
"clean": "backstage-cli package clean",
|
||||
"lint": "backstage-cli package lint",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack",
|
||||
"start": "backstage-cli package start",
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
"@backstage/core-components": "workspace:^",
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/frontend-plugin-api": "workspace:^",
|
||||
"@backstage/integration-react": "workspace:^",
|
||||
"@backstage/plugin-adr-common": "workspace:^",
|
||||
"@backstage/plugin-catalog-react": "workspace:^",
|
||||
"@backstage/plugin-search-common": "workspace:^",
|
||||
"@backstage/plugin-search-react": "workspace:^",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"react-use": "^17.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@testing-library/dom": "^10.0.0",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
"@testing-library/react": "^15.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
"react-router-dom": "6.0.0-beta.0 || ^6.3.0"
|
||||
},
|
||||
"deprecated": "This package has been moved to the backstage/community-plugins repository. You should migrate to using @backstage-community/plugin-adr instead."
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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 React from 'react';
|
||||
import {
|
||||
createPlugin,
|
||||
createSchemaFromZod,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { createSearchResultListItemExtension } from '@backstage/plugin-search-react/alpha';
|
||||
import { AdrDocument } from '@backstage/plugin-adr-common';
|
||||
|
||||
export * from './translations';
|
||||
|
||||
function isAdrDocument(result: any): result is AdrDocument {
|
||||
return result.entityRef;
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
export const adrSearchResultListItemExtension =
|
||||
createSearchResultListItemExtension({
|
||||
configSchema: createSchemaFromZod(z =>
|
||||
z.object({
|
||||
// TODO: Define how the icon can be configurable
|
||||
noTrack: z.boolean().default(false),
|
||||
lineClamp: z.number().default(5),
|
||||
}),
|
||||
),
|
||||
predicate: result => result.type === 'adr',
|
||||
component: async ({ config }) => {
|
||||
const { AdrSearchResultListItem } = await import(
|
||||
'./search/AdrSearchResultListItem'
|
||||
);
|
||||
return ({ result, ...rest }) =>
|
||||
isAdrDocument(result) ? (
|
||||
<AdrSearchResultListItem {...rest} {...config} result={result} />
|
||||
) : null;
|
||||
},
|
||||
});
|
||||
|
||||
/** @alpha */
|
||||
export default createPlugin({
|
||||
id: 'adr',
|
||||
extensions: [adrSearchResultListItemExtension],
|
||||
});
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* 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 { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';
|
||||
import { AdrApi, AdrListResult, AdrReadResult } from './types';
|
||||
|
||||
/**
|
||||
* Options for creating an AdrClient.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface AdrClientOptions {
|
||||
discoveryApi: DiscoveryApi;
|
||||
fetchApi: FetchApi;
|
||||
}
|
||||
|
||||
const readEndpoint = 'file';
|
||||
const listEndpoint = 'list';
|
||||
|
||||
/**
|
||||
* An implementation of the AdrApi that communicates with the ADR backend plugin.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class AdrClient implements AdrApi {
|
||||
private readonly discoveryApi: DiscoveryApi;
|
||||
private readonly fetchApi: FetchApi;
|
||||
|
||||
constructor(options: AdrClientOptions) {
|
||||
this.discoveryApi = options.discoveryApi;
|
||||
this.fetchApi = options.fetchApi;
|
||||
}
|
||||
|
||||
private async fetchAdrApi<T>(endpoint: string, fileUrl: string): Promise<T> {
|
||||
const baseUrl = await this.discoveryApi.getBaseUrl('adr');
|
||||
const targetUrl = `${baseUrl}/${endpoint}?url=${encodeURIComponent(
|
||||
fileUrl,
|
||||
)}`;
|
||||
|
||||
const result = await this.fetchApi.fetch(targetUrl);
|
||||
const data = await result.json();
|
||||
|
||||
if (!result.ok) {
|
||||
throw new Error(`${data.message}`);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
async listAdrs(url: string): Promise<AdrListResult> {
|
||||
return this.fetchAdrApi<AdrListResult>(listEndpoint, url);
|
||||
}
|
||||
|
||||
async readAdr(url: string): Promise<AdrReadResult> {
|
||||
return this.fetchAdrApi<AdrReadResult>(readEndpoint, url);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* 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 { AdrClient } from './AdrClient';
|
||||
export type { AdrClientOptions } from './AdrClient';
|
||||
export { adrApiRef } from './types';
|
||||
export type {
|
||||
AdrApi,
|
||||
AdrFileInfo,
|
||||
AdrListResult,
|
||||
AdrReadResult,
|
||||
} from './types';
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* 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 { createApiRef } from '@backstage/core-plugin-api';
|
||||
|
||||
/**
|
||||
* Contains information about an ADR file.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AdrFileInfo = {
|
||||
/** The file type. */
|
||||
type: string;
|
||||
|
||||
/** The relative path of the ADR file. */
|
||||
path: string;
|
||||
|
||||
/** The name of the ADR file. */
|
||||
name: string;
|
||||
|
||||
/** The title of the ADR. */
|
||||
title?: string;
|
||||
|
||||
/** The status of the ADR. */
|
||||
status?: string;
|
||||
|
||||
/** The date of the ADR. */
|
||||
date?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* The result of listing ADRs.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AdrListResult = {
|
||||
data: AdrFileInfo[];
|
||||
};
|
||||
|
||||
/**
|
||||
* The result of reading an ADR.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AdrReadResult = {
|
||||
/** The contents of the read ADR file. */
|
||||
data: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* The API used by the adr plugin to list and read ADRs.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface AdrApi {
|
||||
/** Lists the ADRs at the provided url. */
|
||||
listAdrs(url: string): Promise<AdrListResult>;
|
||||
|
||||
/** Reads the contents of the ADR at the provided url. */
|
||||
readAdr(url: string): Promise<AdrReadResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
* ApiRef for the AdrApi.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const adrApiRef = createApiRef<AdrApi>({
|
||||
id: 'plugin.adr.api',
|
||||
});
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* 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 React, { useMemo } from 'react';
|
||||
import {
|
||||
InfoCard,
|
||||
MarkdownContent,
|
||||
Progress,
|
||||
WarningPanel,
|
||||
} from '@backstage/core-components';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { scmIntegrationsApiRef } from '@backstage/integration-react';
|
||||
import { getAdrLocationUrl } from '@backstage/plugin-adr-common';
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
|
||||
import { adrDecoratorFactories } from './decorators';
|
||||
import { AdrContentDecorator } from './types';
|
||||
import { adrApiRef } from '../../api';
|
||||
import useAsync from 'react-use/esm/useAsync';
|
||||
|
||||
/**
|
||||
* Component to fetch and render an ADR.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const AdrReader = (props: {
|
||||
adr: string;
|
||||
decorators?: AdrContentDecorator[];
|
||||
}) => {
|
||||
const { adr, decorators } = props;
|
||||
const { entity } = useEntity();
|
||||
const scmIntegrations = useApi(scmIntegrationsApiRef);
|
||||
const adrApi = useApi(adrApiRef);
|
||||
const adrLocationUrl = getAdrLocationUrl(entity, scmIntegrations);
|
||||
const adrFileLocationUrl = getAdrLocationUrl(entity, scmIntegrations, adr);
|
||||
|
||||
const { value, loading, error } = useAsync(
|
||||
async () => adrApi.readAdr(adrFileLocationUrl),
|
||||
[adrFileLocationUrl],
|
||||
);
|
||||
|
||||
const adrContent = useMemo(() => {
|
||||
if (!value?.data) {
|
||||
return '';
|
||||
}
|
||||
const adrDecorators = decorators ?? [
|
||||
adrDecoratorFactories.createRewriteRelativeLinksDecorator(),
|
||||
adrDecoratorFactories.createRewriteRelativeEmbedsDecorator(),
|
||||
adrDecoratorFactories.createFrontMatterFormatterDecorator(),
|
||||
];
|
||||
|
||||
return adrDecorators.reduce(
|
||||
(content, decorator) =>
|
||||
decorator({ baseUrl: adrLocationUrl, content }).content,
|
||||
value.data,
|
||||
);
|
||||
}, [adrLocationUrl, decorators, value]);
|
||||
|
||||
return (
|
||||
<InfoCard>
|
||||
{loading && <Progress />}
|
||||
|
||||
{!loading && error && (
|
||||
<WarningPanel title="Failed to fetch ADR" message={error?.message} />
|
||||
)}
|
||||
|
||||
{!loading && !error && value?.data && (
|
||||
<MarkdownContent content={adrContent} linkTarget="_blank" />
|
||||
)}
|
||||
</InfoCard>
|
||||
);
|
||||
};
|
||||
|
||||
AdrReader.decorators = adrDecoratorFactories;
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* 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 { parseMadrWithFrontmatter } from '@backstage/plugin-adr-common';
|
||||
import { AdrContentDecorator } from './types';
|
||||
|
||||
/**
|
||||
*
|
||||
* Factory for creating default ADR content decorators. The adrDecoratorFactories
|
||||
* symbol is not directly exported, but through the AdrReader.decorators field.
|
||||
* @public
|
||||
*/
|
||||
export const adrDecoratorFactories = Object.freeze({
|
||||
/**
|
||||
* Rewrites relative Markdown links as absolute links.
|
||||
*/
|
||||
createRewriteRelativeLinksDecorator(): AdrContentDecorator {
|
||||
return ({ baseUrl, content }) => ({
|
||||
content: content.replace(
|
||||
/\[([^\[\]]*)\]\((?!https?:\/\/)(.*?)(\.md)\)/gim,
|
||||
`[$1](${baseUrl}/$2$3)`,
|
||||
),
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Rewrites relative Markdown embeds using absolute URLs.
|
||||
*/
|
||||
createRewriteRelativeEmbedsDecorator(): AdrContentDecorator {
|
||||
return ({ baseUrl, content }) => ({
|
||||
content: content.replace(
|
||||
/!\[([^\[\]]*)\]\((?!https?:\/\/)(.*?)(\.png|\.jpg|\.jpeg|\.gif|\.webp)(.*)\)/gim,
|
||||
``,
|
||||
),
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Formats YAML front-matter into a table format (if any exists in the markdown document)
|
||||
*/
|
||||
createFrontMatterFormatterDecorator(): AdrContentDecorator {
|
||||
return ({ content }) => {
|
||||
const parsedFrontmatter = parseMadrWithFrontmatter(content);
|
||||
let table = '';
|
||||
const attrs = parsedFrontmatter.attributes;
|
||||
if (Object.keys(attrs).length > 0) {
|
||||
const stripNewLines = (val: unknown) =>
|
||||
String(val).replaceAll('\n', '<br/>');
|
||||
const row = (vals: string[]) => `|${vals.join('|')}|\n`;
|
||||
table = `${row(Object.keys(attrs))}`;
|
||||
table += `${row(Object.keys(attrs).map(() => '---'))}`;
|
||||
table += `${row(Object.values(attrs).map(stripNewLines))}\n\n`;
|
||||
}
|
||||
return { content: table + parsedFrontmatter.content };
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* 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 * from './AdrReader';
|
||||
export * from './types';
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* ADR content decorator function type. Decorators are responsible for
|
||||
* performing any necessary transformations on the ADR content before rendering.
|
||||
* @public
|
||||
*/
|
||||
export type AdrContentDecorator = (adrInfo: {
|
||||
baseUrl: string;
|
||||
content: string;
|
||||
}) => { content: string };
|
||||
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
* 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 React, { useEffect, useState } from 'react';
|
||||
import { Link, useSearchParams } from 'react-router-dom';
|
||||
import useAsync from 'react-use/esm/useAsync';
|
||||
|
||||
import groupBy from 'lodash/groupBy';
|
||||
|
||||
import {
|
||||
Content,
|
||||
ContentHeader,
|
||||
InfoCard,
|
||||
Progress,
|
||||
SupportButton,
|
||||
WarningPanel,
|
||||
} from '@backstage/core-components';
|
||||
import { configApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { scmIntegrationsApiRef } from '@backstage/integration-react';
|
||||
import {
|
||||
AdrFilePathFilterFn,
|
||||
ANNOTATION_ADR_LOCATION,
|
||||
getAdrLocationUrl,
|
||||
isAdrAvailable,
|
||||
madrFilePathFilter,
|
||||
} from '@backstage/plugin-adr-common';
|
||||
import {
|
||||
useEntity,
|
||||
MissingAnnotationEmptyState,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import Box from '@material-ui/core/Box';
|
||||
import Chip from '@material-ui/core/Chip';
|
||||
import Collapse from '@material-ui/core/Collapse';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import List from '@material-ui/core/List';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { makeStyles, Theme } from '@material-ui/core/styles';
|
||||
import ExpandLess from '@material-ui/icons/ExpandLess';
|
||||
import ExpandMore from '@material-ui/icons/ExpandMore';
|
||||
import FolderIcon from '@material-ui/icons/Folder';
|
||||
|
||||
import { adrApiRef, AdrFileInfo } from '../../api';
|
||||
import { rootRouteRef } from '../../routes';
|
||||
import { AdrContentDecorator, AdrReader } from '../AdrReader';
|
||||
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
import { adrTranslationRef } from '../../translations';
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => ({
|
||||
adrMenu: {
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
},
|
||||
adrContainerTitle: {
|
||||
color: theme.palette.grey[700],
|
||||
marginBottom: theme.spacing(1),
|
||||
},
|
||||
adrBox: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
marginTop: '10px',
|
||||
},
|
||||
}));
|
||||
|
||||
const AdrListContainer = (props: {
|
||||
adrs: AdrFileInfo[];
|
||||
selectedAdr: string;
|
||||
title: string;
|
||||
}) => {
|
||||
const { adrs, selectedAdr, title } = props;
|
||||
const classes = useStyles();
|
||||
const rootLink = useRouteRef(rootRouteRef);
|
||||
const [open, setOpen] = React.useState(true);
|
||||
|
||||
const getChipColor = (status: string) => {
|
||||
switch (status) {
|
||||
case 'accepted':
|
||||
return 'primary';
|
||||
case 'rejected':
|
||||
case 'deprecated':
|
||||
return 'secondary';
|
||||
default:
|
||||
return 'default';
|
||||
}
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
setOpen(!open);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{title && (
|
||||
<ListItem
|
||||
button
|
||||
className={classes.adrContainerTitle}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<FolderIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={title} />
|
||||
{open ? <ExpandLess /> : <ExpandMore />}
|
||||
</ListItem>
|
||||
)}
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
<List dense>
|
||||
{adrs.map((adr, idx) => (
|
||||
<ListItem
|
||||
button
|
||||
component={Link}
|
||||
key={idx}
|
||||
selected={selectedAdr === adr.path}
|
||||
to={`${rootLink()}?record=${adr.path}`}
|
||||
>
|
||||
<ListItemText
|
||||
primary={adr.title ?? adr?.name.replace(/\.md$/, '')}
|
||||
primaryTypographyProps={{
|
||||
style: { whiteSpace: 'normal' },
|
||||
}}
|
||||
secondary={
|
||||
<Box className={classes.adrBox}>
|
||||
{adr.date}
|
||||
{adr.status && (
|
||||
<Chip
|
||||
color={getChipColor(adr.status)}
|
||||
label={adr.status}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Collapse>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Component for browsing ADRs on an entity page.
|
||||
* @public
|
||||
*/
|
||||
export const EntityAdrContent = (props: {
|
||||
contentDecorators?: AdrContentDecorator[];
|
||||
filePathFilterFn?: AdrFilePathFilterFn;
|
||||
}) => {
|
||||
const { contentDecorators, filePathFilterFn } = props;
|
||||
const classes = useStyles();
|
||||
const { entity } = useEntity();
|
||||
const [adrList, setAdrList] = useState<AdrFileInfo[]>([]);
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const scmIntegrations = useApi(scmIntegrationsApiRef);
|
||||
const adrApi = useApi(adrApiRef);
|
||||
const entityHasAdrs = isAdrAvailable(entity);
|
||||
const { t } = useTranslationRef(adrTranslationRef);
|
||||
|
||||
const config = useApi(configApiRef);
|
||||
const appSupportConfigured = config?.getOptionalConfig('app.support');
|
||||
|
||||
const { value, loading, error } = useAsync(async () => {
|
||||
const url = getAdrLocationUrl(entity, scmIntegrations);
|
||||
return adrApi.listAdrs(url);
|
||||
}, [entity, scmIntegrations]);
|
||||
|
||||
const selectedAdr =
|
||||
adrList.find(adr => adr.path === searchParams.get('record'))?.path ?? '';
|
||||
|
||||
const adrSubDirectoryFunc = (adr: AdrFileInfo) => {
|
||||
return adr.path.split('/').slice(0, -1).join('/');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (adrList.length && !selectedAdr) {
|
||||
searchParams.set('record', adrList[0].path);
|
||||
setSearchParams(searchParams, { replace: true });
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!value?.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const adrs: AdrFileInfo[] = value.data.filter(
|
||||
(item: AdrFileInfo) =>
|
||||
item.type === 'file' &&
|
||||
(filePathFilterFn
|
||||
? filePathFilterFn(item.path)
|
||||
: madrFilePathFilter(item.path)),
|
||||
);
|
||||
|
||||
setAdrList(adrs);
|
||||
}, [filePathFilterFn, value]);
|
||||
|
||||
const adrListGrouped = Object.entries(
|
||||
groupBy(adrList, adrSubDirectoryFunc),
|
||||
).sort();
|
||||
|
||||
return (
|
||||
<Content>
|
||||
<ContentHeader title={t('contentHeaderTitle')}>
|
||||
{appSupportConfigured && <SupportButton />}
|
||||
</ContentHeader>
|
||||
|
||||
{!entityHasAdrs && (
|
||||
<MissingAnnotationEmptyState annotation={ANNOTATION_ADR_LOCATION} />
|
||||
)}
|
||||
|
||||
{loading && <Progress />}
|
||||
|
||||
{entityHasAdrs && !loading && error && (
|
||||
<WarningPanel title={t('failedToFetch')} message={error?.message} />
|
||||
)}
|
||||
|
||||
{entityHasAdrs &&
|
||||
!loading &&
|
||||
!error &&
|
||||
(adrList.length ? (
|
||||
<Grid container direction="row">
|
||||
<Grid item xs={3}>
|
||||
<InfoCard>
|
||||
<List className={classes.adrMenu} dense>
|
||||
{adrListGrouped.map(([title, adrs], idx) => (
|
||||
<AdrListContainer
|
||||
adrs={adrs}
|
||||
key={idx}
|
||||
selectedAdr={selectedAdr}
|
||||
title={title}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
</InfoCard>
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
<AdrReader adr={selectedAdr} decorators={contentDecorators} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
) : (
|
||||
<Typography>{t('notFound')}</Typography>
|
||||
))}
|
||||
</Content>
|
||||
);
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* 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 { EntityAdrContent } from './EntityAdrContent';
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Architecture Decision Records frontend plugin.
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export { adrApiRef, AdrClient } from './api';
|
||||
export type {
|
||||
AdrApi,
|
||||
AdrClientOptions,
|
||||
AdrFileInfo,
|
||||
AdrListResult,
|
||||
AdrReadResult,
|
||||
} from './api';
|
||||
export { isAdrAvailable } from '@backstage/plugin-adr-common';
|
||||
export * from './components/AdrReader';
|
||||
export { adrPlugin, AdrSearchResultListItem, EntityAdrContent } from './plugin';
|
||||
export type { AdrSearchResultListItemProps } from './search';
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* 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 { adrPlugin } from './plugin';
|
||||
|
||||
describe('adr', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(adrPlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* 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 { adrApiRef, AdrClient } from './api';
|
||||
import {
|
||||
createApiFactory,
|
||||
createPlugin,
|
||||
createRoutableExtension,
|
||||
discoveryApiRef,
|
||||
fetchApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { createSearchResultListItemExtension } from '@backstage/plugin-search-react';
|
||||
import { rootRouteRef } from './routes';
|
||||
import { AdrSearchResultListItemProps } from './search';
|
||||
|
||||
/**
|
||||
* The Backstage plugin that holds ADR specific components
|
||||
* @public
|
||||
*/
|
||||
export const adrPlugin = createPlugin({
|
||||
id: 'adr',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: adrApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
fetchApi: fetchApiRef,
|
||||
},
|
||||
factory({ discoveryApi, fetchApi }) {
|
||||
return new AdrClient({ discoveryApi, fetchApi });
|
||||
},
|
||||
}),
|
||||
],
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* An extension for browsing ADRs on an entity page.
|
||||
* @public
|
||||
*/
|
||||
export const EntityAdrContent = adrPlugin.provide(
|
||||
createRoutableExtension({
|
||||
name: 'EntityAdrContent',
|
||||
component: () =>
|
||||
import('./components/EntityAdrContent').then(m => m.EntityAdrContent),
|
||||
mountPoint: rootRouteRef,
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* React extension used to render results on Search page or modal
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const AdrSearchResultListItem: (
|
||||
props: AdrSearchResultListItemProps,
|
||||
) => JSX.Element | null = adrPlugin.provide(
|
||||
createSearchResultListItemExtension({
|
||||
name: 'AdrSearchResultListItem',
|
||||
component: () =>
|
||||
import('./search/AdrSearchResultListItem').then(
|
||||
m => m.AdrSearchResultListItem,
|
||||
),
|
||||
predicate: result => result.type === 'adr',
|
||||
}),
|
||||
);
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* 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 { createRouteRef } from '@backstage/core-plugin-api';
|
||||
|
||||
/**
|
||||
* Root route ref for the ADR plugin
|
||||
* @public
|
||||
*/
|
||||
export const rootRouteRef = createRouteRef({
|
||||
id: 'adr',
|
||||
});
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* 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 React, { ReactNode } from 'react';
|
||||
import Box from '@material-ui/core/Box';
|
||||
import Chip from '@material-ui/core/Chip';
|
||||
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { parseEntityRef } from '@backstage/catalog-model';
|
||||
import { Link } from '@backstage/core-components';
|
||||
import { AdrDocument } from '@backstage/plugin-adr-common';
|
||||
import { humanizeEntityRef } from '@backstage/plugin-catalog-react';
|
||||
import { ResultHighlight } from '@backstage/plugin-search-common';
|
||||
import { HighlightedSearchResultText } from '@backstage/plugin-search-react';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
item: {
|
||||
display: 'flex',
|
||||
},
|
||||
flexContainer: {
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
itemText: {
|
||||
width: '100%',
|
||||
wordBreak: 'break-all',
|
||||
marginBottom: '1rem',
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type AdrSearchResultListItemProps = {
|
||||
lineClamp?: number;
|
||||
highlight?: ResultHighlight;
|
||||
icon?: ReactNode;
|
||||
rank?: number;
|
||||
result?: AdrDocument;
|
||||
};
|
||||
|
||||
/**
|
||||
* A component to display an ADR search result.
|
||||
* @public
|
||||
*/
|
||||
export function AdrSearchResultListItem(props: AdrSearchResultListItemProps) {
|
||||
const { lineClamp = 5, highlight, icon, result } = props;
|
||||
const classes = useStyles();
|
||||
|
||||
if (!result) return null;
|
||||
|
||||
return (
|
||||
<div className={classes.item}>
|
||||
{icon && <ListItemIcon>{icon}</ListItemIcon>}
|
||||
<div className={classes.flexContainer}>
|
||||
<ListItemText
|
||||
className={classes.itemText}
|
||||
primaryTypographyProps={{ variant: 'h6' }}
|
||||
primary={
|
||||
<Link noTrack to={result.location}>
|
||||
{highlight?.fields.title ? (
|
||||
<HighlightedSearchResultText
|
||||
text={highlight?.fields.title || ''}
|
||||
preTag={highlight?.preTag || ''}
|
||||
postTag={highlight?.postTag || ''}
|
||||
/>
|
||||
) : (
|
||||
result.title
|
||||
)}
|
||||
</Link>
|
||||
}
|
||||
secondary={
|
||||
<Typography
|
||||
component="span"
|
||||
style={{
|
||||
display: '-webkit-box',
|
||||
WebkitBoxOrient: 'vertical',
|
||||
WebkitLineClamp: lineClamp,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
color="textSecondary"
|
||||
variant="body2"
|
||||
>
|
||||
{highlight?.fields.text ? (
|
||||
<HighlightedSearchResultText
|
||||
text={highlight.fields.text}
|
||||
preTag={highlight.preTag}
|
||||
postTag={highlight.postTag}
|
||||
/>
|
||||
) : (
|
||||
result.text
|
||||
)}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
<Box>
|
||||
<Chip
|
||||
label={`Entity: ${
|
||||
result.entityTitle ??
|
||||
humanizeEntityRef(parseEntityRef(result.entityRef))
|
||||
}`}
|
||||
size="small"
|
||||
/>
|
||||
{result.status && (
|
||||
<Chip label={`Status: ${result.status}`} size="small" />
|
||||
)}
|
||||
{result.date && <Chip label={`Date: ${result.date}`} size="small" />}
|
||||
</Box>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* 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 type { AdrSearchResultListItemProps } from './AdrSearchResultListItem';
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* 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 '@testing-library/jest-dom';
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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 { createTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
|
||||
/** @alpha */
|
||||
export const adrTranslationRef = createTranslationRef({
|
||||
id: 'adr',
|
||||
messages: {
|
||||
contentHeaderTitle: 'Architecture Decision Records',
|
||||
failedToFetch: 'Failed to fetch ADRs',
|
||||
notFound: 'No ADRs found',
|
||||
},
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -1,914 +0,0 @@
|
||||
# @backstage/plugin-airbrake-backend
|
||||
|
||||
## 0.3.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- d5a1fe1: Replaced winston logger with `LoggerService`
|
||||
- e5a2ccc: Updated dependency `@types/http-proxy-middleware` to `^1.0.0`.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.21.7
|
||||
- @backstage/backend-plugin-api@0.6.17
|
||||
- @backstage/config@1.2.0
|
||||
|
||||
## 0.3.14-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.21.7-next.1
|
||||
- @backstage/backend-plugin-api@0.6.17-next.1
|
||||
- @backstage/config@1.2.0
|
||||
|
||||
## 0.3.14-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.21.7-next.0
|
||||
- @backstage/backend-plugin-api@0.6.17-next.0
|
||||
- @backstage/config@1.2.0
|
||||
|
||||
## 0.3.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.21.6
|
||||
- @backstage/backend-plugin-api@0.6.16
|
||||
- @backstage/config@1.2.0
|
||||
|
||||
## 0.3.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.21.5
|
||||
- @backstage/backend-plugin-api@0.6.15
|
||||
- @backstage/config@1.2.0
|
||||
|
||||
## 0.3.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 4467036: Allow unauthenticated access to health check endpoint.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.21.4
|
||||
- @backstage/config@1.2.0
|
||||
- @backstage/backend-plugin-api@0.6.14
|
||||
|
||||
## 0.3.11-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.21.4-next.2
|
||||
- @backstage/backend-plugin-api@0.6.14-next.2
|
||||
- @backstage/config@1.2.0-next.1
|
||||
|
||||
## 0.3.11-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/config@1.2.0-next.1
|
||||
- @backstage/backend-common@0.21.4-next.1
|
||||
- @backstage/backend-plugin-api@0.6.14-next.1
|
||||
|
||||
## 0.3.10-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.21.3-next.0
|
||||
- @backstage/backend-plugin-api@0.6.13-next.0
|
||||
- @backstage/config@1.1.2-next.0
|
||||
|
||||
## 0.3.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.21.0
|
||||
- @backstage/backend-plugin-api@0.6.10
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.7-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.21.0-next.3
|
||||
- @backstage/backend-plugin-api@0.6.10-next.3
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.7-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.21.0-next.2
|
||||
- @backstage/backend-plugin-api@0.6.10-next.2
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.7-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-plugin-api@0.6.10-next.1
|
||||
- @backstage/backend-common@0.21.0-next.1
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.7-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.21.0-next.0
|
||||
- @backstage/backend-plugin-api@0.6.10-next.0
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 4016f21: Remove some unused dependencies
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.20.1
|
||||
- @backstage/backend-plugin-api@0.6.9
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.6-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-plugin-api@0.6.9-next.2
|
||||
- @backstage/backend-common@0.20.1-next.2
|
||||
|
||||
## 0.3.6-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.20.1-next.1
|
||||
- @backstage/config@1.1.1
|
||||
- @backstage/backend-plugin-api@0.6.9-next.1
|
||||
|
||||
## 0.3.6-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 4016f21: Remove some unused dependencies
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.20.1-next.0
|
||||
- @backstage/backend-plugin-api@0.6.9-next.0
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.20.0
|
||||
- @backstage/backend-plugin-api@0.6.8
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.5-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.20.0-next.3
|
||||
- @backstage/backend-plugin-api@0.6.8-next.3
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.5-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.20.0-next.2
|
||||
- @backstage/backend-plugin-api@0.6.8-next.2
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.5-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.20.0-next.1
|
||||
- @backstage/backend-plugin-api@0.6.8-next.1
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.5-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.20.0-next.0
|
||||
- @backstage/backend-plugin-api@0.6.8-next.0
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.9
|
||||
- @backstage/backend-plugin-api@0.6.7
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.4-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-plugin-api@0.6.7-next.2
|
||||
- @backstage/backend-common@0.19.9-next.2
|
||||
|
||||
## 0.3.4-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.9-next.1
|
||||
- @backstage/backend-plugin-api@0.6.7-next.1
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.4-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.9-next.0
|
||||
- @backstage/backend-plugin-api@0.6.7-next.0
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.8
|
||||
- @backstage/backend-plugin-api@0.6.6
|
||||
- @backstage/config@1.1.1
|
||||
|
||||
## 0.3.3-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.8-next.2
|
||||
- @backstage/backend-plugin-api@0.6.6-next.2
|
||||
- @backstage/config@1.1.1-next.0
|
||||
|
||||
## 0.3.2-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.7-next.1
|
||||
- @backstage/backend-plugin-api@0.6.5-next.1
|
||||
- @backstage/config@1.1.0
|
||||
|
||||
## 0.3.2-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.7-next.0
|
||||
- @backstage/config@1.1.0
|
||||
- @backstage/backend-plugin-api@0.6.5-next.0
|
||||
|
||||
## 0.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 71114ac50e02: **BREAKING**: The export for the new backend system has been moved to be the `default` export.
|
||||
|
||||
For example, if you are currently importing the plugin using the following pattern:
|
||||
|
||||
```ts
|
||||
import { examplePlugin } from '@backstage/plugin-example-backend';
|
||||
|
||||
backend.add(examplePlugin);
|
||||
```
|
||||
|
||||
It should be migrated to this:
|
||||
|
||||
```ts
|
||||
backend.add(import('@backstage/plugin-example-backend'));
|
||||
```
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.5
|
||||
- @backstage/config@1.1.0
|
||||
- @backstage/backend-plugin-api@0.6.3
|
||||
|
||||
## 0.3.0-next.3
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 71114ac50e02: **BREAKING**: The export for the new backend system has been moved to be the `default` export.
|
||||
|
||||
For example, if you are currently importing the plugin using the following pattern:
|
||||
|
||||
```ts
|
||||
import { examplePlugin } from '@backstage/plugin-example-backend';
|
||||
|
||||
backend.add(examplePlugin);
|
||||
```
|
||||
|
||||
It should be migrated to this:
|
||||
|
||||
```ts
|
||||
backend.add(import('@backstage/plugin-example-backend'));
|
||||
```
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/config@1.1.0-next.2
|
||||
- @backstage/backend-plugin-api@0.6.3-next.3
|
||||
- @backstage/backend-common@0.19.5-next.3
|
||||
|
||||
## 0.2.24-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/config@1.1.0-next.1
|
||||
- @backstage/backend-common@0.19.5-next.2
|
||||
- @backstage/backend-plugin-api@0.6.3-next.2
|
||||
|
||||
## 0.2.24-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/config@1.1.0-next.0
|
||||
- @backstage/backend-common@0.19.5-next.1
|
||||
- @backstage/backend-plugin-api@0.6.3-next.1
|
||||
|
||||
## 0.2.23-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.4-next.0
|
||||
- @backstage/backend-plugin-api@0.6.2-next.0
|
||||
- @backstage/config@1.0.8
|
||||
|
||||
## 0.2.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config`
|
||||
- 12a8c94eda8d: Add package repository and homepage metadata
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.2
|
||||
- @backstage/backend-plugin-api@0.6.0
|
||||
- @backstage/config@1.0.8
|
||||
|
||||
## 0.2.21-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-plugin-api@0.6.0-next.2
|
||||
- @backstage/backend-common@0.19.2-next.2
|
||||
|
||||
## 0.2.21-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 629cbd194a87: Use `coreServices.rootConfig` instead of `coreService.config`
|
||||
- 12a8c94eda8d: Add package repository and homepage metadata
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.2-next.1
|
||||
- @backstage/backend-plugin-api@0.6.0-next.1
|
||||
- @backstage/config@1.0.8
|
||||
|
||||
## 0.2.21-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.2-next.0
|
||||
- @backstage/backend-plugin-api@0.5.5-next.0
|
||||
- @backstage/config@1.0.8
|
||||
|
||||
## 0.2.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a95bb64e461a: Added support for the [new backend system](https://backstage.io/docs/backend-system/)
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.1
|
||||
- @backstage/backend-plugin-api@0.5.4
|
||||
- @backstage/config@1.0.8
|
||||
|
||||
## 0.2.20-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a95bb64e461a: Added support for the [new backend system](https://backstage.io/docs/backend-system/)
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.1-next.0
|
||||
- @backstage/backend-plugin-api@0.5.4-next.0
|
||||
- @backstage/config@1.0.8
|
||||
|
||||
## 0.2.20-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.1-next.0
|
||||
- @backstage/config@1.0.8
|
||||
|
||||
## 0.2.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.0
|
||||
- @backstage/config@1.0.8
|
||||
|
||||
## 0.2.19-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.0-next.2
|
||||
- @backstage/config@1.0.7
|
||||
|
||||
## 0.2.19-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.19.0-next.1
|
||||
- @backstage/config@1.0.7
|
||||
|
||||
## 0.2.19-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.6-next.0
|
||||
- @backstage/config@1.0.7
|
||||
|
||||
## 0.2.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.5
|
||||
- @backstage/config@1.0.7
|
||||
|
||||
## 0.2.18-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.5-next.1
|
||||
- @backstage/config@1.0.7
|
||||
|
||||
## 0.2.18-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.5-next.0
|
||||
- @backstage/config@1.0.7
|
||||
|
||||
## 0.2.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.4
|
||||
- @backstage/config@1.0.7
|
||||
|
||||
## 0.2.17-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.4-next.2
|
||||
- @backstage/config@1.0.7
|
||||
|
||||
## 0.2.17-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.4-next.1
|
||||
- @backstage/config@1.0.7
|
||||
|
||||
## 0.2.17-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.4-next.0
|
||||
- @backstage/config@1.0.7
|
||||
|
||||
## 0.2.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 52b0022dab7: Updated dependency `msw` to `^1.0.0`.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.3
|
||||
- @backstage/config@1.0.7
|
||||
|
||||
## 0.2.16-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.3-next.2
|
||||
- @backstage/config@1.0.7-next.0
|
||||
|
||||
## 0.2.16-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 52b0022dab7: Updated dependency `msw` to `^1.0.0`.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.3-next.1
|
||||
- @backstage/config@1.0.7-next.0
|
||||
|
||||
## 0.2.16-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.3-next.0
|
||||
- @backstage/config@1.0.6
|
||||
|
||||
## 0.2.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.2
|
||||
- @backstage/config@1.0.6
|
||||
|
||||
## 0.2.15-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.2-next.2
|
||||
- @backstage/config@1.0.6
|
||||
|
||||
## 0.2.15-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.2-next.1
|
||||
- @backstage/config@1.0.6
|
||||
|
||||
## 0.2.15-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.2-next.0
|
||||
|
||||
## 0.2.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.0
|
||||
- @backstage/config@1.0.6
|
||||
|
||||
## 0.2.13-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.0-next.1
|
||||
- @backstage/config@1.0.6-next.0
|
||||
|
||||
## 0.2.13-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.18.0-next.0
|
||||
- @backstage/config@1.0.6-next.0
|
||||
|
||||
## 0.2.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 3280711113: Updated dependency `msw` to `^0.49.0`.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.17.0
|
||||
- @backstage/config@1.0.5
|
||||
|
||||
## 0.2.12-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.17.0-next.3
|
||||
- @backstage/config@1.0.5-next.1
|
||||
|
||||
## 0.2.12-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.17.0-next.2
|
||||
- @backstage/config@1.0.5-next.1
|
||||
|
||||
## 0.2.12-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.17.0-next.1
|
||||
- @backstage/config@1.0.5-next.1
|
||||
|
||||
## 0.2.12-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 3280711113: Updated dependency `msw` to `^0.49.0`.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.16.1-next.0
|
||||
- @backstage/config@1.0.5-next.0
|
||||
|
||||
## 0.2.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.16.0
|
||||
- @backstage/config@1.0.4
|
||||
|
||||
## 0.2.11-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.16.0-next.1
|
||||
- @backstage/config@1.0.4-next.0
|
||||
|
||||
## 0.2.11-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.16.0-next.0
|
||||
- @backstage/config@1.0.4-next.0
|
||||
|
||||
## 0.2.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md`
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.15.2
|
||||
- @backstage/config@1.0.3
|
||||
|
||||
## 0.2.10-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 2d3a5f09ab: Use `response.json` rather than `response.send` where appropriate, as outlined in `SECURITY.md`
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.15.2-next.2
|
||||
- @backstage/config@1.0.3-next.2
|
||||
|
||||
## 0.2.10-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.15.2-next.1
|
||||
- @backstage/config@1.0.3-next.1
|
||||
|
||||
## 0.2.10-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.15.2-next.0
|
||||
- @backstage/config@1.0.3-next.0
|
||||
|
||||
## 0.2.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 148568b5c2: Switched to using node-fetch instead of cross-fetch as is standard for our backend packages
|
||||
- 667d917488: Updated dependency `msw` to `^0.47.0`.
|
||||
- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`.
|
||||
- bf5e9030eb: Updated dependency `msw` to `^0.45.0`.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.15.1
|
||||
- @backstage/config@1.0.2
|
||||
|
||||
## 0.2.9-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/config@1.0.2-next.0
|
||||
- @backstage/backend-common@0.15.1-next.3
|
||||
|
||||
## 0.2.9-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 667d917488: Updated dependency `msw` to `^0.47.0`.
|
||||
- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.15.1-next.2
|
||||
|
||||
## 0.2.9-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 148568b5c2: Switched to using node-fetch instead of cross-fetch as is standard for our backend packages
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.15.1-next.1
|
||||
|
||||
## 0.2.9-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- bf5e9030eb: Updated dependency `msw` to `^0.45.0`.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.15.1-next.0
|
||||
|
||||
## 0.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.15.0
|
||||
|
||||
## 0.2.8-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.15.0-next.0
|
||||
|
||||
## 0.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a70869e775: Updated dependency `msw` to `^0.43.0`.
|
||||
- 8006d0f9bf: Updated dependency `msw` to `^0.44.0`.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.14.1
|
||||
|
||||
## 0.2.7-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a70869e775: Updated dependency `msw` to `^0.43.0`.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.14.1-next.3
|
||||
|
||||
## 0.2.7-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.14.1-next.0
|
||||
|
||||
## 0.2.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 8f7b1835df: Updated dependency `msw` to `^0.41.0`.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.14.0
|
||||
|
||||
## 0.2.6-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.14.0-next.2
|
||||
|
||||
## 0.2.6-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 8f7b1835df: Updated dependency `msw` to `^0.41.0`.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.13.6-next.1
|
||||
|
||||
## 0.2.6-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.13.6-next.0
|
||||
|
||||
## 0.2.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.13.3
|
||||
- @backstage/config@1.0.1
|
||||
|
||||
## 0.2.5-next.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.13.3-next.2
|
||||
- @backstage/config@1.0.1-next.0
|
||||
|
||||
## 0.2.5-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.13.3-next.0
|
||||
|
||||
## 0.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.13.2
|
||||
|
||||
## 0.2.4-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.13.2-next.0
|
||||
|
||||
## 0.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.13.1
|
||||
- @backstage/config@1.0.0
|
||||
|
||||
## 0.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 4f79204c47: Added `backstage.role` to `package.json`
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.13.0
|
||||
|
||||
## 0.2.2-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 4f79204c47: Added `backstage.role` to `package.json`
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.13.0-next.0
|
||||
|
||||
## 0.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 3c1d3cb07e: The Airbrake plugin installation instructions have been updated to work better and conform to how the frontend and backend plugins are supposed to be integrated into a Backstage instance.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.12.0
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- da78e79a94: This marks the first release where the Airbrake plugin is useable. Airbrake frontend and Airbrake backend work with each other. Currently just a summary of the latest Airbrakes is shown on Backstage.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 0045270527: Make config optional
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.11.0
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- a5aebe3e30: Created the Airbrake backend plugin that proxies requests to the Airbrake API
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.10.8
|
||||
- @backstage/config@0.1.14
|
||||
@@ -1,3 +0,0 @@
|
||||
# Deprecated
|
||||
|
||||
This package has been moved to the [backstage-community/plugins](https://github.com/backstage/community-plugins) repository. Migrate to using `@backstage-community/plugin-airbrake-backend` instead.
|
||||
@@ -1,31 +0,0 @@
|
||||
## API Report File for "@backstage/plugin-airbrake-backend"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { Config } from '@backstage/config';
|
||||
import express from 'express';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
|
||||
// @public
|
||||
export interface AirbrakeConfig {
|
||||
apiKey: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
const airbrakePlugin: () => BackendFeature;
|
||||
export default airbrakePlugin;
|
||||
|
||||
// @public
|
||||
export function createRouter(options: RouterOptions): Promise<express.Router>;
|
||||
|
||||
// @public
|
||||
export function extractAirbrakeConfig(config: Config): AirbrakeConfig;
|
||||
|
||||
// @public
|
||||
export interface RouterOptions {
|
||||
airbrakeConfig: AirbrakeConfig;
|
||||
logger: LoggerService;
|
||||
}
|
||||
```
|
||||
@@ -1,9 +0,0 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: backstage-plugin-airbrake-backend
|
||||
title: '@backstage/plugin-airbrake-backend'
|
||||
spec:
|
||||
lifecycle: experimental
|
||||
type: backstage-backend-plugin
|
||||
owner: maintainers
|
||||
Vendored
-28
@@ -1,28 +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.
|
||||
*/
|
||||
|
||||
export interface Config {
|
||||
/** Configuration options for the Airbrake plugin */
|
||||
airbrake?: {
|
||||
/**
|
||||
* The API Key to authenticate requests. More details on how to get this
|
||||
* here: https://airbrake.io/docs/api/#authentication
|
||||
*
|
||||
* @visibility secret
|
||||
*/
|
||||
apiKey: string;
|
||||
};
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
# Knip report
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
{
|
||||
"name": "@backstage/plugin-airbrake-backend",
|
||||
"version": "0.3.14",
|
||||
"backstage": {
|
||||
"role": "backend-plugin",
|
||||
"moved": "@backstage-community/plugin-airbrake-backend"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "plugins/airbrake-backend"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"config.d.ts"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
"clean": "backstage-cli package clean",
|
||||
"lint": "backstage-cli package lint",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack",
|
||||
"start": "backstage-cli package start",
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "workspace:^",
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/config": "workspace:^",
|
||||
"@types/express": "*",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"http-proxy-middleware": "^2.0.0",
|
||||
"yn": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/backend-test-utils": "workspace:^",
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@types/http-proxy-middleware": "^1.0.0",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"supertest": "^6.1.6"
|
||||
},
|
||||
"configSchema": "config.d.ts",
|
||||
"deprecated": "This package has been moved to the backstage/community-plugins repository. You should migrate to using @backstage-community/plugin-airbrake-backend instead."
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* 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 { ConfigReader } from '@backstage/config';
|
||||
import { extractAirbrakeConfig } from './ExtractAirbrakeConfig';
|
||||
|
||||
describe('ExtractAirbrakeConfig', () => {
|
||||
it('gets the API key', () => {
|
||||
const config = new ConfigReader({
|
||||
airbrake: {
|
||||
apiKey: 'fakeApiKey',
|
||||
},
|
||||
});
|
||||
|
||||
const airbrakeConfig = extractAirbrakeConfig(config);
|
||||
expect(airbrakeConfig.apiKey).toStrictEqual('fakeApiKey');
|
||||
});
|
||||
|
||||
it('fails for missing keys', () => {
|
||||
expect(() => extractAirbrakeConfig(new ConfigReader({}))).toThrow();
|
||||
expect(() =>
|
||||
extractAirbrakeConfig(new ConfigReader({ airbrake: {} })),
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* 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 { Config } from '@backstage/config';
|
||||
|
||||
/**
|
||||
* The configuration needed for the airbrake-backend plugin
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface AirbrakeConfig {
|
||||
/**
|
||||
* The API Key to authenticate requests. More details on how to get this here: https://airbrake.io/docs/api/#authentication
|
||||
*/
|
||||
apiKey: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the Airbrake config from a config object
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @param config - The config object to extract from
|
||||
*/
|
||||
export function extractAirbrakeConfig(config: Config): AirbrakeConfig {
|
||||
return {
|
||||
apiKey: config.getString('airbrake.apiKey'),
|
||||
};
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* 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 * from './ExtractAirbrakeConfig';
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Airbrake Backend plugin used by \@backstage/plugin-airbrake
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export * from './service/router';
|
||||
export * from './config';
|
||||
export { airbrakePlugin as default } from './plugin';
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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 {
|
||||
coreServices,
|
||||
createBackendPlugin,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { createRouter } from './service/router';
|
||||
import { extractAirbrakeConfig } from './config';
|
||||
|
||||
/**
|
||||
* The Airbrake Backend plugin.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const airbrakePlugin = createBackendPlugin({
|
||||
pluginId: 'airbrake',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: {
|
||||
config: coreServices.rootConfig,
|
||||
logger: coreServices.logger,
|
||||
httpRouter: coreServices.httpRouter,
|
||||
},
|
||||
async init({ logger, httpRouter, config }) {
|
||||
httpRouter.use(
|
||||
await createRouter({
|
||||
airbrakeConfig: extractAirbrakeConfig(config),
|
||||
logger,
|
||||
}),
|
||||
);
|
||||
httpRouter.addAuthPolicy({
|
||||
path: '/health',
|
||||
allow: 'unauthenticated',
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 { getRootLogger } from '@backstage/backend-common';
|
||||
import yn from 'yn';
|
||||
import { startStandaloneServer } from './service/standaloneServer';
|
||||
|
||||
const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7007;
|
||||
const enableCors = yn(process.env.PLUGIN_CORS, { default: false });
|
||||
const logger = getRootLogger();
|
||||
|
||||
startStandaloneServer({ port, enableCors, logger }).catch(err => {
|
||||
logger.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
logger.info('CTRL+C pressed; exiting.');
|
||||
process.exit(0);
|
||||
});
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 express from 'express';
|
||||
import request from 'supertest';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import {
|
||||
createRouter,
|
||||
generateAirbrakePathRewrite,
|
||||
RouterOptions,
|
||||
} from './router';
|
||||
import { AirbrakeConfig, extractAirbrakeConfig } from '../config';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { mockServices } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('createRouter', () => {
|
||||
let app: express.Express;
|
||||
let airbrakeConfig: AirbrakeConfig;
|
||||
let voidLogger: LoggerService;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.resetAllMocks();
|
||||
|
||||
voidLogger = mockServices.logger.mock();
|
||||
const config = new ConfigReader({
|
||||
airbrake: {
|
||||
apiKey: 'fakeApiKey',
|
||||
},
|
||||
});
|
||||
airbrakeConfig = extractAirbrakeConfig(config);
|
||||
|
||||
const router = await createRouter({
|
||||
logger: voidLogger,
|
||||
airbrakeConfig,
|
||||
});
|
||||
app = express().use(router);
|
||||
});
|
||||
|
||||
describe('GET /health', () => {
|
||||
it('returns ok', async () => {
|
||||
const response = await request(app).get('/health');
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual({ status: 'ok' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /api', () => {
|
||||
it('appends the API Key properly with no other url parameters', () => {
|
||||
const options: RouterOptions = {
|
||||
logger: voidLogger,
|
||||
airbrakeConfig,
|
||||
};
|
||||
const pathRewrite = generateAirbrakePathRewrite(options) as (
|
||||
path: string,
|
||||
) => string;
|
||||
|
||||
expect(pathRewrite('/airbrake-backend/api/v4/random/endpoint')).toBe(
|
||||
'/v4/random/endpoint?key=fakeApiKey',
|
||||
);
|
||||
});
|
||||
|
||||
it('appends the API Key properly despite there being other URL parameters', () => {
|
||||
const options: RouterOptions = {
|
||||
logger: voidLogger,
|
||||
airbrakeConfig,
|
||||
};
|
||||
const pathRewrite = generateAirbrakePathRewrite(options) as (
|
||||
path: string,
|
||||
) => string;
|
||||
|
||||
expect(
|
||||
pathRewrite(
|
||||
'/airbrake-backend/api/v4/random/endpoint?param1=123¶m2=abc',
|
||||
),
|
||||
).toBe('/v4/random/endpoint?param1=123¶m2=abc&key=fakeApiKey');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 { errorHandler } from '@backstage/backend-common';
|
||||
import express from 'express';
|
||||
import Router from 'express-promise-router';
|
||||
import { createProxyMiddleware } from 'http-proxy-middleware';
|
||||
import { AirbrakeConfig } from '../config';
|
||||
import { Options } from 'http-proxy-middleware/dist/types';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
|
||||
/**
|
||||
* The router options that are needed when creating a router.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface RouterOptions {
|
||||
/**
|
||||
* A logger object
|
||||
*/
|
||||
logger: LoggerService;
|
||||
|
||||
/**
|
||||
* The Airbrake config obtained from {@link extractAirbrakeConfig}
|
||||
*/
|
||||
airbrakeConfig: AirbrakeConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mainly used internally to generate the path.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @param options - Router options
|
||||
*/
|
||||
export const generateAirbrakePathRewrite = (
|
||||
options: RouterOptions,
|
||||
): Options['pathRewrite'] => {
|
||||
const apiKey = options.airbrakeConfig.apiKey;
|
||||
|
||||
return path => {
|
||||
let newPath = path.replace(/.+?(\/api)/g, '');
|
||||
if (newPath.includes('?')) {
|
||||
newPath += `&key=${apiKey}`;
|
||||
} else {
|
||||
newPath += `?key=${apiKey}`;
|
||||
}
|
||||
return newPath;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the Airbrake Router, used for making API calls to the Airbrake API.
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @param options - Router options
|
||||
*/
|
||||
export async function createRouter(
|
||||
options: RouterOptions,
|
||||
): Promise<express.Router> {
|
||||
const { logger } = options;
|
||||
|
||||
const router = Router();
|
||||
router.use(express.json());
|
||||
|
||||
router.get('/health', (_, response) => {
|
||||
logger.info('PONG!');
|
||||
response.json({ status: 'ok' });
|
||||
});
|
||||
|
||||
router.use(
|
||||
'/api',
|
||||
createProxyMiddleware({
|
||||
target: 'https://api.airbrake.io/api',
|
||||
changeOrigin: true,
|
||||
pathRewrite: generateAirbrakePathRewrite(options),
|
||||
}),
|
||||
);
|
||||
|
||||
router.use(errorHandler());
|
||||
return router;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 {
|
||||
createServiceBuilder,
|
||||
loadBackendConfig,
|
||||
} from '@backstage/backend-common';
|
||||
import { Server } from 'http';
|
||||
import { createRouter } from './router';
|
||||
import { extractAirbrakeConfig } from '../config';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
|
||||
export interface ServerOptions {
|
||||
port: number;
|
||||
enableCors: boolean;
|
||||
logger: LoggerService;
|
||||
}
|
||||
|
||||
export async function startStandaloneServer(
|
||||
options: ServerOptions,
|
||||
): Promise<Server> {
|
||||
const logger = options.logger.child({ service: 'airbrake-backend' });
|
||||
const config = await loadBackendConfig({ logger, argv: process.argv });
|
||||
const airbrakeConfig = extractAirbrakeConfig(config);
|
||||
logger.debug('Starting application server...');
|
||||
const router = await createRouter({
|
||||
logger,
|
||||
airbrakeConfig,
|
||||
});
|
||||
|
||||
let service = createServiceBuilder(module)
|
||||
.setPort(options.port)
|
||||
.addRouter('/api/airbrake', router);
|
||||
if (options.enableCors) {
|
||||
logger.info('CORS is enabled, limiting to localhost with port 3000');
|
||||
service = service.enableCors({ origin: 'http://localhost:3000' });
|
||||
} else {
|
||||
logger.info('CORS is disabled, allowing all origins');
|
||||
service = service.enableCors({ origin: '*' });
|
||||
}
|
||||
|
||||
return await service.start().catch(err => {
|
||||
logger.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.hot?.accept();
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 {};
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, {
|
||||
rules: {
|
||||
'@backstage/no-top-level-material-ui-4-imports': 'error',
|
||||
},
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
# Deprecated
|
||||
|
||||
This package has been moved to the [backstage-community/plugins](https://github.com/backstage/community-plugins) repository. Migrate to using `@backstage-community/plugin-airbrake` instead.
|
||||
@@ -1,24 +0,0 @@
|
||||
## API Report File for "@backstage/plugin-airbrake"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { BackstagePlugin } from '@backstage/core-plugin-api';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { default as React_2 } from 'react';
|
||||
import { RouteRef } from '@backstage/core-plugin-api';
|
||||
|
||||
// @public
|
||||
export const airbrakePlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{}
|
||||
>;
|
||||
|
||||
// @public
|
||||
export const EntityAirbrakeContent: () => React_2.JSX.Element;
|
||||
|
||||
// @public
|
||||
export const isAirbrakeAvailable: (entity: Entity) => boolean;
|
||||
```
|
||||
@@ -1,9 +0,0 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: backstage-plugin-airbrake
|
||||
title: '@backstage/plugin-airbrake'
|
||||
spec:
|
||||
lifecycle: experimental
|
||||
type: backstage-frontend-plugin
|
||||
owner: maintainers
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* 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 React from 'react';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { Context } from '../ContextProvider';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
gap: '1em',
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
label: {
|
||||
color: `${theme.palette.common.white} !important`,
|
||||
},
|
||||
outline: {
|
||||
color: `${theme.palette.common.white} !important`,
|
||||
borderColor: `${theme.palette.common.white} !important`,
|
||||
},
|
||||
}));
|
||||
|
||||
export const ApiBar = () => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Context.Consumer>
|
||||
{value => (
|
||||
<div className={classes.root}>
|
||||
<TextField
|
||||
label="Project ID"
|
||||
variant="outlined"
|
||||
defaultValue={value.projectId}
|
||||
InputLabelProps={{ classes: { root: classes.label } }}
|
||||
InputProps={{ classes: { notchedOutline: classes.outline } }}
|
||||
onChange={e =>
|
||||
value.setProjectId?.(parseInt(e.target.value, 10) || undefined)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Context.Consumer>
|
||||
);
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 { ApiBar } from './ApiBar';
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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 React, {
|
||||
Dispatch,
|
||||
PropsWithChildren,
|
||||
SetStateAction,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
interface ContextInterface {
|
||||
projectId?: number;
|
||||
setProjectId?: Dispatch<SetStateAction<number | undefined>>;
|
||||
}
|
||||
|
||||
export const Context = React.createContext<ContextInterface>({});
|
||||
|
||||
export const ContextProvider = ({ children }: PropsWithChildren<{}>) => {
|
||||
const [projectId, setProjectId] = useState<number>();
|
||||
|
||||
return (
|
||||
<Context.Provider
|
||||
value={{
|
||||
projectId,
|
||||
setProjectId,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
);
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 { ContextProvider, Context } from './ContextProvider';
|
||||
@@ -1,88 +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 React from 'react';
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { airbrakePlugin, EntityAirbrakeContent } from '../src';
|
||||
import {
|
||||
airbrakeApiRef,
|
||||
localDiscoveryApi,
|
||||
MockAirbrakeApi,
|
||||
ProductionAirbrakeApi,
|
||||
} from '../src/api';
|
||||
import { ApiBar } from './components/ApiBar';
|
||||
import { Content, Header, Page } from '@backstage/core-components';
|
||||
import { EntityProvider } from '@backstage/plugin-catalog-react';
|
||||
import { createEntity } from '../src/api';
|
||||
import CloudOffIcon from '@material-ui/icons/CloudOff';
|
||||
import CloudIcon from '@material-ui/icons/Cloud';
|
||||
import { Context, ContextProvider } from './components/ContextProvider';
|
||||
|
||||
createDevApp()
|
||||
.registerPlugin(airbrakePlugin)
|
||||
.addPage({
|
||||
element: (
|
||||
<Page themeId="tool">
|
||||
<Header title="Airbrake demo application" subtitle="Mock API" />
|
||||
<Content>
|
||||
<TestApiProvider apis={[[airbrakeApiRef, new MockAirbrakeApi(800)]]}>
|
||||
<EntityProvider entity={createEntity(1234)}>
|
||||
<EntityAirbrakeContent />
|
||||
</EntityProvider>
|
||||
</TestApiProvider>
|
||||
</Content>
|
||||
</Page>
|
||||
),
|
||||
title: 'Mock API',
|
||||
path: '/airbrake-mock-api',
|
||||
icon: CloudOffIcon,
|
||||
})
|
||||
.addPage({
|
||||
element: (
|
||||
<ContextProvider>
|
||||
<Page themeId="tool">
|
||||
<Header
|
||||
title="Airbrake demo application"
|
||||
subtitle="Real API (The Airbrake backend plugin must be running for this to work)"
|
||||
>
|
||||
<ApiBar />
|
||||
</Header>
|
||||
<Content>
|
||||
<Context.Consumer>
|
||||
{value => (
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[
|
||||
airbrakeApiRef,
|
||||
new ProductionAirbrakeApi(localDiscoveryApi),
|
||||
],
|
||||
]}
|
||||
>
|
||||
<EntityProvider entity={createEntity(value.projectId)}>
|
||||
<EntityAirbrakeContent />
|
||||
</EntityProvider>
|
||||
</TestApiProvider>
|
||||
)}
|
||||
</Context.Consumer>
|
||||
</Content>
|
||||
</Page>
|
||||
</ContextProvider>
|
||||
),
|
||||
title: 'Real API',
|
||||
path: '/airbrake-real-api',
|
||||
icon: CloudIcon,
|
||||
})
|
||||
.render();
|
||||
@@ -1,10 +0,0 @@
|
||||
# Knip report
|
||||
|
||||
## Unused devDependencies (3)
|
||||
|
||||
| Name | Location | Severity |
|
||||
| :---------------------- | :----------- | :------- |
|
||||
| @backstage/app-defaults | package.json | error |
|
||||
| @testing-library/react | package.json | error |
|
||||
| @testing-library/dom | package.json | error |
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
{
|
||||
"name": "@backstage/plugin-airbrake",
|
||||
"version": "0.3.34",
|
||||
"backstage": {
|
||||
"role": "frontend-plugin",
|
||||
"moved": "@backstage-community/plugin-airbrake"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "plugins/airbrake"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"sideEffects": false,
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
"clean": "backstage-cli package clean",
|
||||
"lint": "backstage-cli package lint",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack",
|
||||
"start": "backstage-cli package start",
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
"@backstage/core-components": "workspace:^",
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/dev-utils": "workspace:^",
|
||||
"@backstage/plugin-catalog-react": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
"react-use": "^17.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/app-defaults": "workspace:^",
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/dev-utils": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@testing-library/dom": "^10.0.0",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
"@testing-library/react": "^15.0.0",
|
||||
"msw": "^1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
"react-router-dom": "6.0.0-beta.0 || ^6.3.0"
|
||||
},
|
||||
"deprecated": "This package has been moved to the backstage/community-plugins repository. You should migrate to using @backstage-community/plugin-airbrake instead."
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 { Groups } from './airbrakeGroups';
|
||||
import { createApiRef } from '@backstage/core-plugin-api';
|
||||
|
||||
export const airbrakeApiRef = createApiRef<AirbrakeApi>({
|
||||
id: 'plugin.airbrake.service',
|
||||
});
|
||||
|
||||
export interface AirbrakeApi {
|
||||
fetchGroups(projectId: string): Promise<Groups>;
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* 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 { ProductionAirbrakeApi } from './ProductionApi';
|
||||
import { rest } from 'msw';
|
||||
import mockGroupsData from './mock/airbrakeGroupsApiMock.json';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { setupRequestMockHandlers } from '@backstage/test-utils';
|
||||
import { localDiscoveryApi } from './mock';
|
||||
|
||||
describe('The production Airbrake API', () => {
|
||||
const productionApi = new ProductionAirbrakeApi(localDiscoveryApi);
|
||||
const worker = setupServer();
|
||||
setupRequestMockHandlers(worker);
|
||||
|
||||
it('fetches groups using the provided project ID', async () => {
|
||||
worker.use(
|
||||
rest.get(
|
||||
'http://localhost:7007/api/airbrake/api/v4/projects/123456/groups',
|
||||
(_, res, ctx) => {
|
||||
return res(ctx.status(200), ctx.json(mockGroupsData));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
const groups = await productionApi.fetchGroups('123456');
|
||||
expect(groups).toStrictEqual(mockGroupsData);
|
||||
});
|
||||
|
||||
it('throws if fetching groups was unsuccessful', async () => {
|
||||
worker.use(
|
||||
rest.get(
|
||||
'http://localhost:7007/api/airbrake/api/v4/projects/123456/groups',
|
||||
(_, res, ctx) => {
|
||||
return res(ctx.status(500));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await expect(productionApi.fetchGroups('123456')).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 { Groups } from './airbrakeGroups';
|
||||
import { AirbrakeApi } from './AirbrakeApi';
|
||||
import { DiscoveryApi } from '@backstage/core-plugin-api';
|
||||
|
||||
export class ProductionAirbrakeApi implements AirbrakeApi {
|
||||
constructor(private readonly discoveryApi: DiscoveryApi) {}
|
||||
|
||||
async fetchGroups(projectId: string): Promise<Groups> {
|
||||
const baseUrl = await this.discoveryApi.getBaseUrl('airbrake');
|
||||
const apiUrl = `${baseUrl}/api/v4/projects/${projectId}/groups`;
|
||||
|
||||
const response = await fetch(apiUrl);
|
||||
|
||||
if (response.status >= 400 && response.status < 600) {
|
||||
throw new Error('Failed fetching Airbrake groups');
|
||||
}
|
||||
|
||||
return (await response.json()) as Groups;
|
||||
}
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* 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 interface Groups {
|
||||
count: number;
|
||||
end: string;
|
||||
groups?: Group[] | null;
|
||||
page: number;
|
||||
resolvedCount: number;
|
||||
unresolvedCount: number;
|
||||
}
|
||||
|
||||
export interface Group {
|
||||
id: string;
|
||||
projectId: number;
|
||||
resolved: boolean;
|
||||
muted: boolean;
|
||||
mutedBy: number;
|
||||
mutedAt?: string | null;
|
||||
errors?: Error[];
|
||||
attributes?: string | null;
|
||||
context: Context;
|
||||
lastDeployId: string;
|
||||
lastDeployAt?: string | null;
|
||||
lastNoticeId: string;
|
||||
lastNoticeAt: string;
|
||||
noticeCount: number;
|
||||
noticeTotalCount: number;
|
||||
commentCount: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface Error {
|
||||
type: string;
|
||||
message: string;
|
||||
backtrace?: Backtrace[];
|
||||
}
|
||||
|
||||
export interface Backtrace {
|
||||
file: string;
|
||||
function: string;
|
||||
line: number;
|
||||
column: number;
|
||||
code?: string | null;
|
||||
}
|
||||
|
||||
export interface Context {
|
||||
action: string;
|
||||
component: string;
|
||||
environment: string;
|
||||
severity: string;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 * from './mock';
|
||||
export type { AirbrakeApi } from './AirbrakeApi';
|
||||
export { airbrakeApiRef } from './AirbrakeApi';
|
||||
export type { Groups } from './airbrakeGroups';
|
||||
export { ProductionAirbrakeApi } from './ProductionApi';
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* 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 { DiscoveryApi } from '@backstage/core-plugin-api';
|
||||
|
||||
export const localDiscoveryApi: DiscoveryApi = {
|
||||
async getBaseUrl(pluginId: string): Promise<string> {
|
||||
return `http://localhost:7007/api/${pluginId}`;
|
||||
},
|
||||
};
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 { Groups } from '../airbrakeGroups';
|
||||
import { AirbrakeApi } from '../AirbrakeApi';
|
||||
import mockGroupsData from './airbrakeGroupsApiMock.json';
|
||||
|
||||
export class MockAirbrakeApi implements AirbrakeApi {
|
||||
waitTimeInMillis: number;
|
||||
|
||||
constructor(waitTimeInMillis = 10) {
|
||||
this.waitTimeInMillis = waitTimeInMillis;
|
||||
}
|
||||
|
||||
fetchGroups(): Promise<Groups> {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => resolve(mockGroupsData), this.waitTimeInMillis);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* 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 { AIRBRAKE_PROJECT_ID_ANNOTATION } from '../../components/useProjectId';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
|
||||
export const createEntity = (projectId?: number) => {
|
||||
const projectIdString = projectId?.toString();
|
||||
|
||||
return {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
annotations: {
|
||||
[AIRBRAKE_PROJECT_ID_ANNOTATION]: projectIdString,
|
||||
},
|
||||
name: projectIdString,
|
||||
},
|
||||
} as Entity;
|
||||
};
|
||||
@@ -1,85 +0,0 @@
|
||||
{
|
||||
"count": 2,
|
||||
"end": "",
|
||||
"groups": [
|
||||
{
|
||||
"id": "1",
|
||||
"projectId": 123,
|
||||
"resolved": false,
|
||||
"muted": false,
|
||||
"mutedBy": 0,
|
||||
"mutedAt": null,
|
||||
"errors": [
|
||||
{
|
||||
"type": "Error",
|
||||
"message": "useSearch must be used within a SearchContextProvider",
|
||||
"backtrace": [
|
||||
{
|
||||
"file": "webpack-internal:///../../node_modules/@backstage/plugin-search/dist/esm/index-893ec2f5.esm.js",
|
||||
"function": "useSearch",
|
||||
"line": 303,
|
||||
"column": 11,
|
||||
"code": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"attributes": null,
|
||||
"context": {
|
||||
"action": "",
|
||||
"component": "",
|
||||
"environment": "local",
|
||||
"severity": "error"
|
||||
},
|
||||
"lastDeployId": "0",
|
||||
"lastDeployAt": null,
|
||||
"lastNoticeId": "234",
|
||||
"lastNoticeAt": "2021-12-19T09:59:00.124Z",
|
||||
"noticeCount": 5,
|
||||
"noticeTotalCount": 5,
|
||||
"commentCount": 0,
|
||||
"createdAt": "2021-12-19T09:44:30.067447Z"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"projectId": 123,
|
||||
"resolved": true,
|
||||
"muted": false,
|
||||
"mutedBy": 0,
|
||||
"mutedAt": null,
|
||||
"errors": [
|
||||
{
|
||||
"type": "ChunkLoadError",
|
||||
"message": "Loading chunk 7764 failed.",
|
||||
"backtrace": [
|
||||
{
|
||||
"file": "/PROJECT_ROOT/static/runtime.069c874d.js",
|
||||
"function": "Object._.f.j",
|
||||
"line": 1,
|
||||
"column": 19465,
|
||||
"code": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"attributes": null,
|
||||
"context": {
|
||||
"action": "",
|
||||
"component": "",
|
||||
"environment": "local",
|
||||
"severity": "error"
|
||||
},
|
||||
"lastDeployId": "0",
|
||||
"lastDeployAt": null,
|
||||
"lastNoticeId": "345",
|
||||
"lastNoticeAt": "2021-12-15T17:16:38.419Z",
|
||||
"noticeCount": 1,
|
||||
"noticeTotalCount": 1,
|
||||
"commentCount": 0,
|
||||
"createdAt": "2021-12-15T17:16:38.41983Z"
|
||||
}
|
||||
],
|
||||
"page": 1,
|
||||
"resolvedCount": 1,
|
||||
"unresolvedCount": 1
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 * from './MockApi';
|
||||
export * from './MockEntity';
|
||||
export * from './LocalDiscoveryApi';
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 React from 'react';
|
||||
import { EntityAirbrakeWidget } from './EntityAirbrakeWidget';
|
||||
import exampleData from '../../api/mock/airbrakeGroupsApiMock.json';
|
||||
import {
|
||||
renderInTestApp,
|
||||
setupRequestMockHandlers,
|
||||
TestApiProvider,
|
||||
} from '@backstage/test-utils';
|
||||
import {
|
||||
airbrakeApiRef,
|
||||
createEntity,
|
||||
localDiscoveryApi,
|
||||
MockAirbrakeApi,
|
||||
ProductionAirbrakeApi,
|
||||
} from '../../api';
|
||||
import { rest } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
|
||||
describe('EntityAirbrakeWidget', () => {
|
||||
const worker = setupServer();
|
||||
setupRequestMockHandlers(worker);
|
||||
|
||||
it('renders all errors sent from Airbrake', async () => {
|
||||
const widget = await renderInTestApp(
|
||||
<TestApiProvider apis={[[airbrakeApiRef, new MockAirbrakeApi()]]}>
|
||||
<EntityAirbrakeWidget entity={createEntity(123)} />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
expect(exampleData.groups.length).toBeGreaterThan(0);
|
||||
for (const group of exampleData.groups) {
|
||||
expect(
|
||||
await widget.findByText(group.errors[0].message),
|
||||
).toBeInTheDocument();
|
||||
}
|
||||
});
|
||||
|
||||
it('states that the annotation is missing if no project ID annotation is provided but does not error', async () => {
|
||||
const widget = await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[[airbrakeApiRef, new ProductionAirbrakeApi(localDiscoveryApi)]]}
|
||||
>
|
||||
<EntityAirbrakeWidget entity={createEntity()} />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
await expect(
|
||||
widget.findByText('Missing Annotation'),
|
||||
).resolves.toBeInTheDocument();
|
||||
expect(
|
||||
widget.queryByText(/.*Failed fetching Airbrake groups.*/),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('states that an error occurred if the API call fails', async () => {
|
||||
worker.use(
|
||||
rest.get(
|
||||
'http://localhost:7007/api/airbrake/api/v4/projects/123/groups',
|
||||
(_, res, ctx) => {
|
||||
return res(ctx.status(500));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
const widget = await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[[airbrakeApiRef, new ProductionAirbrakeApi(localDiscoveryApi)]]}
|
||||
>
|
||||
<EntityAirbrakeWidget entity={createEntity(123)} />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
await expect(
|
||||
widget.findByText(/.*there was an issue communicating with Airbrake.*/),
|
||||
).resolves.toBeInTheDocument();
|
||||
expect(
|
||||
widget.getByRole('heading', {
|
||||
name: /.*Failed fetching Airbrake groups.*/,
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,90 +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 {
|
||||
EmptyState,
|
||||
ErrorPanel,
|
||||
InfoCard,
|
||||
Progress,
|
||||
} from '@backstage/core-components';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import React from 'react';
|
||||
import useAsync from 'react-use/esm/useAsync';
|
||||
import { airbrakeApiRef } from '../../api';
|
||||
import { MissingAnnotationEmptyState } from '@backstage/plugin-catalog-react';
|
||||
import { AIRBRAKE_PROJECT_ID_ANNOTATION, useProjectId } from '../useProjectId';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
multilineText: {
|
||||
whiteSpace: 'pre-wrap',
|
||||
},
|
||||
});
|
||||
|
||||
export const EntityAirbrakeWidget = ({ entity }: { entity: Entity }) => {
|
||||
const classes = useStyles();
|
||||
|
||||
const projectId = useProjectId(entity);
|
||||
const airbrakeApi = useApi(airbrakeApiRef);
|
||||
|
||||
const { loading, value, error } = useAsync(() => {
|
||||
if (!projectId) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
return airbrakeApi.fetchGroups(projectId);
|
||||
}, [airbrakeApi, projectId]);
|
||||
|
||||
if (!projectId) {
|
||||
return (
|
||||
<MissingAnnotationEmptyState
|
||||
annotation={AIRBRAKE_PROJECT_ID_ANNOTATION}
|
||||
/>
|
||||
);
|
||||
} else if (loading) {
|
||||
return <Progress />;
|
||||
} else if (value) {
|
||||
return (
|
||||
<Grid container spacing={3} direction="column">
|
||||
{value.groups?.map(group => (
|
||||
<Grid item key={group.id}>
|
||||
{group.errors?.map((groupError, i) => (
|
||||
<InfoCard title={groupError.type} key={i}>
|
||||
<Typography variant="body1" className={classes.multilineText}>
|
||||
{groupError.message}
|
||||
</Typography>
|
||||
</InfoCard>
|
||||
))}
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{error && <ErrorPanel error={error} />}
|
||||
<EmptyState
|
||||
missing="info"
|
||||
title="No information to display"
|
||||
description={`There is no Airbrake project with id '${projectId}' or there was an issue communicating with Airbrake.`}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 { EntityAirbrakeWidget } from './EntityAirbrakeWidget';
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 { AIRBRAKE_PROJECT_ID_ANNOTATION } from './useProjectId';
|
||||
|
||||
/**
|
||||
* Utility function to determine if the given entity has an Airbrake ID set in the repos catalog-info.yml .
|
||||
* @public
|
||||
*/
|
||||
export const isAirbrakeAvailable = (entity: Entity) =>
|
||||
Boolean(entity.metadata.annotations?.[AIRBRAKE_PROJECT_ID_ANNOTATION]);
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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';
|
||||
|
||||
export const AIRBRAKE_PROJECT_ID_ANNOTATION = 'airbrake.io/project-id';
|
||||
|
||||
export const useProjectId = (entity: Entity) => {
|
||||
return entity?.metadata.annotations?.[AIRBRAKE_PROJECT_ID_ANNOTATION] ?? '';
|
||||
};
|
||||
@@ -1,39 +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 React from 'react';
|
||||
import { EntityAirbrakeContent } from './extensions';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { airbrakeApiRef, MockAirbrakeApi } from './api';
|
||||
import { createEntity } from './api';
|
||||
import { EntityProvider } from '@backstage/plugin-catalog-react';
|
||||
|
||||
describe('The Airbrake entity', () => {
|
||||
it('should render the content properly', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider apis={[[airbrakeApiRef, new MockAirbrakeApi()]]}>
|
||||
<EntityProvider entity={createEntity(123)}>
|
||||
<Routes>
|
||||
<Route path="/" element={<EntityAirbrakeContent />} />
|
||||
</Routes>
|
||||
</EntityProvider>
|
||||
</TestApiProvider>,
|
||||
);
|
||||
await expect(
|
||||
rendered.findByText('ChunkLoadError'),
|
||||
).resolves.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 { useEntity } from '@backstage/plugin-catalog-react';
|
||||
import React from 'react';
|
||||
import { airbrakePlugin } from './plugin';
|
||||
import { createRoutableExtension } from '@backstage/core-plugin-api';
|
||||
import { rootRouteRef } from './routes';
|
||||
|
||||
/**
|
||||
* This is the widget that shows up on a component page
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const EntityAirbrakeContent = airbrakePlugin.provide(
|
||||
createRoutableExtension({
|
||||
name: 'EntityAirbrakeContent',
|
||||
mountPoint: rootRouteRef,
|
||||
component: () =>
|
||||
import('./components/EntityAirbrakeWidget').then(
|
||||
({ EntityAirbrakeWidget }) => {
|
||||
return () => {
|
||||
const { entity } = useEntity();
|
||||
return <EntityAirbrakeWidget entity={entity} />;
|
||||
};
|
||||
},
|
||||
),
|
||||
}),
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user