Merge branch 'master' of https://github.com/spotify/backstage into k8s-load-with-only-queryselector

This commit is contained in:
Fidel Coria
2021-02-23 10:17:16 -06:00
191 changed files with 3127 additions and 1442 deletions
+24
View File
@@ -1,5 +1,29 @@
# @backstage/plugin-api-docs
## 0.4.7
### Patch Changes
- d6593abe6: Remove domain column from `HasSystemsCard` and system from `HasComponentsCard`,
`HasSubcomponentsCard`, and `HasApisCard`.
- 437bac549: Make the description column in the catalog table and api-docs table use up as
much space as possible before hiding overflowing text.
- 5469a9761: Changes made in CatalogTable and ApiExplorerTable for using the OverflowTooltip component for truncating large description and showing tooltip on hover-over.
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
## 0.4.6
### Patch Changes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-api-docs",
"version": "0.4.6",
"version": "0.4.7",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -30,9 +30,9 @@
},
"dependencies": {
"@asyncapi/react-component": "^0.18.2",
"@backstage/catalog-model": "^0.7.1",
"@backstage/core": "^0.6.2",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/catalog-model": "^0.7.2",
"@backstage/core": "^0.6.3",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/theme": "^0.2.3",
"@material-icons/font": "^1.0.2",
"@material-ui/core": "^4.11.0",
@@ -49,9 +49,9 @@
"swagger-ui-react": "^3.37.2"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
@@ -99,6 +99,7 @@ const columns: TableColumn<EntityRow>[] = [
placement="bottom-start"
/>
),
width: 'auto',
},
{
title: 'Tags',
@@ -20,6 +20,7 @@ import {
InfoCard,
Link,
Progress,
TableColumn,
WarningPanel,
} from '@backstage/core';
import {
@@ -28,12 +29,20 @@ import {
useRelatedEntities,
} from '@backstage/plugin-catalog-react';
import React from 'react';
import { apiEntityColumns } from './presets';
import { createSpecApiTypeColumn } from './presets';
type Props = {
variant?: 'gridItem';
};
const columns: TableColumn<ApiEntity>[] = [
EntityTable.columns.createEntityRefColumn({ defaultKind: 'API' }),
EntityTable.columns.createOwnerColumn(),
EntityTable.columns.createSpecLifecycleColumn(),
createSpecApiTypeColumn(),
EntityTable.columns.createMetadataDescriptionColumn(),
];
export const HasApisCard = ({ variant = 'gridItem' }: Props) => {
const { entity } = useEntity();
const { entities, loading, error } = useRelatedEntities(entity, {
@@ -73,7 +82,7 @@ export const HasApisCard = ({ variant = 'gridItem' }: Props) => {
</Link>
</div>
}
columns={apiEntityColumns}
columns={columns}
entities={entities as ApiEntity[]}
/>
);
+8
View File
@@ -1,5 +1,13 @@
# @backstage/plugin-app-backend
## 0.3.8
### Patch Changes
- 1c06cb312: Clarify troubleshooting steps for schema serialization issues.
- Updated dependencies [a1f5e6545]
- @backstage/config@0.1.3
## 0.3.7
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-app-backend",
"version": "0.3.7",
"version": "0.3.8",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -31,7 +31,7 @@
"dependencies": {
"@backstage/backend-common": "^0.5.3",
"@backstage/config-loader": "^0.5.1",
"@backstage/config": "^0.1.2",
"@backstage/config": "^0.1.3",
"@types/express": "^4.17.6",
"express": "^4.17.1",
"express-promise-router": "^3.0.3",
@@ -40,7 +40,7 @@
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.6.0",
"@backstage/cli": "^0.6.2",
"@types/supertest": "^2.0.8",
"msw": "^0.20.5",
"supertest": "^4.0.2"
+10
View File
@@ -1,5 +1,15 @@
# @backstage/plugin-auth-backend
## 0.3.2
### Patch Changes
- ec504e7b4: Fix for refresh token being lost during Microsoft login.
- Updated dependencies [bad21a085]
- Updated dependencies [a1f5e6545]
- @backstage/catalog-model@0.7.2
- @backstage/config@0.1.3
## 0.3.1
### Patch Changes
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-auth-backend",
"version": "0.3.1",
"version": "0.3.2",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -31,8 +31,8 @@
"dependencies": {
"@backstage/backend-common": "^0.5.4",
"@backstage/catalog-client": "^0.3.6",
"@backstage/catalog-model": "^0.7.1",
"@backstage/config": "^0.1.2",
"@backstage/catalog-model": "^0.7.2",
"@backstage/config": "^0.1.3",
"@types/express": "^4.17.6",
"@types/passport": "^1.0.3",
"compression": "^1.7.4",
@@ -66,7 +66,7 @@
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/cli": "^0.6.2",
"@types/body-parser": "^1.19.0",
"@types/cookie-parser": "^1.4.2",
"@types/express-session": "^1.17.2",
@@ -54,12 +54,12 @@ export class GithubAuthProvider implements OAuthHandlers {
},
(
accessToken: any,
refreshToken: any,
_refreshToken: any,
params: any,
fullProfile: any,
done: PassportDoneCallback<OAuthResult>,
) => {
done(undefined, { fullProfile, params, accessToken, refreshToken });
done(undefined, { fullProfile, params, accessToken });
},
);
}
@@ -51,12 +51,12 @@ export class GitlabAuthProvider implements OAuthHandlers {
},
(
accessToken: any,
refreshToken: any,
_refreshToken: any,
params: any,
fullProfile: any,
done: PassportDoneCallback<OAuthResult>,
) => {
done(undefined, { fullProfile, params, accessToken, refreshToken });
done(undefined, { fullProfile, params, accessToken });
},
);
}
@@ -72,7 +72,7 @@ export class MicrosoftAuthProvider implements OAuthHandlers {
fullProfile: passport.Profile,
done: PassportDoneCallback<OAuthResult, PrivateInfo>,
) => {
done(undefined, { fullProfile, accessToken, refreshToken, params });
done(undefined, { fullProfile, accessToken, params }, { refreshToken });
},
);
}
+10
View File
@@ -1,5 +1,15 @@
# @backstage/plugin-catalog-backend
## 0.6.3
### Patch Changes
- 2499f6cde: Add support for assuming role in AWS integrations
- Updated dependencies [bad21a085]
- Updated dependencies [a1f5e6545]
- @backstage/catalog-model@0.7.2
- @backstage/config@0.1.3
## 0.6.2
### Patch Changes
+13
View File
@@ -334,6 +334,19 @@ export interface Config {
}>;
};
/**
* AwsOrganizationCloudAccountProcessor configuration
*/
awsOrganization?: {
provider: {
/**
* The role to be assumed by this processor
*
*/
roleArn?: string;
};
};
/**
* MicrosoftGraphOrgReaderProcessor configuration
*/
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-catalog-backend",
"version": "0.6.2",
"version": "0.6.3",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -31,8 +31,8 @@
"dependencies": {
"@azure/msal-node": "^1.0.0-beta.3",
"@backstage/backend-common": "^0.5.4",
"@backstage/catalog-model": "^0.7.1",
"@backstage/config": "^0.1.2",
"@backstage/catalog-model": "^0.7.2",
"@backstage/config": "^0.1.3",
"@backstage/integration": "^0.5.0",
"@octokit/graphql": "^4.5.8",
"@types/express": "^4.17.6",
@@ -59,8 +59,8 @@
"yup": "^0.29.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/test-utils": "^0.1.8",
"@types/core-js": "^2.5.4",
"@types/git-url-parse": "^9.0.0",
"@types/lodash": "^4.14.151",
@@ -15,10 +15,14 @@
*/
import { AwsOrganizationCloudAccountProcessor } from './AwsOrganizationCloudAccountProcessor';
import * as winston from 'winston';
describe('AwsOrganizationCloudAccountProcessor', () => {
describe('readLocation', () => {
const processor = new AwsOrganizationCloudAccountProcessor();
const processor = new AwsOrganizationCloudAccountProcessor({
provider: {},
logger: winston.createLogger(),
});
const location = { type: 'aws-cloud-accounts', target: '' };
const emit = jest.fn();
const listAccounts = jest.fn();
@@ -14,11 +14,17 @@
* limitations under the License.
*/
import { LocationSpec, ResourceEntityV1alpha1 } from '@backstage/catalog-model';
import AWS, { Organizations } from 'aws-sdk';
import AWS, { Credentials, Organizations } from 'aws-sdk';
import { Account, ListAccountsResponse } from 'aws-sdk/clients/organizations';
import * as results from './results';
import { CatalogProcessor, CatalogProcessorEmit } from './types';
import { Config } from '@backstage/config';
import { Logger } from 'winston';
import {
AwsOrganizationProviderConfig,
readAwsOrganizationConfig,
} from './awsOrganization/config';
const AWS_ORGANIZATION_REGION = 'us-east-1';
const LOCATION_TYPE = 'aws-cloud-accounts';
@@ -33,9 +39,39 @@ const ORGANIZATION_ANNOTATION: string = 'amazonaws.com/organization-id';
* If custom authentication is needed, it can be achieved by configuring the global AWS.credentials object.
*/
export class AwsOrganizationCloudAccountProcessor implements CatalogProcessor {
logger: Logger;
organizations: Organizations;
constructor() {
provider: AwsOrganizationProviderConfig;
static fromConfig(config: Config, options: { logger: Logger }) {
const c = config.getOptionalConfig('catalog.processors.awsOrganization');
return new AwsOrganizationCloudAccountProcessor({
...options,
provider: c ? readAwsOrganizationConfig(c) : {},
});
}
constructor(options: {
provider: AwsOrganizationProviderConfig;
logger: Logger;
}) {
this.provider = options.provider;
this.logger = options.logger;
let credentials = undefined;
if (
this.provider.roleArn !== undefined &&
AWS.config.credentials instanceof Credentials
) {
credentials = new AWS.ChainableTemporaryCredentials({
masterCredentials: AWS.config.credentials as Credentials,
params: {
RoleSessionName: 'backstage-aws-organization-processor',
RoleArn: this.provider.roleArn,
},
});
}
this.organizations = new AWS.Organizations({
credentials,
region: AWS_ORGANIZATION_REGION,
}); // Only available in us-east-1
}
@@ -0,0 +1,33 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { readAwsOrganizationConfig } from './config';
describe('readAwsOrganizationConfig', () => {
it('applies all of the defaults', () => {
const config = {
provider: {
roleArn: 'aws::arn::foo',
},
};
const actual = readAwsOrganizationConfig(new ConfigReader(config));
const expected = {
roleArn: 'aws::arn::foo',
};
expect(actual).toEqual(expected);
});
});
@@ -14,20 +14,25 @@
* limitations under the License.
*/
import { useApi } from '@backstage/core';
import { catalogApiRef } from '@backstage/plugin-catalog-react';
import { CircularProgress, useTheme } from '@material-ui/core';
import React from 'react';
import { useAsync } from 'react-use';
import { Config } from '@backstage/config';
export const AllServicesCount = () => {
const theme = useTheme();
const catalogApi = useApi(catalogApiRef);
const { value, loading } = useAsync(() => catalogApi.getEntities());
if (loading) {
return <CircularProgress size={theme.spacing(2)} />;
}
return <span>{value ?? length ?? '-'}</span>;
/**
* The configuration parameters for a single AWS Organization Processor
*/
export type AwsOrganizationProviderConfig = {
/**
* The role to assume for the processor.
*/
roleArn?: string;
};
export function readAwsOrganizationConfig(
config: Config,
): AwsOrganizationProviderConfig {
const providerConfig = config.getOptionalConfig('provider');
const roleArn = providerConfig?.getOptionalString('roleArn');
return {
roleArn,
};
}
@@ -60,6 +60,16 @@ export async function createRouter(
res.status(200).json(entities.map(fieldMapper));
})
.post('/entities', async (req, res) => {
/*
* NOTE: THIS METHOD IS DEPRECATED AND NOT RECOMMENDED TO USE
*
* Posting entities to this method has unclear semantics and will not
* properly subject them to limitations, processing, or resolution of
* relations.
*
* It stays around in the service for the time being, but may be
* removed or change semantics at any time without prior notice.
*/
const body = await requireRequestBody(req);
const [result] = await entitiesCatalog.batchAddOrUpdateEntities([
{ entity: body as Entity, relations: [] },
+22
View File
@@ -1,5 +1,27 @@
# @backstage/plugin-catalog-import
## 0.4.2
### Patch Changes
- a8953a9c9: This updates the `catalog-import` plugin to omit the default metadata namespace
field and also use the short form entity reference format for selected group owners.
- 968b588f7: Allows the CodeOwnersProcessor to set the owner automatically within the catalog-import plugin. This adds an additional checkbox that overrides the group selector and will omit the owner option in the generated catalog file yaml.
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
## 0.4.1
### Patch Changes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-catalog-import",
"version": "0.4.1",
"version": "0.4.2",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -30,11 +30,11 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.7.1",
"@backstage/catalog-model": "^0.7.2",
"@backstage/catalog-client": "^0.3.6",
"@backstage/core": "^0.6.2",
"@backstage/core": "^0.6.3",
"@backstage/integration": "^0.5.0",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -51,9 +51,9 @@
"yaml": "^1.10.0"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/react-hooks": "^3.3.0",
@@ -15,7 +15,15 @@
*/
import { ConfigApi } from '@backstage/core';
import { Box, StepLabel, TextField, Typography } from '@material-ui/core';
import {
Box,
Checkbox,
FormControlLabel,
FormHelperText,
StepLabel,
TextField,
Typography,
} from '@material-ui/core';
import React from 'react';
import { BackButton } from '../Buttons';
import { StepFinishImportLocation } from '../StepFinishImportLocation';
@@ -167,6 +175,7 @@ export function defaultGenerateStepper(
defaultTitle={title}
defaultBody={body}
renderFormFields={({
values,
control,
errors,
groupsLoading,
@@ -219,22 +228,47 @@ export function defaultGenerateStepper(
required
/>
<AutocompleteTextField
name="owner"
control={control}
errors={errors}
options={groups || []}
loading={groupsLoading}
loadingText="Loading groups…"
helperText="Select an owner from the list or enter a reference to a Group or a User"
errorHelperText="required value"
textFieldProps={{
label: 'Entity Owner',
placeholder: 'Group:default/my-group',
}}
rules={{ required: true }}
required
{!values.useCodeowners && (
<AutocompleteTextField
name="owner"
control={control}
errors={errors}
options={groups || []}
loading={groupsLoading}
loadingText="Loading groups…"
helperText="Select an owner from the list or enter a reference to a Group or a User"
errorHelperText="required value"
textFieldProps={{
label: 'Entity Owner',
placeholder: 'my-group',
}}
rules={{ required: true }}
required
/>
)}
<FormControlLabel
control={
<Checkbox
name="useCodeowners"
inputRef={register}
onChange={(_, value) => {
if (value) {
control.setValue('owner', '');
}
}}
/>
}
label={
<>
Use <em>CODEOWNERS</em> file as Entity Owner
</>
}
/>
<FormHelperText>
WARNING: This may fail is no CODEOWNERS file is found at
the target location.
</FormHelperText>
</>
)}
/>
@@ -21,7 +21,10 @@ import { act, render, RenderResult } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { AnalyzeResult, catalogImportApiRef } from '../../api';
import { StepPrepareCreatePullRequest } from './StepPrepareCreatePullRequest';
import {
generateEntities,
StepPrepareCreatePullRequest,
} from './StepPrepareCreatePullRequest';
describe('<StepPrepareCreatePullRequest />', () => {
const catalogImportApi: jest.Mocked<typeof catalogImportApiRef.T> = {
@@ -60,6 +63,7 @@ describe('<StepPrepareCreatePullRequest />', () => {
kind: 'Component',
metadata: {
name: 'my-component',
namespace: 'default',
},
spec: {
owner: 'my-owner',
@@ -282,6 +286,46 @@ spec:
renderFormFieldsFn.mock.calls[
renderFormFieldsFn.mock.calls.length - 1
][0],
).toMatchObject({ groups: ['Group:my-group'], groupsLoading: false });
).toMatchObject({ groups: ['my-group'], groupsLoading: false });
});
describe('generateEntities', () => {
it.each([[undefined], [null]])(
'should not include blank namespace for %s',
namespace => {
expect(
generateEntities(
[{ metadata: { namespace: namespace as any } }],
'my-component',
'group-1',
),
).toEqual([
expect.objectContaining({
metadata: expect.not.objectContaining({
namespace: 'default',
}),
}),
]);
},
);
it.each([['default'], ['my-namespace']])(
'should include explicit namespace %s',
namespace => {
expect(
generateEntities(
[{ metadata: { namespace } }],
'my-component',
'group-1',
),
).toEqual([
expect.objectContaining({
metadata: expect.objectContaining({
namespace,
}),
}),
]);
},
);
});
});
@@ -14,13 +14,16 @@
* limitations under the License.
*/
import { Entity, serializeEntityRef } from '@backstage/catalog-model';
import { Entity } from '@backstage/catalog-model';
import { useApi } from '@backstage/core';
import { catalogApiRef } from '@backstage/plugin-catalog-react';
import {
catalogApiRef,
formatEntityRefTitle,
} from '@backstage/plugin-catalog-react';
import { Box, FormHelperText, Grid, Typography } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import React, { useCallback, useState } from 'react';
import { UseFormMethods } from 'react-hook-form';
import { UnpackNestedValue, UseFormMethods } from 'react-hook-form';
import { useAsync } from 'react-use';
import YAML from 'yaml';
import { AnalyzeResult, catalogImportApiRef } from '../../api';
@@ -45,6 +48,7 @@ type FormData = {
body: string;
componentName: string;
owner: string;
useCodeowners: boolean;
};
type Props = {
@@ -60,16 +64,17 @@ type Props = {
renderFormFields: (
props: Pick<UseFormMethods<FormData>, 'errors' | 'register' | 'control'> & {
values: UnpackNestedValue<FormData>;
groups: string[];
groupsLoading: boolean;
},
) => React.ReactNode;
};
function generateEntities(
export function generateEntities(
entities: PartialEntity[],
componentName: string,
owner: string,
owner?: string,
): Entity[] {
return entities.map(e => ({
...e,
@@ -78,11 +83,10 @@ function generateEntities(
metadata: {
...e.metadata,
name: componentName,
namespace: e.metadata?.namespace ?? 'default',
},
spec: {
...e.spec,
owner,
...(owner ? { owner } : {}),
},
}));
}
@@ -107,8 +111,9 @@ export const StepPrepareCreatePullRequest = ({
filter: { kind: 'group' },
});
// TODO: defaultKind (=group), defaultNamespace (=same as entity)
return groupEntities.items.map(e => serializeEntityRef(e) as string).sort();
return groupEntities.items
.map(e => formatEntityRefTitle(e, { defaultKind: 'group' }))
.sort();
});
const handleResult = useCallback(
@@ -186,10 +191,12 @@ export const StepPrepareCreatePullRequest = ({
(analyzeResult.generatedEntities[0]?.spec?.owner as string) || '',
componentName:
analyzeResult.generatedEntities[0]?.metadata?.name || '',
useCodeowners: false,
}}
render={({ values, errors, control, register }) => (
<>
{renderFormFields({
values,
errors,
register,
control,
+24
View File
@@ -1,5 +1,29 @@
# @backstage/plugin-catalog-react
## 0.1.0
### Minor Changes
- d0760ecdf: Moved common useStarredEntities hook to plugin-catalog-react
### Patch Changes
- 88f1f1b60: Truncate and show ellipsis with tooltip if content of
`createMetadataDescriptionColumn` is too wide.
- 9615e68fb: Forward link styling of `EntityRefLink` and `EnriryRefLinks` into the underling
`Link`.
- 5c2e2863f: Added the proper type parameters to entityRouteRef.
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [1407b34c6]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [3a58084b6]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/catalog-model@0.7.2
## 0.0.4
### Patch Changes
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-catalog-react",
"version": "0.0.4",
"version": "0.1.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -29,8 +29,8 @@
},
"dependencies": {
"@backstage/catalog-client": "^0.3.6",
"@backstage/catalog-model": "^0.7.1",
"@backstage/core": "^0.6.2",
"@backstage/catalog-model": "^0.7.2",
"@backstage/core": "^0.6.3",
"@material-ui/core": "^4.11.0",
"@types/react": "^16.9",
"react": "^16.13.1",
@@ -39,9 +39,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/react-hooks": "^3.3.0",
@@ -26,7 +26,7 @@ export const EntityProvider = ({ entity, children }: EntityProviderProps) => (
<EntityContext.Provider
value={{
entity,
loading: Boolean(entity),
loading: !Boolean(entity),
error: undefined,
}}
>
@@ -20,7 +20,7 @@ import {
RELATION_OWNED_BY,
RELATION_PART_OF,
} from '@backstage/catalog-model';
import { TableColumn } from '@backstage/core';
import { OverflowTooltip, TableColumn } from '@backstage/core';
import React from 'react';
import { getEntityRelations } from '../../utils';
import {
@@ -139,6 +139,12 @@ export function createMetadataDescriptionColumn<
return {
title: 'Description',
field: 'metadata.description',
render: entity => (
<OverflowTooltip
text={entity.metadata.description}
placement="bottom-start"
/>
),
width: 'auto',
};
}
@@ -74,7 +74,7 @@ describe('systemEntityColumns', () => {
expect(getByText('my-namespace/my-system')).toBeInTheDocument();
expect(getByText('my-namespace/my-domain')).toBeInTheDocument();
expect(getByText('Test')).toBeInTheDocument();
expect(getByText('Some description')).toBeInTheDocument();
expect(getByText(/Some/)).toBeInTheDocument();
});
});
});
@@ -131,7 +131,7 @@ describe('componentEntityColumns', () => {
expect(getByText('Test')).toBeInTheDocument();
expect(getByText('production')).toBeInTheDocument();
expect(getByText('service')).toBeInTheDocument();
expect(getByText('Some description')).toBeInTheDocument();
expect(getByText(/Some/)).toBeInTheDocument();
});
});
});
+1
View File
@@ -16,3 +16,4 @@
export { EntityContext, useEntity, useEntityFromUrl } from './useEntity';
export { useEntityCompoundName } from './useEntityCompoundName';
export { useRelatedEntities } from './useRelatedEntities';
export { useStarredEntities } from './useStarredEntities';
+1
View File
@@ -31,6 +31,7 @@ export const entityRoute = createRouteRef({
icon: NoIcon,
path: ':namespace/:kind/:name/*',
title: 'Entity',
params: ['namespace', 'kind', 'name'],
});
export const entityRouteRef = entityRoute;
+65
View File
@@ -1,5 +1,70 @@
# @backstage/plugin-catalog
## 0.4.0
### Minor Changes
- a5f42cf66: The Scaffolder and Catalog plugins have been migrated to partially require use of the [new composability API](https://backstage.io/docs/plugins/composability). The Scaffolder used to register its pages using the deprecated route registration plugin API, but those registrations have been removed. This means you now need to add the Scaffolder plugin page to the app directly.
The page is imported from the Scaffolder plugin and added to the `<FlatRoutes>` component:
```tsx
<Route path="/create" element={<ScaffolderPage />} />
```
The Catalog plugin has also been migrated to use an [external route reference](https://backstage.io/docs/plugins/composability#binding-external-routes-in-the-app) to dynamically link to the create component page. This means you need to migrate the catalog plugin to use the new extension components, as well as bind the external route.
To use the new extension components, replace existing usage of the `CatalogRouter` with the following:
```tsx
<Route path="/catalog" element={<CatalogIndexPage />} />
<Route path="/catalog/:namespace/:kind/:name" element={<CatalogEntityPage />}>
<EntityPage />
</Route>
```
And to bind the external route from the catalog plugin to the scaffolder template index page, make sure you have the appropriate imports and add the following to the `createApp` call:
```ts
import { catalogPlugin } from '@backstage/plugin-catalog';
import { scaffolderPlugin } from '@backstage/plugin-scaffolder';
const app = createApp({
// ...
bindRoutes({ bind }) {
bind(catalogPlugin.externalRoutes, {
createComponent: scaffolderPlugin.routes.root,
});
},
});
```
- d0760ecdf: Moved common useStarredEntities hook to plugin-catalog-react
### Patch Changes
- d6593abe6: Remove domain column from `HasSystemsCard` and system from `HasComponentsCard`,
`HasSubcomponentsCard`, and `HasApisCard`.
- bad21a085: Implement annotations for customising Entity URLs in the Catalog pages.
- 437bac549: Make the description column in the catalog table and api-docs table use up as
much space as possible before hiding overflowing text.
- 5469a9761: Changes made in CatalogTable and ApiExplorerTable for using the OverflowTooltip component for truncating large description and showing tooltip on hover-over.
- 60d1bc3e7: Fix Japanese Good Morning
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
## 0.3.2
### Patch Changes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-catalog",
"version": "0.3.2",
"version": "0.4.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -31,9 +31,9 @@
},
"dependencies": {
"@backstage/catalog-client": "^0.3.6",
"@backstage/catalog-model": "^0.7.1",
"@backstage/core": "^0.6.2",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/catalog-model": "^0.7.2",
"@backstage/core": "^0.6.3",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -50,9 +50,9 @@
"swr": "^0.3.0"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@microsoft/microsoft-graph-types": "^1.25.0",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
@@ -15,6 +15,10 @@
*/
import { EntityProvider } from '@backstage/plugin-catalog-react';
import {
SOURCE_LOCATION_ANNOTATION,
EDIT_URL_ANNOTATION,
} from '@backstage/catalog-model';
import { render } from '@testing-library/react';
import React from 'react';
import { AboutCard } from './AboutCard';
@@ -123,3 +127,41 @@ describe('<AboutCard /> BitBucket', () => {
);
});
});
describe('<AboutCard /> custom links', () => {
it('renders info and "view source" link', () => {
const entity = {
apiVersion: 'v1',
kind: 'Component',
metadata: {
name: 'software',
annotations: {
'backstage.io/managed-by-location':
'bitbucket:https://bitbucket.org/backstage/backstage/src/master/software.yaml',
[EDIT_URL_ANNOTATION]: 'https://another.place',
[SOURCE_LOCATION_ANNOTATION]:
'url:https://another.place/backstage.git',
},
},
spec: {
owner: 'guest',
type: 'service',
lifecycle: 'production',
},
};
const { getByText } = render(
<EntityProvider entity={entity}>
<AboutCard />
</EntityProvider>,
);
expect(getByText('service')).toBeInTheDocument();
expect(getByText('View Source').closest('a')).toHaveAttribute(
'href',
'https://another.place/backstage.git',
);
expect(getByText('View Source').closest('a')).toHaveAttribute(
'edithref',
'https://another.place',
);
});
});
@@ -16,7 +16,9 @@
import {
Entity,
LocationSpec,
ENTITY_DEFAULT_NAMESPACE,
SOURCE_LOCATION_ANNOTATION,
RELATION_PROVIDES_API,
} from '@backstage/catalog-model';
import { HeaderIconLinkRow } from '@backstage/core';
@@ -34,8 +36,8 @@ import EditIcon from '@material-ui/icons/Edit';
import ExtensionIcon from '@material-ui/icons/Extension';
import GitHubIcon from '@material-ui/icons/GitHub';
import React from 'react';
import { findLocationForEntityMeta } from '../../data/utils';
import { createEditLink, determineUrlType } from '../createEditLink';
import { findLocationForEntityMeta, parseLocation } from '../../data/utils';
import { findEditUrl, determineUrlType } from '../actions';
import { AboutContent } from './AboutContent';
const useStyles = makeStyles({
@@ -60,20 +62,35 @@ type CodeLinkInfo = {
href?: string;
};
function getSourceLocationForEntity(
entity: Entity,
location?: LocationSpec,
): LocationSpec | undefined {
const annotation = entity.metadata?.annotations?.[SOURCE_LOCATION_ANNOTATION];
const parsed = annotation && parseLocation(annotation);
return parsed || location;
}
function getCodeLinkInfo(entity: Entity): CodeLinkInfo {
const location = findLocationForEntityMeta(entity?.metadata);
const editUrl = findEditUrl(entity);
let sourceLocation = getSourceLocationForEntity(entity, location);
if (location) {
sourceLocation = sourceLocation || location;
const type =
location.type === 'url'
? determineUrlType(location.target)
: location.type;
sourceLocation.type === 'url'
? determineUrlType(sourceLocation.target)
: sourceLocation.type;
return {
edithref: editUrl,
icon: iconMap[type],
edithref: createEditLink(location),
href: location.target,
href: sourceLocation.target,
};
}
return {};
return { edithref: editUrl, href: sourceLocation?.target };
}
type AboutCardProps = {
@@ -23,14 +23,18 @@ import {
useApi,
useRouteRef,
} from '@backstage/core';
import { catalogApiRef, isOwnerOf } from '@backstage/plugin-catalog-react';
import {
catalogApiRef,
isOwnerOf,
useStarredEntities,
} from '@backstage/plugin-catalog-react';
import { Button, makeStyles } from '@material-ui/core';
import SettingsIcon from '@material-ui/icons/Settings';
import StarIcon from '@material-ui/icons/Star';
import React, { useCallback, useMemo, useState } from 'react';
import { Link as RouterLink } from 'react-router-dom';
import { EntityFilterGroupsProvider, useFilteredEntities } from '../../filter';
import { useStarredEntities } from '../../hooks/useStarredEntities';
import { createComponentRouteRef } from '../../routes';
import {
ButtonGroup,
@@ -27,7 +27,7 @@
"Indonesian": "Selamat pagi",
"Irish": "Dia dhuit",
"Italian": "Buongiorno",
"Japanese": "おう",
"Japanese": "おはよう",
"Korean": "안녕하세요",
"Kazakh": "Kayırlı tan",
"Kurdish": "Beyanî baş",
@@ -14,7 +14,12 @@
* limitations under the License.
*/
import { Entity } from '@backstage/catalog-model';
import {
Entity,
VIEW_URL_ANNOTATION,
EDIT_URL_ANNOTATION,
} from '@backstage/catalog-model';
import { act, fireEvent } from '@testing-library/react';
import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils';
import * as React from 'react';
import { CatalogTable } from './CatalogTable';
@@ -38,6 +43,14 @@ const entities: Entity[] = [
];
describe('CatalogTable component', () => {
beforeEach(() => {
window.open = jest.fn();
});
afterEach(() => {
jest.resetAllMocks();
});
it('should render error message when error is passed in props', async () => {
const rendered = await renderWithEffects(
wrapInTestApp(
@@ -70,4 +83,62 @@ describe('CatalogTable component', () => {
expect(rendered.getByText(/component2/)).toBeInTheDocument();
expect(rendered.getByText(/component3/)).toBeInTheDocument();
});
it('should use specified edit URL if in annotation', async () => {
const entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
name: 'component1',
annotations: { [EDIT_URL_ANNOTATION]: 'https://other.place' },
},
};
const { getByTitle } = await renderWithEffects(
wrapInTestApp(
<CatalogTable
titlePreamble="Owned"
entities={[entity]}
loading={false}
/>,
),
);
const editButton = getByTitle('Edit');
await act(async () => {
fireEvent.click(editButton);
});
expect(window.open).toHaveBeenCalledWith('https://other.place', '_blank');
});
it('should use specified view URL if in annotation', async () => {
const entity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
name: 'component1',
annotations: { [VIEW_URL_ANNOTATION]: 'https://other.place' },
},
};
const { getByTitle } = await renderWithEffects(
wrapInTestApp(
<CatalogTable
titlePreamble="Owned"
entities={[entity]}
loading={false}
/>,
),
);
const viewButton = getByTitle('View');
await act(async () => {
fireEvent.click(viewButton);
});
expect(window.open).toHaveBeenCalledWith('https://other.place', '_blank');
});
});
@@ -32,14 +32,13 @@ import {
EntityRefLinks,
formatEntityRefTitle,
getEntityRelations,
useStarredEntities,
} from '@backstage/plugin-catalog-react';
import { Chip } from '@material-ui/core';
import Edit from '@material-ui/icons/Edit';
import OpenInNew from '@material-ui/icons/OpenInNew';
import React from 'react';
import { findLocationForEntityMeta } from '../../data/utils';
import { useStarredEntities } from '../../hooks/useStarredEntities';
import { createEditLink } from '../createEditLink';
import { findViewUrl, findEditUrl } from '../actions';
import {
favouriteEntityIcon,
favouriteEntityTooltip,
@@ -98,6 +97,7 @@ const columns: TableColumn<EntityRow>[] = [
placement="bottom-start"
/>
),
width: 'auto',
},
{
title: 'Tags',
@@ -152,24 +152,24 @@ export const CatalogTable = ({
const actions: TableProps<EntityRow>['actions'] = [
({ entity }) => {
const location = findLocationForEntityMeta(entity.metadata);
const url = findViewUrl(entity);
return {
icon: () => <OpenInNew fontSize="small" />,
tooltip: 'View',
onClick: () => {
if (!location) return;
window.open(location.target, '_blank');
if (!url) return;
window.open(url, '_blank');
},
};
},
({ entity }) => {
const location = findLocationForEntityMeta(entity.metadata);
const url = findEditUrl(entity);
return {
icon: () => <Edit fontSize="small" />,
tooltip: 'Edit',
onClick: () => {
if (!location) return;
window.open(createEditLink(location), '_blank');
if (!url) return;
window.open(url, '_blank');
},
};
},
@@ -15,10 +15,10 @@
*/
import React, { ComponentProps } from 'react';
import { useStarredEntities } from '@backstage/plugin-catalog-react';
import { IconButton, Tooltip, withStyles } from '@material-ui/core';
import StarBorder from '@material-ui/icons/StarBorder';
import Star from '@material-ui/icons/Star';
import { useStarredEntities } from '../../hooks/useStarredEntities';
import { Entity } from '@backstage/catalog-model';
type Props = ComponentProps<typeof IconButton> & { entity: Entity };
@@ -33,6 +33,14 @@ type Props = {
variant?: 'gridItem';
};
const columns = [
EntityTable.columns.createEntityRefColumn({ defaultKind: 'component' }),
EntityTable.columns.createOwnerColumn(),
EntityTable.columns.createSpecTypeColumn(),
EntityTable.columns.createSpecLifecycleColumn(),
EntityTable.columns.createMetadataDescriptionColumn(),
];
export const HasComponentsCard = ({ variant = 'gridItem' }: Props) => {
const { entity } = useEntity();
const { entities, loading, error } = useRelatedEntities(entity, {
@@ -72,7 +80,7 @@ export const HasComponentsCard = ({ variant = 'gridItem' }: Props) => {
</Link>
</div>
}
columns={EntityTable.componentEntityColumns}
columns={columns}
entities={entities as ComponentEntity[]}
/>
);
@@ -33,6 +33,14 @@ type Props = {
variant?: 'gridItem';
};
const columns = [
EntityTable.columns.createEntityRefColumn({ defaultKind: 'component' }),
EntityTable.columns.createOwnerColumn(),
EntityTable.columns.createSpecTypeColumn(),
EntityTable.columns.createSpecLifecycleColumn(),
EntityTable.columns.createMetadataDescriptionColumn(),
];
export const HasSubcomponentsCard = ({ variant = 'gridItem' }: Props) => {
const { entity } = useEntity();
const { entities, loading, error } = useRelatedEntities(entity, {
@@ -72,7 +80,7 @@ export const HasSubcomponentsCard = ({ variant = 'gridItem' }: Props) => {
</Link>
</div>
}
columns={EntityTable.componentEntityColumns}
columns={columns}
entities={entities as ComponentEntity[]}
/>
);
@@ -33,6 +33,12 @@ type Props = {
variant?: 'gridItem';
};
const columns = [
EntityTable.columns.createEntityRefColumn({ defaultKind: 'system' }),
EntityTable.columns.createOwnerColumn(),
EntityTable.columns.createMetadataDescriptionColumn(),
];
export const HasSystemsCard = ({ variant = 'gridItem' }: Props) => {
const { entity } = useEntity();
const { entities, loading, error } = useRelatedEntities(entity, {
@@ -71,7 +77,7 @@ export const HasSystemsCard = ({ variant = 'gridItem' }: Props) => {
</Link>
</div>
}
columns={EntityTable.systemEntityColumns}
columns={columns}
entities={entities as SystemEntity[]}
/>
);
@@ -14,7 +14,13 @@
* limitations under the License.
*/
import { LocationSpec } from '@backstage/catalog-model';
import {
LocationSpec,
Entity,
EDIT_URL_ANNOTATION,
VIEW_URL_ANNOTATION,
} from '@backstage/catalog-model';
import { findLocationForEntityMeta } from '../data/utils';
import parseGitUrl from 'git-url-parse';
/**
@@ -75,3 +81,22 @@ export const determineUrlType = (url: string): string => {
}
return 'url';
};
export const findEditUrl = ({ metadata }: Entity): string | undefined => {
const annotations = metadata.annotations || {};
const editUrl = annotations[EDIT_URL_ANNOTATION];
if (editUrl) return editUrl;
const location = findLocationForEntityMeta(metadata);
return location && createEditLink(location);
};
export const findViewUrl = ({ metadata }: Entity): string | undefined => {
const annotations = metadata.annotations || {};
const location = findLocationForEntityMeta(metadata);
return annotations[VIEW_URL_ANNOTATION] || location?.target;
};
+7 -3
View File
@@ -32,13 +32,17 @@ export function findLocationForEntityMeta(
return undefined;
}
const separatorIndex = annotation.indexOf(':');
return parseLocation(annotation);
}
export function parseLocation(reference: string): LocationSpec | undefined {
const separatorIndex = reference.indexOf(':');
if (separatorIndex === -1) {
return undefined;
}
return {
type: annotation.substring(0, separatorIndex),
target: annotation.substring(separatorIndex + 1),
type: reference.substring(0, separatorIndex),
target: reference.substring(separatorIndex + 1),
};
}
@@ -1,121 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* 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, { PropsWithChildren } from 'react';
import { renderHook, act } from '@testing-library/react-hooks';
import { useStarredEntities } from './useStarredEntities';
import {
ApiProvider,
ApiRegistry,
storageApiRef,
WebStorage,
StorageApi,
} from '@backstage/core';
import { MockErrorApi } from '@backstage/test-utils';
import { Entity } from '@backstage/catalog-model';
describe('useStarredEntities', () => {
let mockStorage: StorageApi | undefined;
const mockEntity: Entity = {
apiVersion: '1',
kind: 'Component',
metadata: {
name: 'mock',
},
};
const secondMockEntity: Entity = {
apiVersion: '1',
kind: 'Component',
metadata: {
namespace: 'test',
name: 'mock2',
},
};
const wrapper = ({ children }: PropsWithChildren<{}>) => {
return (
<ApiProvider apis={ApiRegistry.with(storageApiRef, mockStorage)}>
{children}
</ApiProvider>
);
};
beforeEach(() => {
mockStorage = new WebStorage('@backstage', new MockErrorApi()).forBucket(
Date.now().toString(), // TODO(blam): need something that changes every test run for now until the MockStorage is implemented
);
});
it('should return an empty set for when there is no items in storage', async () => {
const { result } = renderHook(() => useStarredEntities(), { wrapper });
expect(result.current.starredEntities.size).toBe(0);
});
it('should return a set with the current items when there is items in storage', async () => {
const expectedIds = ['i', 'am', 'some', 'test', 'ids'];
const store = mockStorage?.forBucket('settings');
await store?.set('starredEntities', expectedIds);
const { result } = renderHook(() => useStarredEntities(), { wrapper });
for (const item of expectedIds) {
expect(result.current.starredEntities.has(item)).toBeTruthy();
}
});
it('should listen to changes when the storage is set elsewhere', async () => {
const { result, waitForNextUpdate } = renderHook(
() => useStarredEntities(),
{ wrapper },
);
expect(result.current.starredEntities.size).toBe(0);
expect(result.current.isStarredEntity(mockEntity)).toBeFalsy();
// Make this happen after awaiting for the next update so we can
// catch when the hook re-renders with the latest data
setTimeout(() => result.current.toggleStarredEntity(mockEntity), 1);
await waitForNextUpdate();
expect(result.current.starredEntities.size).toBe(1);
expect(result.current.isStarredEntity(mockEntity)).toBeTruthy();
});
it('should write new entries to the local store when adding a togglging entity', async () => {
const { result } = renderHook(() => useStarredEntities(), { wrapper });
act(() => {
result.current.toggleStarredEntity(mockEntity);
});
expect(result.current.isStarredEntity(mockEntity)).toBeTruthy();
expect(result.current.isStarredEntity(secondMockEntity)).toBeFalsy();
});
it('should remove an existing entity when toggling entries', async () => {
const { result } = renderHook(() => useStarredEntities(), { wrapper });
act(() => {
result.current.toggleStarredEntity(mockEntity);
result.current.toggleStarredEntity(secondMockEntity);
result.current.toggleStarredEntity(mockEntity);
});
expect(result.current.isStarredEntity(mockEntity)).toBeFalsy();
expect(result.current.isStarredEntity(secondMockEntity)).toBeTruthy();
});
});
+19
View File
@@ -1,5 +1,24 @@
# @backstage/plugin-circleci
## 0.2.10
### Patch Changes
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
## 0.2.9
### Patch Changes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-circleci",
"version": "0.2.9",
"version": "0.2.10",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -31,9 +31,9 @@
"postpack": "backstage-cli postpack"
},
"dependencies": {
"@backstage/catalog-model": "^0.7.1",
"@backstage/core": "^0.6.2",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/catalog-model": "^0.7.2",
"@backstage/core": "^0.6.3",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -50,9 +50,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
+19
View File
@@ -1,5 +1,24 @@
# @backstage/plugin-cloudbuild
## 0.2.11
### Patch Changes
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
## 0.2.10
### Patch Changes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-cloudbuild",
"version": "0.2.10",
"version": "0.2.11",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -30,9 +30,9 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.7.1",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/core": "^0.6.2",
"@backstage/catalog-model": "^0.7.2",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/core": "^0.6.3",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -47,9 +47,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
+16
View File
@@ -1,5 +1,21 @@
# @backstage/plugin-cost-insights
## 0.8.2
### Patch Changes
- 38205492a: Default alert properties can be overridden using accessors
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [1407b34c6]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [3a58084b6]
- Updated dependencies [a1f5e6545]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/config@0.1.3
## 0.8.1
### Patch Changes
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-cost-insights",
"version": "0.8.1",
"version": "0.8.2",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -30,8 +30,8 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/config": "^0.1.2",
"@backstage/core": "^0.6.2",
"@backstage/config": "^0.1.3",
"@backstage/core": "^0.6.3",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -55,9 +55,9 @@
"yup": "^0.29.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
@@ -0,0 +1,101 @@
/*
* Copyright 2020 Spotify AB
*
* 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 pluralize from 'pluralize';
import { renderInTestApp } from '@backstage/test-utils';
import { ProjectGrowthAlert } from './ProjectGrowthAlert';
import { ProjectGrowthData } from '../types';
import {
MockCurrencyProvider,
MockConfigProvider,
MockBillingDateProvider,
} from '../utils/tests';
const mockData: ProjectGrowthData = {
project: 'test-project',
periodStart: '2021-01-01',
periodEnd: '2021-02-01',
aggregation: [0, 0],
change: {
ratio: 0,
amount: 0,
},
products: [
{
id: 'product-a',
aggregation: [0, 0],
},
],
};
// suppress recharts componentDidUpdate deprecation warnings
jest.spyOn(console, 'warn').mockImplementation(() => {});
async function renderInContext(children: JSX.Element) {
return renderInTestApp(
<MockConfigProvider>
<MockBillingDateProvider>
<MockCurrencyProvider>{children}</MockCurrencyProvider>
</MockBillingDateProvider>
</MockConfigProvider>,
);
}
class CustomProjectGrowthAlert extends ProjectGrowthAlert {
get url() {
return 'path/to/resource';
}
get title() {
return `Investigate cost growth in ${pluralize(
'project',
this.data.products.length,
true,
)}`;
}
}
describe('ProjectGrowthAlert', () => {
describe('constructor', () => {
it('should create a project growth alert', async () => {
const alert = new ProjectGrowthAlert(mockData);
const { getByText, queryByText } = await renderInContext(alert.element);
expect(alert.url).toBe('/cost-insights/investigating-growth');
expect(alert.title).toBe(
'Investigate cost growth in project test-project',
);
expect(alert.subtitle).toBe(
'Cost growth outpacing business growth is unsustainable long-term.',
);
expect(getByText('1 product')).toBeInTheDocument();
expect(queryByText('sorted by cost')).not.toBeInTheDocument();
});
it('a subclass can inherit and override defaults using accessors', async () => {
const alert = new CustomProjectGrowthAlert(mockData);
const { getByText, queryByText } = await renderInContext(alert.element);
expect(alert.url).toBe('path/to/resource');
expect(alert.title).toBe('Investigate cost growth in 1 project');
expect(alert.subtitle).toBe(
'Cost growth outpacing business growth is unsustainable long-term.',
);
expect(getByText('1 product')).toBeInTheDocument();
expect(queryByText('sorted by cost')).not.toBeInTheDocument();
});
});
});
@@ -27,18 +27,22 @@ import { Alert, ProjectGrowthData } from '../types';
export class ProjectGrowthAlert implements Alert {
data: ProjectGrowthData;
url = '/cost-insights/investigating-growth';
subtitle =
'Cost growth outpacing business growth is unsustainable long-term.';
constructor(data: ProjectGrowthData) {
this.data = data;
}
get url() {
return '/cost-insights/investigating-growth';
}
get title() {
return `Investigate cost growth in project ${this.data.project}`;
}
get subtitle() {
return 'Cost growth outpacing business growth is unsustainable long-term.';
}
get element() {
return <ProjectGrowthAlertCard alert={this.data} />;
}
@@ -0,0 +1,102 @@
/*
* Copyright 2020 Spotify AB
*
* 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 pluralize from 'pluralize';
import { renderInTestApp } from '@backstage/test-utils';
import { UnlabeledDataflowAlert } from './UnlabeledDataflowAlert';
import { UnlabeledDataflowData } from '../types';
import {
MockCurrencyProvider,
MockConfigProvider,
MockBillingDateProvider,
} from '../utils/tests';
const mockData: UnlabeledDataflowData = {
periodStart: '2021-02-01',
periodEnd: '2021-03-31',
unlabeledCost: 0,
labeledCost: 0,
projects: [
{
id: 'project-a',
labeledCost: 0,
unlabeledCost: 0,
},
],
};
// suppress recharts componentDidUpdate deprecation warnings
jest.spyOn(console, 'warn').mockImplementation(() => {});
async function renderInContext(children: JSX.Element) {
return renderInTestApp(
<MockConfigProvider>
<MockBillingDateProvider>
<MockCurrencyProvider>{children}</MockCurrencyProvider>
</MockBillingDateProvider>
</MockConfigProvider>,
);
}
class CustomUnlabeledDataflowAlert extends UnlabeledDataflowAlert {
get url() {
return 'path/to/resource';
}
get title() {
return `Add labels to ${pluralize(
'workflow',
this.data.projects.length,
true,
)}`;
}
}
describe('UnlabeledDataflowAlert', () => {
describe('constructor', () => {
it('should create an unlabeled dataflow alert', async () => {
const alert = new UnlabeledDataflowAlert(mockData);
const { getByText } = await renderInContext(alert.element);
expect(alert.url).toBe('/cost-insights/labeling-jobs');
expect(alert.title).toBe('Add labels to workflows');
expect(alert.subtitle).toBe(
'Labels show in billing data, enabling cost insights for each workflow.',
);
expect(
getByText(
'Showing costs from 1 project with unlabeled Dataflow jobs in the last 30 days.',
),
).toBeInTheDocument();
});
it('a subclass can inherit and override defaults using accessors', async () => {
const alert = new CustomUnlabeledDataflowAlert(mockData);
const { getByText } = await renderInContext(alert.element);
expect(alert.url).toBe('path/to/resource');
expect(alert.title).toBe('Add labels to 1 workflow');
expect(alert.subtitle).toBe(
'Labels show in billing data, enabling cost insights for each workflow.',
);
expect(
getByText(
'Showing costs from 1 project with unlabeled Dataflow jobs in the last 30 days.',
),
).toBeInTheDocument();
});
});
});
@@ -28,15 +28,22 @@ export class UnlabeledDataflowAlert implements Alert {
data: UnlabeledDataflowData;
status?: AlertStatus;
url = '/cost-insights/labeling-jobs';
title = 'Add labels to workflows';
subtitle =
'Labels show in billing data, enabling cost insights for each workflow.';
constructor(data: UnlabeledDataflowData) {
this.data = data;
}
get url() {
return '/cost-insights/labeling-jobs';
}
get title() {
return 'Add labels to workflows';
}
get subtitle() {
return 'Labels show in billing data, enabling cost insights for each workflow.';
}
get element() {
return <UnlabeledDataflowAlertCard alert={this.data} />;
}
+20
View File
@@ -1,5 +1,25 @@
# @backstage/plugin-explore
## 0.2.7
### Patch Changes
- 347137ccf: Display the owner of a domain on the domain card.
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
## 0.2.6
### Patch Changes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-explore",
"version": "0.2.6",
"version": "0.2.7",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -30,9 +30,9 @@
"start": "backstage-cli plugin:serve"
},
"dependencies": {
"@backstage/catalog-model": "^0.7.1",
"@backstage/core": "^0.6.2",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/catalog-model": "^0.7.2",
"@backstage/core": "^0.6.3",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/plugin-explore-react": "^0.0.2",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
@@ -45,9 +45,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
+19
View File
@@ -1,5 +1,24 @@
# @backstage/plugin-fossa
## 0.2.3
### Patch Changes
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
## 0.2.2
### Patch Changes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-fossa",
"version": "0.2.2",
"version": "0.2.3",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -31,9 +31,9 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.7.1",
"@backstage/core": "^0.6.2",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/catalog-model": "^0.7.2",
"@backstage/core": "^0.6.3",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -44,9 +44,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
+4 -4
View File
@@ -30,7 +30,7 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/core": "^0.6.2",
"@backstage/core": "^0.6.3",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -41,9 +41,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
+19
View File
@@ -1,5 +1,24 @@
# @backstage/plugin-github-actions
## 0.3.4
### Patch Changes
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
## 0.3.3
### Patch Changes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-github-actions",
"version": "0.3.3",
"version": "0.3.4",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -32,9 +32,9 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.7.1",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/core": "^0.6.2",
"@backstage/catalog-model": "^0.7.2",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/core": "^0.6.3",
"@backstage/integration": "^0.5.0",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
@@ -50,9 +50,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
+4 -4
View File
@@ -31,7 +31,7 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/core": "^0.6.2",
"@backstage/core": "^0.6.3",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -42,9 +42,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
+5 -5
View File
@@ -31,21 +31,21 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/core": "^0.6.2",
"@backstage/core": "^0.6.3",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
"graphiql": "^1.0.0-alpha.10",
"graphql": "15.4.0",
"graphql": "15.5.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
+19
View File
@@ -1,5 +1,24 @@
# @backstage/plugin-jenkins
## 0.3.11
### Patch Changes
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
## 0.3.10
### Patch Changes
+3 -3
View File
@@ -50,7 +50,7 @@ metadata:
name: 'your-component'
description: 'a description'
annotations:
jenkins.io/github-folder: 'folder-name/job-name'
jenkins.io/github-folder: 'folder-name/project-name'
spec:
type: service
lifecycle: experimental
@@ -84,5 +84,5 @@ YWRtaW46MTFlYzI1NmU0Mzg1MDFjM2Y1Yzc2Yjc1MWE3ZTQ3YWY4Mw== is the base64 of user a
## Limitations
- Only works with organization folder jobs backed by GitHub
- No pagination support currently, limited to 50 jobs - don't run this on a Jenkins with lots of builds
- Only works with organization folder projects backed by GitHub
- No pagination support currently, limited to 50 projects - don't run this on a Jenkins with lots of builds
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-jenkins",
"version": "0.3.10",
"version": "0.3.11",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -31,9 +31,9 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.7.1",
"@backstage/core": "^0.6.2",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/catalog-model": "^0.7.2",
"@backstage/core": "^0.6.3",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -47,9 +47,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 29 KiB

@@ -15,10 +15,10 @@
*/
import React from 'react';
import { useParams } from 'react-router-dom';
import { Content, Link } from '@backstage/core';
import { Breadcrumbs, Content, Link } from '@backstage/core';
import {
Box,
Typography,
Breadcrumbs,
Paper,
TableContainer,
Table,
@@ -36,7 +36,6 @@ import ExternalLinkIcon from '@material-ui/icons/Launch';
const useStyles = makeStyles(theme => ({
root: {
maxWidth: 720,
margin: theme.spacing(2),
},
table: {
padding: theme.spacing(1),
@@ -57,9 +56,10 @@ const BuildWithStepsView = () => {
return (
<div className={classes.root}>
<Breadcrumbs aria-label="breadcrumb">
<Link to="../../..">Jobs</Link>
<Link to="../../..">Projects</Link>
<Typography>Run</Typography>
</Breadcrumbs>
<Box m={2} />
<TableContainer component={Paper} className={classes.table}>
<Table>
<TableBody>
@@ -16,7 +16,7 @@
import React from 'react';
import { Box, IconButton, Link, Typography, Tooltip } from '@material-ui/core';
import RetryIcon from '@material-ui/icons/Replay';
import GitHubIcon from '@material-ui/icons/GitHub';
import JenkinsLogo from '../../../../assets/JenkinsLogo.svg';
import { generatePath, Link as RouterLink } from 'react-router-dom';
import { Table, TableColumn } from '@backstage/core';
import { JenkinsRunStatus } from '../Status';
@@ -231,9 +231,9 @@ export const CITableView = ({
onChangeRowsPerPage={onChangePageSize}
title={
<Box display="flex" alignItems="center">
<GitHubIcon />
<Box mr={1} />
<Typography variant="h6">{projectName}</Typography>
<img src={JenkinsLogo} alt="Jenkins logo" height="50px" />
<Box mr={2} />
<Typography variant="h6">Project: {projectName}</Typography>
</Box>
}
columns={generatedColumns}
+19
View File
@@ -1,5 +1,24 @@
# @backstage/plugin-kafka
## 0.2.4
### Patch Changes
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
## 0.2.3
### Patch Changes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-kafka",
"version": "0.2.3",
"version": "0.2.4",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -20,9 +20,9 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.7.1",
"@backstage/core": "^0.6.2",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/catalog-model": "^0.7.2",
"@backstage/core": "^0.6.3",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -33,9 +33,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/react-hooks": "^3.4.2",
+10
View File
@@ -1,5 +1,15 @@
# @backstage/plugin-kubernetes-backend
## 0.2.7
### Patch Changes
- a70af22a2: update kubernetes plugin backend function to use classes
- Updated dependencies [bad21a085]
- Updated dependencies [a1f5e6545]
- @backstage/catalog-model@0.7.2
- @backstage/config@0.1.3
## 0.2.6
### Patch Changes
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-kubernetes-backend",
"version": "0.2.6",
"version": "0.2.7",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -33,8 +33,8 @@
"dependencies": {
"@aws-sdk/credential-provider-node": "^3.3.0",
"@backstage/backend-common": "^0.5.2",
"@backstage/catalog-model": "^0.7.1",
"@backstage/config": "^0.1.2",
"@backstage/catalog-model": "^0.7.2",
"@backstage/config": "^0.1.3",
"@kubernetes/client-node": "^0.13.2",
"@types/express": "^4.17.6",
"aws4": "^1.11.0",
@@ -51,7 +51,7 @@
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.6.0",
"@backstage/cli": "^0.6.2",
"@types/aws4": "^1.5.1",
"supertest": "^4.0.2"
},
@@ -14,11 +14,9 @@
* limitations under the License.
*/
import { handleGetKubernetesObjectsForService } from './getKubernetesObjectsForServiceHandler';
import { getVoidLogger } from '@backstage/backend-common';
import { ObjectFetchParams } from '..';
const TEST_SERVICE_ID = 'my-service';
import { KubernetesFanOutHandler } from './KubernetesFanOutHandler';
const fetchObjectsForService = jest.fn();
@@ -81,34 +79,34 @@ describe('handleGetKubernetesObjectsForService', () => {
mockFetch(fetchObjectsForService);
const result = await handleGetKubernetesObjectsForService(
TEST_SERVICE_ID,
const sut = new KubernetesFanOutHandler(
getVoidLogger(),
{
fetchObjectsForService: fetchObjectsForService,
fetchObjectsForService,
},
{
getClustersByServiceId,
},
getVoidLogger(),
{
entity: {
apiVersion: 'backstage.io/v1beta1',
kind: 'Component',
metadata: {
name: 'test-component',
annotations: {
'backstage.io/kubernetes-labels-selector':
'backstage.io/test-label=test-component',
},
},
spec: {
type: 'service',
lifecycle: 'production',
owner: 'joe',
);
const result = await sut.getKubernetesObjectsByEntity({
entity: {
apiVersion: 'backstage.io/v1beta1',
kind: 'Component',
metadata: {
name: 'test-component',
annotations: {
'backstage.io/kubernetes-labels-selector':
'backstage.io/test-label=test-component',
},
},
spec: {
type: 'service',
lifecycle: 'production',
owner: 'joe',
},
},
);
});
expect(getClustersByServiceId.mock.calls.length).toBe(1);
expect(fetchObjectsForService.mock.calls.length).toBe(1);
@@ -124,7 +122,7 @@ describe('handleGetKubernetesObjectsForService', () => {
resources: [
{
metadata: {
name: 'my-pods-my-service-test-cluster',
name: 'my-pods-test-component-test-cluster',
},
},
],
@@ -134,7 +132,7 @@ describe('handleGetKubernetesObjectsForService', () => {
resources: [
{
metadata: {
name: 'my-configmaps-my-service-test-cluster',
name: 'my-configmaps-test-component-test-cluster',
},
},
],
@@ -144,7 +142,7 @@ describe('handleGetKubernetesObjectsForService', () => {
resources: [
{
metadata: {
name: 'my-services-my-service-test-cluster',
name: 'my-services-test-component-test-cluster',
},
},
],
@@ -172,37 +170,37 @@ describe('handleGetKubernetesObjectsForService', () => {
mockFetch(fetchObjectsForService);
const result = await handleGetKubernetesObjectsForService(
TEST_SERVICE_ID,
const sut = new KubernetesFanOutHandler(
getVoidLogger(),
{
fetchObjectsForService: fetchObjectsForService,
fetchObjectsForService,
},
{
getClustersByServiceId,
},
getVoidLogger(),
{
auth: {
google: 'google_token_123',
);
const result = await sut.getKubernetesObjectsByEntity({
auth: {
google: 'google_token_123',
},
entity: {
apiVersion: 'backstage.io/v1beta1',
kind: 'Component',
metadata: {
name: 'test-component',
annotations: {
'backstage.io/kubernetes-labels-selector':
'backstage.io/test-label=test-component',
},
},
entity: {
apiVersion: 'backstage.io/v1beta1',
kind: 'Component',
metadata: {
name: 'test-component',
annotations: {
'backstage.io/kubernetes-labels-selector':
'backstage.io/test-label=test-component',
},
},
spec: {
type: 'service',
lifecycle: 'production',
owner: 'joe',
},
spec: {
type: 'service',
lifecycle: 'production',
owner: 'joe',
},
},
);
});
expect(getClustersByServiceId.mock.calls.length).toBe(1);
expect(fetchObjectsForService.mock.calls.length).toBe(2);
@@ -218,7 +216,7 @@ describe('handleGetKubernetesObjectsForService', () => {
resources: [
{
metadata: {
name: 'my-pods-my-service-test-cluster',
name: 'my-pods-test-component-test-cluster',
},
},
],
@@ -228,7 +226,7 @@ describe('handleGetKubernetesObjectsForService', () => {
resources: [
{
metadata: {
name: 'my-configmaps-my-service-test-cluster',
name: 'my-configmaps-test-component-test-cluster',
},
},
],
@@ -238,7 +236,7 @@ describe('handleGetKubernetesObjectsForService', () => {
resources: [
{
metadata: {
name: 'my-services-my-service-test-cluster',
name: 'my-services-test-component-test-cluster',
},
},
],
@@ -256,7 +254,7 @@ describe('handleGetKubernetesObjectsForService', () => {
resources: [
{
metadata: {
name: 'my-pods-my-service-other-cluster',
name: 'my-pods-test-component-other-cluster',
},
},
],
@@ -266,7 +264,7 @@ describe('handleGetKubernetesObjectsForService', () => {
resources: [
{
metadata: {
name: 'my-configmaps-my-service-other-cluster',
name: 'my-configmaps-test-component-other-cluster',
},
},
],
@@ -276,7 +274,7 @@ describe('handleGetKubernetesObjectsForService', () => {
resources: [
{
metadata: {
name: 'my-services-my-service-other-cluster',
name: 'my-services-test-component-other-cluster',
},
},
],
@@ -0,0 +1,109 @@
/*
* Copyright 2021 Spotify AB
*
* 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 { Logger } from 'winston';
import {
ClusterDetails,
KubernetesFetcher,
KubernetesObjectTypes,
KubernetesRequestBody,
KubernetesServiceLocator,
} from '../types/types';
import { KubernetesAuthTranslator } from '../kubernetes-auth-translator/types';
import { KubernetesAuthTranslatorGenerator } from '../kubernetes-auth-translator/KubernetesAuthTranslatorGenerator';
const DEFAULT_OBJECTS = new Set<KubernetesObjectTypes>([
'pods',
'services',
'configmaps',
'deployments',
'replicasets',
'horizontalpodautoscalers',
'ingresses',
]);
export class KubernetesFanOutHandler {
private readonly logger: Logger;
private readonly fetcher: KubernetesFetcher;
private readonly serviceLocator: KubernetesServiceLocator;
constructor(
logger: Logger,
fetcher: KubernetesFetcher,
serviceLocator: KubernetesServiceLocator,
) {
this.logger = logger;
this.fetcher = fetcher;
this.serviceLocator = serviceLocator;
}
async getKubernetesObjectsByEntity(
requestBody: KubernetesRequestBody,
objectTypesToFetch: Set<KubernetesObjectTypes> = DEFAULT_OBJECTS,
) {
const entityName = requestBody.entity.metadata.name;
const clusterDetails: ClusterDetails[] = await this.serviceLocator.getClustersByServiceId(
entityName,
);
// Execute all of these async actions simultaneously/without blocking sequentially as no common object is modified by them
const promises: Promise<ClusterDetails>[] = clusterDetails.map(cd => {
const kubernetesAuthTranslator: KubernetesAuthTranslator = KubernetesAuthTranslatorGenerator.getKubernetesAuthTranslatorInstance(
cd.authProvider,
);
return kubernetesAuthTranslator.decorateClusterDetailsWithAuth(
cd,
requestBody,
);
});
const clusterDetailsDecoratedForAuth: ClusterDetails[] = await Promise.all(
promises,
);
this.logger.info(
`entity.metadata.name=${entityName} clusterDetails=[${clusterDetailsDecoratedForAuth
.map(c => c.name)
.join(', ')}]`,
);
const labelSelector: string =
requestBody.entity?.metadata?.annotations?.[
'backstage.io/kubernetes-label-selector'
] || `backstage.io/kubernetes-id=${entityName}`;
return Promise.all(
clusterDetailsDecoratedForAuth.map(clusterDetailsItem => {
return this.fetcher
.fetchObjectsForService({
serviceId: entityName,
clusterDetails: clusterDetailsItem,
objectTypesToFetch,
labelSelector,
})
.then(result => {
return {
cluster: {
name: clusterDetailsItem.name,
},
resources: result.responses,
errors: result.errors,
};
});
}),
).then(r => ({ items: r }));
}
}
@@ -1,107 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { Logger } from 'winston';
import {
KubernetesRequestBody,
ClusterDetails,
KubernetesServiceLocator,
KubernetesFetcher,
KubernetesObjectTypes,
ObjectsByEntityResponse,
ObjectFetchParams,
} from '../types/types';
import { KubernetesAuthTranslator } from '../kubernetes-auth-translator/types';
import { KubernetesAuthTranslatorGenerator } from '../kubernetes-auth-translator/KubernetesAuthTranslatorGenerator';
export type GetKubernetesObjectsForServiceHandler = (
serviceId: string,
fetcher: KubernetesFetcher,
serviceLocator: KubernetesServiceLocator,
logger: Logger,
requestBody: KubernetesRequestBody,
objectTypesToFetch?: Set<KubernetesObjectTypes>,
) => Promise<ObjectsByEntityResponse>;
const DEFAULT_OBJECTS = new Set<KubernetesObjectTypes>([
'pods',
'services',
'configmaps',
'deployments',
'replicasets',
'horizontalpodautoscalers',
'ingresses',
]);
// Fans out the request to all clusters that the service lives in, aggregates their responses together
export const handleGetKubernetesObjectsForService: GetKubernetesObjectsForServiceHandler = async (
serviceId,
fetcher,
serviceLocator,
logger,
requestBody,
objectTypesToFetch = DEFAULT_OBJECTS,
) => {
const clusterDetails: ClusterDetails[] = await serviceLocator.getClustersByServiceId(
serviceId,
);
// Execute all of these async actions simultaneously/without blocking sequentially as no common object is modified by them
const promises: Promise<ClusterDetails>[] = clusterDetails.map(cd => {
const kubernetesAuthTranslator: KubernetesAuthTranslator = KubernetesAuthTranslatorGenerator.getKubernetesAuthTranslatorInstance(
cd.authProvider,
);
return kubernetesAuthTranslator.decorateClusterDetailsWithAuth(
cd,
requestBody,
);
});
const clusterDetailsDecoratedForAuth: ClusterDetails[] = await Promise.all(
promises,
);
logger.info(
`serviceId=${serviceId} clusterDetails=[${clusterDetailsDecoratedForAuth
.map(c => c.name)
.join(', ')}]`,
);
const labelSelector: string =
requestBody.entity?.metadata?.annotations?.[
'backstage.io/kubernetes-label-selector'
] || `backstage.io/kubernetes-id=${requestBody.entity.metadata.name}`;
return Promise.all(
clusterDetailsDecoratedForAuth.map(clusterDetailsItem => {
return fetcher
.fetchObjectsForService({
serviceId,
clusterDetails: clusterDetailsItem,
objectTypesToFetch,
labelSelector,
} as ObjectFetchParams)
.then(result => {
return {
cluster: {
name: clusterDetailsItem.name,
},
resources: result.responses,
errors: result.errors,
};
});
}),
).then(r => ({ items: r }));
};
@@ -18,35 +18,18 @@ import { getVoidLogger } from '@backstage/backend-common';
import express from 'express';
import request from 'supertest';
import { makeRouter } from './router';
import {
KubernetesServiceLocator,
KubernetesFetcher,
ObjectsByEntityResponse,
} from '..';
import { KubernetesFanOutHandler } from './KubernetesFanOutHandler';
describe('router', () => {
let app: express.Express;
let kubernetesFetcher: jest.Mocked<KubernetesFetcher>;
let kubernetesServiceLocator: jest.Mocked<KubernetesServiceLocator>;
let handleGetByServiceId: jest.Mock<Promise<ObjectsByEntityResponse>>;
let kubernetesFanOutHandler: jest.Mocked<KubernetesFanOutHandler>;
beforeAll(async () => {
kubernetesFetcher = {
fetchObjectsForService: jest.fn(),
};
kubernetesFanOutHandler = {
getKubernetesObjectsByEntity: jest.fn(),
} as any;
kubernetesServiceLocator = {
getClustersByServiceId: jest.fn(),
};
handleGetByServiceId = jest.fn();
const router = makeRouter(
getVoidLogger(),
kubernetesFetcher,
kubernetesServiceLocator,
handleGetByServiceId as any,
);
const router = makeRouter(getVoidLogger(), kubernetesFanOutHandler);
app = express().use(router);
});
@@ -67,7 +50,9 @@ describe('router', () => {
],
},
} as any;
handleGetByServiceId.mockReturnValueOnce(Promise.resolve(result));
kubernetesFanOutHandler.getKubernetesObjectsByEntity.mockReturnValueOnce(
Promise.resolve(result),
);
const response = await request(app).post('/services/test-service');
@@ -87,7 +72,9 @@ describe('router', () => {
],
},
} as any;
handleGetByServiceId.mockReturnValueOnce(Promise.resolve(result));
kubernetesFanOutHandler.getKubernetesObjectsByEntity.mockReturnValueOnce(
Promise.resolve(result),
);
const response = await request(app)
.post('/services/test-service')
@@ -103,7 +90,9 @@ describe('router', () => {
});
it('internal error: lists kubernetes objects', async () => {
handleGetByServiceId.mockRejectedValue(Error('some internal error'));
kubernetesFanOutHandler.getKubernetesObjectsByEntity.mockRejectedValue(
Error('some internal error'),
);
const response = await request(app).post('/services/test-service');
@@ -21,19 +21,15 @@ import { Config } from '@backstage/config';
import { MultiTenantServiceLocator } from '../service-locator/MultiTenantServiceLocator';
import { KubernetesClientBasedFetcher } from './KubernetesFetcher';
import { KubernetesClientProvider } from './KubernetesClientProvider';
import {
GetKubernetesObjectsForServiceHandler,
handleGetKubernetesObjectsForService,
} from './getKubernetesObjectsForServiceHandler';
import {
KubernetesRequestBody,
KubernetesServiceLocator,
KubernetesFetcher,
ServiceLocatorMethod,
ClusterLocatorMethod,
ClusterDetails,
} from '..';
import { getCombinedClusterDetails } from '../cluster-locator';
import { KubernetesFanOutHandler } from './KubernetesFanOutHandler';
export interface RouterOptions {
logger: Logger;
@@ -62,9 +58,7 @@ const getServiceLocator = (
export const makeRouter = (
logger: Logger,
fetcher: KubernetesFetcher,
serviceLocator: KubernetesServiceLocator,
handleGetByEntity: GetKubernetesObjectsForServiceHandler,
kubernetesFanOutHandler: KubernetesFanOutHandler,
): express.Router => {
const router = Router();
router.use(express.json());
@@ -73,11 +67,7 @@ export const makeRouter = (
const serviceId = req.params.serviceId;
const requestBody: KubernetesRequestBody = req.body;
try {
const response = await handleGetByEntity(
serviceId,
fetcher,
serviceLocator,
logger,
const response = await kubernetesFanOutHandler.getKubernetesObjectsByEntity(
requestBody,
);
res.send(response);
@@ -115,10 +105,11 @@ export async function createRouter(
const serviceLocator = getServiceLocator(options.config, clusterDetails);
return makeRouter(
const kubernetesFanOutHandler = new KubernetesFanOutHandler(
logger,
fetcher,
serviceLocator,
handleGetKubernetesObjectsForService,
);
return makeRouter(logger, kubernetesFanOutHandler);
}
+23
View File
@@ -1,5 +1,28 @@
# @backstage/plugin-kubernetes
## 0.3.11
### Patch Changes
- Updated dependencies [a70af22a2]
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [a1f5e6545]
- Updated dependencies [2c1f2a7c2]
- @backstage/plugin-kubernetes-backend@0.2.7
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
- @backstage/config@0.1.3
## 0.3.10
### Patch Changes
+9 -9
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-kubernetes",
"version": "0.3.10",
"version": "0.3.11",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -31,11 +31,11 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.7.1",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/config": "^0.1.2",
"@backstage/core": "^0.6.2",
"@backstage/plugin-kubernetes-backend": "^0.2.6",
"@backstage/catalog-model": "^0.7.2",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/config": "^0.1.3",
"@backstage/core": "^0.6.3",
"@backstage/plugin-kubernetes-backend": "^0.2.7",
"@backstage/theme": "^0.2.3",
"@kubernetes/client-node": "^0.13.2",
"@material-ui/core": "^4.11.0",
@@ -48,9 +48,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
+21
View File
@@ -1,5 +1,26 @@
# @backstage/plugin-lighthouse
## 0.2.12
### Patch Changes
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [a1f5e6545]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
- @backstage/config@0.1.3
## 0.2.11
### Patch Changes
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-lighthouse",
"version": "0.2.11",
"version": "0.2.12",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -31,10 +31,10 @@
"start": "backstage-cli plugin:serve"
},
"dependencies": {
"@backstage/catalog-model": "^0.7.1",
"@backstage/config": "^0.1.2",
"@backstage/core": "^0.6.2",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/catalog-model": "^0.7.2",
"@backstage/config": "^0.1.3",
"@backstage/core": "^0.6.3",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -46,9 +46,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
+4 -4
View File
@@ -31,7 +31,7 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/core": "^0.6.2",
"@backstage/core": "^0.6.3",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -41,9 +41,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
+22
View File
@@ -1,5 +1,27 @@
# @backstage/plugin-org
## 0.3.8
### Patch Changes
- e3bc5aad7: Use the `pageTheme` to colour the OwnershipCard boxes with their respective theme colours.
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [b6c4f485d]
- Updated dependencies [3a58084b6]
- Updated dependencies [2c1f2a7c2]
- @backstage/core-api@0.2.11
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
## 0.3.7
### Patch Changes
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-org",
"version": "0.3.7",
"version": "0.3.8",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -20,10 +20,10 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.7.1",
"@backstage/core": "^0.6.2",
"@backstage/core-api": "^0.2.10",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/catalog-model": "^0.7.2",
"@backstage/core": "^0.6.3",
"@backstage/core-api": "^0.2.11",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -35,9 +35,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
+19
View File
@@ -1,5 +1,24 @@
# @backstage/plugin-pagerduty
## 0.3.1
### Patch Changes
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
## 0.3.0
### Minor Changes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-pagerduty",
"version": "0.3.0",
"version": "0.3.1",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -30,9 +30,9 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.7.1",
"@backstage/core": "^0.6.2",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/catalog-model": "^0.7.2",
"@backstage/core": "^0.6.3",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -46,9 +46,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
+19
View File
@@ -1,5 +1,24 @@
# @backstage/plugin-register-component
## 0.2.11
### Patch Changes
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
## 0.2.10
### Patch Changes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-register-component",
"version": "0.2.10",
"version": "0.2.11",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -30,9 +30,9 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.7.1",
"@backstage/core": "^0.6.2",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/catalog-model": "^0.7.2",
"@backstage/core": "^0.6.3",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -45,9 +45,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
+19
View File
@@ -1,5 +1,24 @@
# @backstage/plugin-rollbar
## 0.3.2
### Patch Changes
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
## 0.3.1
### Patch Changes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-rollbar",
"version": "0.3.1",
"version": "0.3.2",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -31,9 +31,9 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.7.1",
"@backstage/core": "^0.6.2",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/catalog-model": "^0.7.2",
"@backstage/core": "^0.6.3",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -47,9 +47,9 @@
"react-use": "^15.3.3"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/react-hooks": "^3.3.0",
+60
View File
@@ -1,5 +1,65 @@
# @backstage/plugin-scaffolder-backend
## 0.8.0
### Minor Changes
- a5f42cf66: # Stateless scaffolding
The scaffolder has been redesigned to be horizontally scalable and to persistently store task state and execution logs in the database.
Each scaffolder task is given a unique task ID which is persisted in the database.
Tasks are then picked up by a `TaskWorker` which performs the scaffolding steps.
Execution logs are also persisted in the database meaning you can now refresh the scaffolder task status page without losing information.
The task status page is now dynamically created based on the step information stored in the database.
This allows for custom steps to be displayed once the next version of the scaffolder template schema is available.
The task page is updated to display links to both the git repository and to the newly created catalog entity.
Component registration has moved from the frontend into a separate registration step executed by the `TaskWorker`. This requires that a `CatalogClient` is passed to the scaffolder backend instead of the old `CatalogEntityClient`.
Make sure to update `plugins/scaffolder.ts`
```diff
import {
CookieCutter,
createRouter,
Preparers,
Publishers,
CreateReactAppTemplater,
Templaters,
- CatalogEntityClient,
} from '@backstage/plugin-scaffolder-backend';
+import { CatalogClient } from '@backstage/catalog-client';
const discovery = SingleHostDiscovery.fromConfig(config);
-const entityClient = new CatalogEntityClient({ discovery });
+const catalogClient = new CatalogClient({ discoveryApi: discovery })
return await createRouter({
preparers,
templaters,
publishers,
logger,
config,
dockerClient,
- entityClient,
database,
+ catalogClient,
});
```
As well as adding the `@backstage/catalog-client` packages as a dependency of your backend package.
### Patch Changes
- Updated dependencies [bad21a085]
- Updated dependencies [a1f5e6545]
- @backstage/catalog-model@0.7.2
- @backstage/config@0.1.3
## 0.7.1
### Patch Changes
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-scaffolder-backend",
"version": "0.7.1",
"version": "0.8.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -31,8 +31,8 @@
"dependencies": {
"@backstage/backend-common": "^0.5.4",
"@backstage/catalog-client": "^0.3.6",
"@backstage/catalog-model": "^0.7.1",
"@backstage/config": "^0.1.2",
"@backstage/catalog-model": "^0.7.2",
"@backstage/config": "^0.1.3",
"@backstage/integration": "^0.5.0",
"@gitbeaker/core": "^28.0.2",
"@gitbeaker/node": "^28.0.2",
@@ -63,8 +63,8 @@
"yaml": "^1.10.0"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/test-utils": "^0.1.8",
"@types/fs-extra": "^9.0.1",
"@types/mock-fs": "^4.13.0",
"@types/supertest": "^2.0.8",
+112
View File
@@ -1,5 +1,117 @@
# @backstage/plugin-scaffolder
## 0.6.0
### Minor Changes
- a5f42cf66: The Scaffolder and Catalog plugins have been migrated to partially require use of the [new composability API](https://backstage.io/docs/plugins/composability). The Scaffolder used to register its pages using the deprecated route registration plugin API, but those registrations have been removed. This means you now need to add the Scaffolder plugin page to the app directly.
The page is imported from the Scaffolder plugin and added to the `<FlatRoutes>` component:
```tsx
<Route path="/create" element={<ScaffolderPage />} />
```
The Catalog plugin has also been migrated to use an [external route reference](https://backstage.io/docs/plugins/composability#binding-external-routes-in-the-app) to dynamically link to the create component page. This means you need to migrate the catalog plugin to use the new extension components, as well as bind the external route.
To use the new extension components, replace existing usage of the `CatalogRouter` with the following:
```tsx
<Route path="/catalog" element={<CatalogIndexPage />} />
<Route path="/catalog/:namespace/:kind/:name" element={<CatalogEntityPage />}>
<EntityPage />
</Route>
```
And to bind the external route from the catalog plugin to the scaffolder template index page, make sure you have the appropriate imports and add the following to the `createApp` call:
```ts
import { catalogPlugin } from '@backstage/plugin-catalog';
import { scaffolderPlugin } from '@backstage/plugin-scaffolder';
const app = createApp({
// ...
bindRoutes({ bind }) {
bind(catalogPlugin.externalRoutes, {
createComponent: scaffolderPlugin.routes.root,
});
},
});
```
- d0760ecdf: Moved common useStarredEntities hook to plugin-catalog-react
- e8e35fb5f: Adding Search and Filter features to Scaffolder/Templates Grid
### Patch Changes
- a5f42cf66: # Stateless scaffolding
The scaffolder has been redesigned to be horizontally scalable and to persistently store task state and execution logs in the database.
Each scaffolder task is given a unique task ID which is persisted in the database.
Tasks are then picked up by a `TaskWorker` which performs the scaffolding steps.
Execution logs are also persisted in the database meaning you can now refresh the scaffolder task status page without losing information.
The task status page is now dynamically created based on the step information stored in the database.
This allows for custom steps to be displayed once the next version of the scaffolder template schema is available.
The task page is updated to display links to both the git repository and to the newly created catalog entity.
Component registration has moved from the frontend into a separate registration step executed by the `TaskWorker`. This requires that a `CatalogClient` is passed to the scaffolder backend instead of the old `CatalogEntityClient`.
Make sure to update `plugins/scaffolder.ts`
```diff
import {
CookieCutter,
createRouter,
Preparers,
Publishers,
CreateReactAppTemplater,
Templaters,
- CatalogEntityClient,
} from '@backstage/plugin-scaffolder-backend';
+import { CatalogClient } from '@backstage/catalog-client';
const discovery = SingleHostDiscovery.fromConfig(config);
-const entityClient = new CatalogEntityClient({ discovery });
+const catalogClient = new CatalogClient({ discoveryApi: discovery })
return await createRouter({
preparers,
templaters,
publishers,
logger,
config,
dockerClient,
- entityClient,
database,
+ catalogClient,
});
```
As well as adding the `@backstage/catalog-client` packages as a dependency of your backend package.
- e488f0502: Update messages that process during loading, error, and no templates found.
Remove unused dependencies.
- Updated dependencies [3a58084b6]
- Updated dependencies [e799e74d4]
- Updated dependencies [d0760ecdf]
- Updated dependencies [1407b34c6]
- Updated dependencies [88f1f1b60]
- Updated dependencies [bad21a085]
- Updated dependencies [9615e68fb]
- Updated dependencies [49f9b7346]
- Updated dependencies [5c2e2863f]
- Updated dependencies [3a58084b6]
- Updated dependencies [a1f5e6545]
- Updated dependencies [2c1f2a7c2]
- @backstage/core@0.6.3
- @backstage/plugin-catalog-react@0.1.0
- @backstage/catalog-model@0.7.2
- @backstage/config@0.1.3
## 0.5.1
### Patch Changes
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-scaffolder",
"version": "0.5.1",
"version": "0.6.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -31,10 +31,10 @@
},
"dependencies": {
"@backstage/catalog-client": "^0.3.6",
"@backstage/catalog-model": "^0.7.1",
"@backstage/config": "^0.1.2",
"@backstage/core": "^0.6.2",
"@backstage/plugin-catalog-react": "^0.0.4",
"@backstage/catalog-model": "^0.7.2",
"@backstage/config": "^0.1.3",
"@backstage/core": "^0.6.3",
"@backstage/plugin-catalog-react": "^0.1.0",
"@backstage/theme": "^0.2.3",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
@@ -56,9 +56,9 @@
"zen-observable": "^0.8.15"
},
"devDependencies": {
"@backstage/cli": "^0.6.1",
"@backstage/dev-utils": "^0.1.11",
"@backstage/test-utils": "^0.1.7",
"@backstage/cli": "^0.6.2",
"@backstage/dev-utils": "^0.1.12",
"@backstage/test-utils": "^0.1.8",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^10.4.1",
"@testing-library/user-event": "^12.0.7",
@@ -25,7 +25,7 @@ import {
Theme,
Typography,
} from '@material-ui/core';
import React, { useCallback, useContext, useState } from 'react';
import React, { useContext } from 'react';
import { filterGroupsContext } from '../../filter/context';
const useStyles = makeStyles<Theme>(theme => ({
@@ -59,20 +59,12 @@ type Props = {
export const ResultsFilter = ({ availableCategories }: Props) => {
const classes = useStyles();
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
const context = useContext(filterGroupsContext);
if (!context) {
throw new Error(`Must be used inside an EntityFilterGroupsProvider`);
}
const setSelectedCatgoriesFilter = context?.setSelectedCategories;
const updateSelectedCategories = useCallback(
(categories: string[]) => {
setSelectedCategories(categories);
setSelectedCatgoriesFilter(categories);
},
[setSelectedCategories, setSelectedCatgoriesFilter],
);
const { selectedCategories, setSelectedCategories } = context;
return (
<>
@@ -80,7 +72,7 @@ export const ResultsFilter = ({ availableCategories }: Props) => {
<Typography variant="subtitle2" className={classes.filterBoxTitle}>
Refine Results
</Typography>{' '}
<Button onClick={() => updateSelectedCategories([])}>Clear</Button>
<Button onClick={() => setSelectedCategories([])}>Clear</Button>
</div>
<Divider />
<Typography variant="subtitle2" className={classes.title}>
@@ -95,7 +87,7 @@ export const ResultsFilter = ({ availableCategories }: Props) => {
dense
button
onClick={() =>
updateSelectedCategories(
setSelectedCategories(
selectedCategories.includes(category)
? selectedCategories.filter(
selectedCategory => selectedCategory !== category,

Some files were not shown because too many files have changed in this diff Show More