Merge remote-tracking branch 'origin/master' into cost-insight-trending-line

# Conflicts:
#	plugins/cost-insights/src/components/CostOverviewCard/CostOverviewChart.tsx
#	plugins/cost-insights/src/testUtils/providers.tsx
This commit is contained in:
bnechyporenko
2022-11-22 16:31:23 +01:00
195 changed files with 2840 additions and 1172 deletions
@@ -19,7 +19,6 @@ import {
Content,
DocsIcon,
Header,
Lifecycle,
Page,
useSidebarPinState,
} from '@backstage/core-components';
@@ -66,7 +65,7 @@ const SearchPage = () => {
return (
<Page themeId="home">
{!isMobile && <Header title="Search" subtitle={<Lifecycle alpha />} />}
{!isMobile && <Header title="Search" />}
<Content>
<Grid container direction="row">
<Grid item xs={12}>
+1 -1
View File
@@ -117,7 +117,7 @@
"better-sqlite3": "^7.5.0",
"http-errors": "^2.0.0",
"mock-fs": "^5.1.0",
"msw": "^0.48.0",
"msw": "^0.49.0",
"mysql2": "^2.2.5",
"recursive-readdir": "^2.2.2",
"supertest": "^6.1.3"
+1 -1
View File
@@ -41,7 +41,7 @@
"@backstage/config": "workspace:^",
"better-sqlite3": "^7.5.0",
"knex": "^2.0.0",
"msw": "^0.48.0",
"msw": "^0.49.0",
"mysql2": "^2.2.5",
"pg": "^8.3.0",
"testcontainers": "^8.1.2",
+1 -1
View File
@@ -38,7 +38,7 @@
},
"devDependencies": {
"@backstage/cli": "workspace:^",
"msw": "^0.48.0"
"msw": "^0.49.0"
},
"files": [
"dist"
+1 -1
View File
@@ -155,7 +155,7 @@
"@types/yarnpkg__lockfile": "^1.1.4",
"del": "^6.0.0",
"mock-fs": "^5.1.0",
"msw": "^0.48.0",
"msw": "^0.49.0",
"nodemon": "^2.0.2",
"ts-node": "^10.0.0",
"type-fest": "^2.0.0"
@@ -41,7 +41,7 @@
"@backstage/cli": "{{versionQuery '@backstage/cli'}}",
"@types/supertest": "{{versionQuery '@types/supertest' '2.0.12'}}",
"supertest": "{{versionQuery 'supertest' '6.2.4'}}",
"msw": "{{versionQuery 'msw' '0.47.0'}}"
"msw": "{{versionQuery 'msw' '0.49.0'}}"
},
"files": [
"dist"
@@ -48,7 +48,7 @@
"@testing-library/react": "{{versionQuery '@testing-library/react' '12.1.3'}}",
"@testing-library/user-event": "{{versionQuery '@testing-library/user-event' '14.0.0'}}",
"@types/node": "{{versionQuery '@types/node' '16.11.26'}}",
"msw": "{{versionQuery 'msw' '0.47.0'}}",
"msw": "{{versionQuery 'msw' '0.49.0'}}",
"cross-fetch": "{{versionQuery 'cross-fetch' '3.1.5'}}"
},
"files": [
+1 -1
View File
@@ -56,7 +56,7 @@
"@types/node": "^16.11.26",
"@types/yup": "^0.29.13",
"mock-fs": "^5.1.0",
"msw": "^0.48.0"
"msw": "^0.49.0"
},
"files": [
"dist"
+1 -1
View File
@@ -57,7 +57,7 @@
"@types/node": "^16.11.26",
"@types/zen-observable": "^0.8.0",
"cross-fetch": "^3.1.5",
"msw": "^0.48.0",
"msw": "^0.49.0",
"react-router-beta": "npm:react-router@6.0.0-beta.0",
"react-router-dom-beta": "npm:react-router-dom@6.0.0-beta.0",
"react-router-dom-stable": "npm:react-router-dom@^6.3.0",
+1 -1
View File
@@ -99,7 +99,7 @@
"@types/react-window": "^1.8.5",
"@types/zen-observable": "^0.8.0",
"cross-fetch": "^3.1.5",
"msw": "^0.48.0"
"msw": "^0.49.0"
},
"files": [
"dist"
@@ -15,31 +15,38 @@
*/
import React from 'react';
import { fireEvent, screen } from '@testing-library/react';
import {
render,
act,
RenderResult,
waitFor,
fireEvent,
screen,
} from '@testing-library/react';
import { wrapInTestApp, renderInTestApp } from '@backstage/test-utils';
MockConfigApi,
renderInTestApp,
TestApiProvider,
} from '@backstage/test-utils';
import { SupportButton } from './SupportButton';
import { configApiRef } from '@backstage/core-plugin-api';
const configApi = new MockConfigApi({
app: {
support: {
url: 'https://github.com',
items: [
{
title: 'Github',
icon: 'github',
links: [{ title: 'Github Issues', url: '/issues' }],
},
],
},
},
});
const SUPPORT_BUTTON_ID = 'support-button';
const POPOVER_ID = 'support-button-popover';
describe('<SupportButton />', () => {
it('renders without exploding', async () => {
let renderResult: RenderResult;
await renderInTestApp(<SupportButton />);
await act(async () => {
renderResult = render(wrapInTestApp(<SupportButton />));
});
await waitFor(() =>
expect(renderResult.getByTestId(SUPPORT_BUTTON_ID)).toBeInTheDocument(),
);
expect(screen.getByTestId(SUPPORT_BUTTON_ID)).toBeInTheDocument();
});
it('supports passing a title', async () => {
@@ -48,26 +55,52 @@ describe('<SupportButton />', () => {
expect(screen.getByText('Custom title')).toBeInTheDocument();
});
it('supports passing link items through props', async () => {
await renderInTestApp(
<SupportButton
items={[
{
title: 'Documentation',
icon: 'description',
links: [{ title: 'Show docs', url: '/docs' }],
},
]}
/>,
);
const supportButton = screen.getByTestId(SUPPORT_BUTTON_ID);
expect(supportButton).toBeInTheDocument();
fireEvent.click(supportButton);
const documentationItem = screen.getByText('Documentation');
expect(documentationItem).toBeInTheDocument();
});
it('shows items from support config', async () => {
await renderInTestApp(
<TestApiProvider apis={[[configApiRef, configApi]]}>
<SupportButton />
</TestApiProvider>,
);
const supportButton = screen.getByTestId(SUPPORT_BUTTON_ID);
expect(supportButton).toBeInTheDocument();
fireEvent.click(supportButton);
const defaultGithubSupportConfig = screen.getByText('Github Issues');
expect(defaultGithubSupportConfig).toBeInTheDocument();
});
it('shows popover on click', async () => {
let renderResult: RenderResult;
await renderInTestApp(<SupportButton />);
await act(async () => {
renderResult = render(wrapInTestApp(<SupportButton />));
});
const supportButton = screen.getByTestId(SUPPORT_BUTTON_ID);
expect(supportButton).toBeInTheDocument();
let button: HTMLElement;
fireEvent.click(supportButton);
await waitFor(() => {
expect(renderResult.getByTestId(SUPPORT_BUTTON_ID)).toBeInTheDocument();
button = renderResult.getByTestId(SUPPORT_BUTTON_ID);
});
await act(async () => {
fireEvent.click(button);
});
await waitFor(() => {
expect(renderResult.getByTestId(POPOVER_ID)).toBeInTheDocument();
});
expect(screen.getByTestId(POPOVER_ID)).toBeInTheDocument();
});
});
@@ -35,6 +35,7 @@ import { Link } from '../Link';
type SupportButtonProps = {
title?: string;
items?: SupportItem[];
children?: React.ReactNode;
};
@@ -82,8 +83,8 @@ const SupportListItem = ({ item }: { item: SupportItem }) => {
};
export function SupportButton(props: SupportButtonProps) {
const { title, children } = props;
const { items } = useSupportConfig();
const { title, items, children } = props;
const { items: configItems } = useSupportConfig();
const [popoverOpen, setPopoverOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState<Element | null>(null);
@@ -150,10 +151,9 @@ export function SupportButton(props: SupportButtonProps) {
{child}
</ListItem>
))}
{items &&
items.map((item, i) => (
<SupportListItem item={item} key={`item-${i}`} />
))}
{(items ?? configItems).map((item, i) => (
<SupportListItem item={item} key={`item-${i}`} />
))}
</List>
<DialogActions>
<Button color="primary" onClick={popoverCloseHandler}>
+1 -1
View File
@@ -57,7 +57,7 @@
"@types/prop-types": "^15.7.3",
"@types/zen-observable": "^0.8.0",
"cross-fetch": "^3.1.5",
"msw": "^0.48.0"
"msw": "^0.49.0"
},
"files": [
"dist",
+1 -1
View File
@@ -45,7 +45,7 @@
"@testing-library/user-event": "^14.0.0",
"@types/node": "^16.11.26",
"cross-fetch": "^3.1.5",
"msw": "^0.48.0"
"msw": "^0.49.0"
},
"files": [
"dist"
+1 -1
View File
@@ -46,7 +46,7 @@
"@backstage/config-loader": "workspace:^",
"@backstage/test-utils": "workspace:^",
"@types/luxon": "^3.0.0",
"msw": "^0.48.0"
"msw": "^0.49.0"
},
"files": [
"dist",
+1 -1
View File
@@ -38,7 +38,7 @@
"@backstage/cli": "workspace:^",
"@backstage/test-utils": "workspace:^",
"@types/node": "^16.0.0",
"msw": "^0.48.0"
"msw": "^0.49.0"
},
"files": [
"dist"
+5
View File
@@ -0,0 +1,5 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, {
rules: {
'no-console': 0,
},
});
+11
View File
@@ -0,0 +1,11 @@
# @backstage/repo-tools
This package provides a CLI for backstage repo tooling.
## Installation
Install the package via Yarn:
```sh
yarn add @backstage/repo-tools
```
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env node
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const path = require('path');
// Figure out whether we're running inside the backstage repo or as an installed dependency
/* eslint-disable-next-line no-restricted-syntax */
const isLocal = require('fs').existsSync(path.resolve(__dirname, '../src'));
if (!isLocal || process.env.BACKSTAGE_E2E_CLI_TEST) {
require('..');
} else {
require('ts-node').register({
transpileOnly: true,
/* eslint-disable-next-line no-restricted-syntax */
project: path.resolve(__dirname, '../../../tsconfig.json'),
compilerOptions: {
module: 'CommonJS',
},
});
require('../src');
}
+29
View File
@@ -0,0 +1,29 @@
## CLI Report file for "@backstage/repo-tools"
> Do not edit this file. It is a report generated by `yarn build:api-reports`
### `backstage-repo-tools`
```
Usage: backstage-repo-tools [options] [command]
Options:
-V, --version
-h, --help
Commands:
api-reports [options] [path...]
help [command]
```
### `backstage-repo-tools api-reports`
```
Usage: backstage-repo-tools api-reports [options] [path...]
Options:
--ci
--tsc
--docs
-h, --help
```
+52
View File
@@ -0,0 +1,52 @@
{
"name": "@backstage/repo-tools",
"description": "CLI for Backstage repo tooling ",
"version": "0.0.0",
"publishConfig": {
"access": "public"
},
"backstage": {
"role": "cli"
},
"homepage": "https://backstage.io",
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
"directory": "packages/repo-tools"
},
"keywords": [
"backstage"
],
"license": "Apache-2.0",
"main": "dist/index.cjs.js",
"scripts": {
"build": "backstage-cli package build",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"clean": "backstage-cli package clean",
"start": "nodemon --"
},
"bin": {
"backstage-repo-tools": "bin/backstage-repo-tools"
},
"dependencies": {
"@backstage/errors": "workspace:^",
"@microsoft/api-documenter": "^7.17.11",
"@microsoft/api-extractor": "^7.23.0",
"@microsoft/api-extractor-model": "^7.17.2",
"@microsoft/tsdoc": "0.14.1",
"chalk": "^4.0.0",
"commander": "^9.1.0",
"fs-extra": "10.1.0",
"ts-node": "^10.0.0"
},
"files": [
"bin",
"dist/**/*.js"
],
"nodemonConfig": {
"watch": "./src",
"exec": "bin/backstage-repo-tools",
"ext": "ts"
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,120 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { OptionValues } from 'commander';
import { resolve as resolvePath } from 'path';
import fs from 'fs-extra';
import { spawnSync } from 'child_process';
import {
findSpecificPackageDirs,
createTemporaryTsConfig,
findPackageDirs,
categorizePackageDirs,
runApiExtraction,
runCliExtraction,
buildDocs,
} from './api-extractor';
export default async (paths: string[], opts: OptionValues) => {
const tmpDir = resolvePath(
process.cwd(),
'./node_modules/.cache/api-extractor',
);
const projectRoot = resolvePath(process.cwd());
const isCiBuild = opts.ci;
const isDocsBuild = opts.docs;
const runTsc = opts.tsc;
const selectedPackageDirs = await findSpecificPackageDirs(paths);
if (selectedPackageDirs && isCiBuild) {
throw new Error(
'Package path arguments are not supported together with the --ci flag',
);
}
if (!selectedPackageDirs && !isCiBuild && !isDocsBuild) {
console.log('');
console.log(
'TIP: You can generate api-reports for select packages by passing package paths:',
);
console.log('');
console.log(
' yarn build:api-reports packages/config packages/core-plugin-api',
);
console.log('');
}
let temporaryTsConfigPath: string | undefined;
if (selectedPackageDirs) {
temporaryTsConfigPath = await createTemporaryTsConfig(selectedPackageDirs);
}
const tsconfigFilePath =
temporaryTsConfigPath ?? resolvePath(projectRoot, 'tsconfig.json');
if (runTsc) {
await fs.remove(resolvePath(projectRoot, 'dist-types'));
const { status } = spawnSync(
'yarn',
[
'tsc',
['--project', tsconfigFilePath],
['--skipLibCheck', 'false'],
['--incremental', 'false'],
].flat(),
{
stdio: 'inherit',
shell: true,
cwd: projectRoot,
},
);
if (status !== 0) {
process.exit(status || undefined);
}
}
const packageDirs = selectedPackageDirs ?? (await findPackageDirs());
const { tsPackageDirs, cliPackageDirs } = await categorizePackageDirs(
projectRoot,
packageDirs,
);
if (tsPackageDirs.length > 0) {
console.log('# Generating package API reports');
await runApiExtraction({
packageDirs: tsPackageDirs,
outputDir: tmpDir,
isLocalBuild: !isCiBuild,
tsconfigFilePath,
});
}
if (cliPackageDirs.length > 0) {
console.log('# Generating package CLI reports');
await runCliExtraction({
projectRoot,
packageDirs: cliPackageDirs,
isLocalBuild: !isCiBuild,
});
}
if (isDocsBuild) {
console.log('# Generating package documentation');
await buildDocs({
inputDir: tmpDir,
outputDir: resolvePath(projectRoot, 'docs/reference'),
});
}
};
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { assertError } from '@backstage/errors';
import { Command } from 'commander';
import { exitWithError } from '../lib/errors';
export function registerCommands(program: Command) {
program
.command('api-reports [path...]')
.option('--ci', 'CI run checks that there is no changes on API reports')
.option('--tsc', 'executes the tsc compilation before extracting the APIs')
.option('--docs', 'generates the api documentation')
.description('Generate an API report for selected packages')
.action(
lazy(() => import('./api-reports/api-reports').then(m => m.default)),
);
}
// Wraps an action function so that it always exits and handles errors
function lazy(
getActionFunc: () => Promise<(...args: any[]) => Promise<void>>,
): (...args: any[]) => Promise<never> {
return async (...args: any[]) => {
try {
const actionFunc = await getActionFunc();
await actionFunc(...args);
process.exit(0);
} catch (error) {
assertError(error);
exitWithError(error);
}
};
}
+52
View File
@@ -0,0 +1,52 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* CLI for Backstage repo tooling
*
* @packageDocumentation
*/
import { program } from 'commander';
import chalk from 'chalk';
import { exitWithError } from './lib/errors';
import { registerCommands } from './commands';
const main = (argv: string[]) => {
program.name('backstage-repo-tools').version('1.0');
registerCommands(program);
program.on('command:*', () => {
console.log();
console.log(chalk.red(`Invalid command: ${program.args.join(' ')}`));
console.log();
program.outputHelp();
process.exit(1);
});
program.parse(argv);
};
process.on('unhandledRejection', rejection => {
if (rejection instanceof Error) {
exitWithError(rejection);
} else {
exitWithError(new Error(`Unknown rejection: '${rejection}'`));
}
});
main(process.argv);
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import chalk from 'chalk';
export class CustomError extends Error {
get name(): string {
return this.constructor.name;
}
}
export class ExitCodeError extends CustomError {
readonly code: number;
constructor(code: number, command?: string) {
super(
command
? `Command '${command}' exited with code ${code}`
: `Child exited with code ${code}`,
);
this.code = code;
}
}
export function exitWithError(error: Error): never {
if (error instanceof ExitCodeError) {
process.stderr.write(`\n${chalk.red(error.message)}\n\n`);
process.exit(error.code);
} else {
process.stderr.write(`\n${chalk.red(`${error}`)}\n\n`);
process.exit(1);
}
}
export class NotFoundError extends CustomError {}
+1 -1
View File
@@ -58,7 +58,7 @@
"devDependencies": {
"@backstage/cli": "workspace:^",
"@types/node": "^16.11.26",
"msw": "^0.48.0"
"msw": "^0.49.0"
},
"files": [
"dist",