From 709a7352b21da89f0b0a654ea38ceb1043517b3d Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 22 Jul 2020 14:51:15 +0200 Subject: [PATCH 01/38] chore(cli): create backend plugin start --- .../default-backend-plugin/.eslintrc.js | 3 + .../default-backend-plugin/README.md.hbs | 13 +++ .../default-backend-plugin/dev/index.tsx | 20 ++++ .../default-backend-plugin/package.json.hbs | 46 ++++++++ .../ExampleComponent.test.tsx.hbs | 34 ++++++ .../ExampleComponent/ExampleComponent.tsx.hbs | 57 +++++++++ .../src/components/ExampleComponent/index.ts | 17 +++ .../ExampleFetchComponent.test.tsx.hbs | 28 +++++ .../ExampleFetchComponent.tsx.hbs | 108 ++++++++++++++++++ .../components/ExampleFetchComponent/index.ts | 17 +++ .../default-backend-plugin/src/index.ts | 17 +++ .../src/plugin.test.ts.hbs | 23 ++++ .../default-backend-plugin/src/plugin.ts.hbs | 30 +++++ .../default-backend-plugin/src/setupTests.ts | 19 +++ 14 files changed, 432 insertions(+) create mode 100644 packages/cli/templates/default-backend-plugin/.eslintrc.js create mode 100644 packages/cli/templates/default-backend-plugin/README.md.hbs create mode 100644 packages/cli/templates/default-backend-plugin/dev/index.tsx create mode 100644 packages/cli/templates/default-backend-plugin/package.json.hbs create mode 100644 packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs create mode 100644 packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs create mode 100644 packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/index.ts create mode 100644 packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs create mode 100644 packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs create mode 100644 packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/index.ts create mode 100644 packages/cli/templates/default-backend-plugin/src/index.ts create mode 100644 packages/cli/templates/default-backend-plugin/src/plugin.test.ts.hbs create mode 100644 packages/cli/templates/default-backend-plugin/src/plugin.ts.hbs create mode 100644 packages/cli/templates/default-backend-plugin/src/setupTests.ts diff --git a/packages/cli/templates/default-backend-plugin/.eslintrc.js b/packages/cli/templates/default-backend-plugin/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/packages/cli/templates/default-backend-plugin/README.md.hbs b/packages/cli/templates/default-backend-plugin/README.md.hbs new file mode 100644 index 0000000000..d6f2d65134 --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/README.md.hbs @@ -0,0 +1,13 @@ +# {{id}} + +Welcome to the {{id}} plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/{{id}}](http://localhost:3000/{{id}}). + +You can also serve the plugin in isolation by running `yarn start` in the plugin directory. +This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. +It is only meant for local development, and the setup for it can be found inside the [/dev](/dev) directory. diff --git a/packages/cli/templates/default-backend-plugin/dev/index.tsx b/packages/cli/templates/default-backend-plugin/dev/index.tsx new file mode 100644 index 0000000000..812a5585d4 --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/dev/index.tsx @@ -0,0 +1,20 @@ +/* + * 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 { createDevApp } from '@backstage/dev-utils'; +import { plugin } from '../src/plugin'; + +createDevApp().registerPlugin(plugin).render(); diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/default-backend-plugin/package.json.hbs new file mode 100644 index 0000000000..c0ba6676c5 --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/package.json.hbs @@ -0,0 +1,46 @@ +{ +"name": "@backstage/plugin-{{id}}", +"version": "{{version}}", +"main": "src/index.ts", +"types": "src/index.ts", +"license": "Apache-2.0", +"private": true, +"publishConfig": { +"access": "public", +"main": "dist/index.esm.js", +"types": "dist/index.d.ts" +}, +"scripts": { +"build": "backstage-cli backend:build", +"start": "backstage-cli plugin:serve", +"lint": "backstage-cli lint", +"test": "backstage-cli test", +"diff": "backstage-cli plugin:diff", +"prepack": "backstage-cli prepack", +"postpack": "backstage-cli postpack", +"clean": "backstage-cli clean" +}, +"dependencies": { +"@backstage/core": "^{{version}}", +"@backstage/theme": "^{{version}}", +"@material-ui/core": "^4.9.1", +"@material-ui/icons": "^4.9.1", +"@material-ui/lab": "4.0.0-alpha.45", +"react": "^16.13.1", +"react-dom": "^16.13.1", +"react-use": "^14.2.0" +}, +"devDependencies": { +"@backstage/cli": "^{{version}}", +"@backstage/dev-utils": "^{{version}}", +"@testing-library/jest-dom": "^5.10.1", +"@testing-library/react": "^10.4.1", +"@testing-library/user-event": "^12.0.7", +"@types/jest": "^25.2.2", +"@types/node": "^12.0.0", +"jest-fetch-mock": "^3.0.3" +}, +"files": [ +"dist" +] +} diff --git a/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs b/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs new file mode 100644 index 0000000000..dff57e66a4 --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs @@ -0,0 +1,34 @@ +/* + * 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 { render } from '@testing-library/react'; +import mockFetch from 'jest-fetch-mock'; +import ExampleComponent from './ExampleComponent'; +import { ThemeProvider } from '@material-ui/core'; +import { lightTheme } from '@backstage/theme'; + +describe('ExampleComponent', () => { + it('should render', () => { + mockFetch.mockResponse(() => new Promise(() => {})); + const rendered = render( + + + , + ); + expect(rendered.getByText('Welcome to {{ id }}!')).toBeInTheDocument(); + }); +}); diff --git a/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs b/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs new file mode 100644 index 0000000000..34e37f3e7b --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs @@ -0,0 +1,57 @@ +/* + * 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, { FC } from 'react'; +import { Typography, Grid } from '@material-ui/core'; +import { + InfoCard, + Header, + Page, + pageTheme, + Content, + ContentHeader, + HeaderLabel, + SupportButton, +} from '@backstage/core'; +import ExampleFetchComponent from '../ExampleFetchComponent'; + +const ExampleComponent: FC<{}> = () => ( + +
+ + +
+ + + A description of your plugin goes here. + + + + + + All content should be wrapped in a card like this. + + + + + + + + +
+); + +export default ExampleComponent; diff --git a/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/index.ts b/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/index.ts new file mode 100644 index 0000000000..e785d45082 --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export { default } from './ExampleComponent'; diff --git a/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs b/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs new file mode 100644 index 0000000000..7fecdc6f11 --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs @@ -0,0 +1,28 @@ +/* + * 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 { render } from '@testing-library/react'; +import mockFetch from 'jest-fetch-mock'; +import ExampleFetchComponent from './ExampleFetchComponent'; + +describe('ExampleFetchComponent', () => { + it('should render', async () => { + mockFetch.mockResponse(() => new Promise(() => {})); + const rendered = render(); + expect(await rendered.findByTestId('progress')).toBeInTheDocument(); + }); +}); diff --git a/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs b/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs new file mode 100644 index 0000000000..2dd54f726e --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs @@ -0,0 +1,108 @@ +/* + * 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, { FC } from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import { Table, TableColumn, Progress } from '@backstage/core'; +import Alert from '@material-ui/lab/Alert'; +import { useAsync } from 'react-use'; + +const useStyles = makeStyles({ + avatar: { + height: 32, + width: 32, + borderRadius: '50%', + }, +}); + +type User = { + gender: string; // "male" + name: { + title: string; // "Mr", + first: string; // "Duane", + last: string; // "Reed" + }; + location: object; // {street: {number: 5060, name: "Hickory Creek Dr"}, city: "Albany", state: "New South Wales",…} + email: string; // "duane.reed@example.com" + login: object; // {uuid: "4b785022-9a23-4ab9-8a23-cb3fb43969a9", username: "blackdog796", password: "patch",…} + dob: object; // {date: "1983-06-22T12:30:23.016Z", age: 37} + registered: object; // {date: "2006-06-13T18:48:28.037Z", age: 14} + phone: string; // "07-2154-5651" + cell: string; // "0405-592-879" + id: { + name: string; // "TFN", + value: string; // "796260432" + }; + picture: { medium: string }; // {medium: "https://randomuser.me/api/portraits/men/95.jpg",…} + nat: string; // "AU" +}; + +type DenseTableProps = { + users: User[]; +}; + +export const DenseTable: FC = ({ users }) => { + const classes = useStyles(); + + const columns: TableColumn[] = [ + { title: 'Avatar', field: 'avatar' }, + { title: 'Name', field: 'name' }, + { title: 'Email', field: 'email' }, + { title: 'Nationality', field: 'nationality' }, + ]; + + const data = users.map((user) => { + return { + avatar: ( + {user.name.first} + ), + name: `${user.name.first} ${user.name.last}`, + email: user.email, + nationality: user.nat, + }; + }); + + return ( + + ); +}; + +const ExampleFetchComponent: FC<{}> = () => { + const { value, loading, error } = useAsync(async (): Promise => { + const response = await fetch('https://randomuser.me/api/?results=20'); + const data = await response.json(); + return data.results; + }, []); + + if (loading) { + return ; + } else if (error) { + return {error.message}; + } + + return ; +}; + +export default ExampleFetchComponent; diff --git a/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/index.ts b/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/index.ts new file mode 100644 index 0000000000..28482f9fe1 --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export { default } from './ExampleFetchComponent'; diff --git a/packages/cli/templates/default-backend-plugin/src/index.ts b/packages/cli/templates/default-backend-plugin/src/index.ts new file mode 100644 index 0000000000..3a0a0fe2d3 --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/src/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export { plugin } from './plugin'; diff --git a/packages/cli/templates/default-backend-plugin/src/plugin.test.ts.hbs b/packages/cli/templates/default-backend-plugin/src/plugin.test.ts.hbs new file mode 100644 index 0000000000..e34204c9ae --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/src/plugin.test.ts.hbs @@ -0,0 +1,23 @@ +/* + * 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 { plugin } from './plugin'; + +describe('{{ id }}', () => { + it('should export plugin', () => { + expect(plugin).toBeDefined(); + }); +}); diff --git a/packages/cli/templates/default-backend-plugin/src/plugin.ts.hbs b/packages/cli/templates/default-backend-plugin/src/plugin.ts.hbs new file mode 100644 index 0000000000..ad5db2e711 --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/src/plugin.ts.hbs @@ -0,0 +1,30 @@ +/* + * 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 { createPlugin, createRouteRef } from '@backstage/core'; +import ExampleComponent from './components/ExampleComponent'; + +export const rootRouteRef = createRouteRef({ +path: '/{{ id }}', +title: '{{ id }}', +}); + +export const plugin = createPlugin({ +id: '{{ id }}', +register({ router }) { +router.addRoute(rootRouteRef, ExampleComponent); +}, +}); diff --git a/packages/cli/templates/default-backend-plugin/src/setupTests.ts b/packages/cli/templates/default-backend-plugin/src/setupTests.ts new file mode 100644 index 0000000000..8553642152 --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/src/setupTests.ts @@ -0,0 +1,19 @@ +/* + * 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 '@testing-library/jest-dom'; + +require('jest-fetch-mock').enableMocks(); From b468631bc2c2b99d177c8697c988e055c1650b8c Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 22 Jul 2020 15:00:46 +0200 Subject: [PATCH 02/38] chore(cli): starting to move some more boilerplate over --- .../default-backend-plugin/dev/index.tsx | 20 ----- .../default-backend-plugin/package.json.hbs | 79 ++++++++----------- .../scripts/mock-data.sh | 2 + .../templates/default-plugin/package.json.hbs | 2 +- 4 files changed, 38 insertions(+), 65 deletions(-) delete mode 100644 packages/cli/templates/default-backend-plugin/dev/index.tsx create mode 100755 packages/cli/templates/default-backend-plugin/scripts/mock-data.sh diff --git a/packages/cli/templates/default-backend-plugin/dev/index.tsx b/packages/cli/templates/default-backend-plugin/dev/index.tsx deleted file mode 100644 index 812a5585d4..0000000000 --- a/packages/cli/templates/default-backend-plugin/dev/index.tsx +++ /dev/null @@ -1,20 +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 { createDevApp } from '@backstage/dev-utils'; -import { plugin } from '../src/plugin'; - -createDevApp().registerPlugin(plugin).render(); diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/default-backend-plugin/package.json.hbs index c0ba6676c5..51be95a3ba 100644 --- a/packages/cli/templates/default-backend-plugin/package.json.hbs +++ b/packages/cli/templates/default-backend-plugin/package.json.hbs @@ -1,46 +1,37 @@ { -"name": "@backstage/plugin-{{id}}", -"version": "{{version}}", -"main": "src/index.ts", -"types": "src/index.ts", -"license": "Apache-2.0", -"private": true, -"publishConfig": { -"access": "public", -"main": "dist/index.esm.js", -"types": "dist/index.d.ts" -}, -"scripts": { -"build": "backstage-cli backend:build", -"start": "backstage-cli plugin:serve", -"lint": "backstage-cli lint", -"test": "backstage-cli test", -"diff": "backstage-cli plugin:diff", -"prepack": "backstage-cli prepack", -"postpack": "backstage-cli postpack", -"clean": "backstage-cli clean" -}, -"dependencies": { -"@backstage/core": "^{{version}}", -"@backstage/theme": "^{{version}}", -"@material-ui/core": "^4.9.1", -"@material-ui/icons": "^4.9.1", -"@material-ui/lab": "4.0.0-alpha.45", -"react": "^16.13.1", -"react-dom": "^16.13.1", -"react-use": "^14.2.0" -}, -"devDependencies": { -"@backstage/cli": "^{{version}}", -"@backstage/dev-utils": "^{{version}}", -"@testing-library/jest-dom": "^5.10.1", -"@testing-library/react": "^10.4.1", -"@testing-library/user-event": "^12.0.7", -"@types/jest": "^25.2.2", -"@types/node": "^12.0.0", -"jest-fetch-mock": "^3.0.3" -}, -"files": [ -"dist" -] + "name": "@backstage/plugin-{{id}}-backend", + "version": "{{version}}", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": true, + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "start": "backstage-cli backend:dev", + "build": "backstage-cli backend:build", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean", + "mock-data": "./scripts/mock-data.sh" + }, + "dependencies": { + "@backstage/backend-common": "^{{version}}", + "@types/express": "^4.17.6", + "express": "^4.17.1", + "express-promise-router": "^3.0.3" + }, + "devDependencies": { + "@backstage/cli": "^{{version}}", + "@types/supertest": "^2.0.8", + "supertest": "^4.0.2" + }, + "files": [ + "dist" + ] } diff --git a/packages/cli/templates/default-backend-plugin/scripts/mock-data.sh b/packages/cli/templates/default-backend-plugin/scripts/mock-data.sh new file mode 100755 index 0000000000..ff30921715 --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/scripts/mock-data.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +echo "use this script to load your service with some mock data if needed!" diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index da611a13b7..b457bc41fe 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -38,7 +38,7 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^25.2.2", "@types/node": "^12.0.0", - "jest-fetch-mock": "^3.0.3" + "msw": "^0.19.5" }, "files": [ "dist" From 1516a0872150b497ab21d66a89cd28008681467f Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 22 Jul 2020 15:32:53 +0200 Subject: [PATCH 03/38] chore(cli): removing some other stuff to make way for more things --- .../ExampleComponent.test.tsx.hbs | 34 ------ .../ExampleComponent/ExampleComponent.tsx.hbs | 57 --------- .../src/components/ExampleComponent/index.ts | 17 --- .../ExampleFetchComponent.test.tsx.hbs | 28 ----- .../ExampleFetchComponent.tsx.hbs | 108 ------------------ .../components/ExampleFetchComponent/index.ts | 17 --- .../default-backend-plugin/src/index.ts | 17 --- .../src/plugin.test.ts.hbs | 23 ---- .../default-backend-plugin/src/plugin.ts.hbs | 30 ----- 9 files changed, 331 deletions(-) delete mode 100644 packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs delete mode 100644 packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs delete mode 100644 packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/index.ts delete mode 100644 packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs delete mode 100644 packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs delete mode 100644 packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/index.ts delete mode 100644 packages/cli/templates/default-backend-plugin/src/index.ts delete mode 100644 packages/cli/templates/default-backend-plugin/src/plugin.test.ts.hbs delete mode 100644 packages/cli/templates/default-backend-plugin/src/plugin.ts.hbs diff --git a/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs b/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs deleted file mode 100644 index dff57e66a4..0000000000 --- a/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs +++ /dev/null @@ -1,34 +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 from 'react'; -import { render } from '@testing-library/react'; -import mockFetch from 'jest-fetch-mock'; -import ExampleComponent from './ExampleComponent'; -import { ThemeProvider } from '@material-ui/core'; -import { lightTheme } from '@backstage/theme'; - -describe('ExampleComponent', () => { - it('should render', () => { - mockFetch.mockResponse(() => new Promise(() => {})); - const rendered = render( - - - , - ); - expect(rendered.getByText('Welcome to {{ id }}!')).toBeInTheDocument(); - }); -}); diff --git a/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs b/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs deleted file mode 100644 index 34e37f3e7b..0000000000 --- a/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs +++ /dev/null @@ -1,57 +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, { FC } from 'react'; -import { Typography, Grid } from '@material-ui/core'; -import { - InfoCard, - Header, - Page, - pageTheme, - Content, - ContentHeader, - HeaderLabel, - SupportButton, -} from '@backstage/core'; -import ExampleFetchComponent from '../ExampleFetchComponent'; - -const ExampleComponent: FC<{}> = () => ( - -
- - -
- - - A description of your plugin goes here. - - - - - - All content should be wrapped in a card like this. - - - - - - - - -
-); - -export default ExampleComponent; diff --git a/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/index.ts b/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/index.ts deleted file mode 100644 index e785d45082..0000000000 --- a/packages/cli/templates/default-backend-plugin/src/components/ExampleComponent/index.ts +++ /dev/null @@ -1,17 +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. - */ - -export { default } from './ExampleComponent'; diff --git a/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs b/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs deleted file mode 100644 index 7fecdc6f11..0000000000 --- a/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs +++ /dev/null @@ -1,28 +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 from 'react'; -import { render } from '@testing-library/react'; -import mockFetch from 'jest-fetch-mock'; -import ExampleFetchComponent from './ExampleFetchComponent'; - -describe('ExampleFetchComponent', () => { - it('should render', async () => { - mockFetch.mockResponse(() => new Promise(() => {})); - const rendered = render(); - expect(await rendered.findByTestId('progress')).toBeInTheDocument(); - }); -}); diff --git a/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs b/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs deleted file mode 100644 index 2dd54f726e..0000000000 --- a/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs +++ /dev/null @@ -1,108 +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, { FC } from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import { Table, TableColumn, Progress } from '@backstage/core'; -import Alert from '@material-ui/lab/Alert'; -import { useAsync } from 'react-use'; - -const useStyles = makeStyles({ - avatar: { - height: 32, - width: 32, - borderRadius: '50%', - }, -}); - -type User = { - gender: string; // "male" - name: { - title: string; // "Mr", - first: string; // "Duane", - last: string; // "Reed" - }; - location: object; // {street: {number: 5060, name: "Hickory Creek Dr"}, city: "Albany", state: "New South Wales",…} - email: string; // "duane.reed@example.com" - login: object; // {uuid: "4b785022-9a23-4ab9-8a23-cb3fb43969a9", username: "blackdog796", password: "patch",…} - dob: object; // {date: "1983-06-22T12:30:23.016Z", age: 37} - registered: object; // {date: "2006-06-13T18:48:28.037Z", age: 14} - phone: string; // "07-2154-5651" - cell: string; // "0405-592-879" - id: { - name: string; // "TFN", - value: string; // "796260432" - }; - picture: { medium: string }; // {medium: "https://randomuser.me/api/portraits/men/95.jpg",…} - nat: string; // "AU" -}; - -type DenseTableProps = { - users: User[]; -}; - -export const DenseTable: FC = ({ users }) => { - const classes = useStyles(); - - const columns: TableColumn[] = [ - { title: 'Avatar', field: 'avatar' }, - { title: 'Name', field: 'name' }, - { title: 'Email', field: 'email' }, - { title: 'Nationality', field: 'nationality' }, - ]; - - const data = users.map((user) => { - return { - avatar: ( - {user.name.first} - ), - name: `${user.name.first} ${user.name.last}`, - email: user.email, - nationality: user.nat, - }; - }); - - return ( -
- ); -}; - -const ExampleFetchComponent: FC<{}> = () => { - const { value, loading, error } = useAsync(async (): Promise => { - const response = await fetch('https://randomuser.me/api/?results=20'); - const data = await response.json(); - return data.results; - }, []); - - if (loading) { - return ; - } else if (error) { - return {error.message}; - } - - return ; -}; - -export default ExampleFetchComponent; diff --git a/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/index.ts b/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/index.ts deleted file mode 100644 index 28482f9fe1..0000000000 --- a/packages/cli/templates/default-backend-plugin/src/components/ExampleFetchComponent/index.ts +++ /dev/null @@ -1,17 +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. - */ - -export { default } from './ExampleFetchComponent'; diff --git a/packages/cli/templates/default-backend-plugin/src/index.ts b/packages/cli/templates/default-backend-plugin/src/index.ts deleted file mode 100644 index 3a0a0fe2d3..0000000000 --- a/packages/cli/templates/default-backend-plugin/src/index.ts +++ /dev/null @@ -1,17 +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. - */ - -export { plugin } from './plugin'; diff --git a/packages/cli/templates/default-backend-plugin/src/plugin.test.ts.hbs b/packages/cli/templates/default-backend-plugin/src/plugin.test.ts.hbs deleted file mode 100644 index e34204c9ae..0000000000 --- a/packages/cli/templates/default-backend-plugin/src/plugin.test.ts.hbs +++ /dev/null @@ -1,23 +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 { plugin } from './plugin'; - -describe('{{ id }}', () => { - it('should export plugin', () => { - expect(plugin).toBeDefined(); - }); -}); diff --git a/packages/cli/templates/default-backend-plugin/src/plugin.ts.hbs b/packages/cli/templates/default-backend-plugin/src/plugin.ts.hbs deleted file mode 100644 index ad5db2e711..0000000000 --- a/packages/cli/templates/default-backend-plugin/src/plugin.ts.hbs +++ /dev/null @@ -1,30 +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 { createPlugin, createRouteRef } from '@backstage/core'; -import ExampleComponent from './components/ExampleComponent'; - -export const rootRouteRef = createRouteRef({ -path: '/{{ id }}', -title: '{{ id }}', -}); - -export const plugin = createPlugin({ -id: '{{ id }}', -register({ router }) { -router.addRoute(rootRouteRef, ExampleComponent); -}, -}); From 444b8f768c2a3dc80b0548bf12924cff6052168d Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 23 Jul 2020 10:41:46 +0200 Subject: [PATCH 04/38] chore(cli): finish the template for the backend plugin --- .../default-backend-plugin/README.md.hbs | 5 +- .../default-backend-plugin/package.json.hbs | 7 ++- .../default-backend-plugin/src/index.ts | 17 +++++++ .../default-backend-plugin/src/run.ts.hbs | 33 +++++++++++++ .../src/service/router.test.ts | 45 ++++++++++++++++++ .../src/service/router.ts | 40 ++++++++++++++++ .../src/service/standaloneServer.ts.hbs | 47 +++++++++++++++++++ 7 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 packages/cli/templates/default-backend-plugin/src/index.ts create mode 100644 packages/cli/templates/default-backend-plugin/src/run.ts.hbs create mode 100644 packages/cli/templates/default-backend-plugin/src/service/router.test.ts create mode 100644 packages/cli/templates/default-backend-plugin/src/service/router.ts create mode 100644 packages/cli/templates/default-backend-plugin/src/service/standaloneServer.ts.hbs diff --git a/packages/cli/templates/default-backend-plugin/README.md.hbs b/packages/cli/templates/default-backend-plugin/README.md.hbs index d6f2d65134..5c34b40360 100644 --- a/packages/cli/templates/default-backend-plugin/README.md.hbs +++ b/packages/cli/templates/default-backend-plugin/README.md.hbs @@ -1,12 +1,13 @@ # {{id}} -Welcome to the {{id}} plugin! +Welcome to the {{id}} backend plugin! _This plugin was created through the Backstage CLI_ ## Getting started -Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/{{id}}](http://localhost:3000/{{id}}). +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn +start` in the root directory, and then navigating to [/{{id}}](http://localhost:3000/{{id}}). You can also serve the plugin in isolation by running `yarn start` in the plugin directory. This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/default-backend-plugin/package.json.hbs index 51be95a3ba..4d347107de 100644 --- a/packages/cli/templates/default-backend-plugin/package.json.hbs +++ b/packages/cli/templates/default-backend-plugin/package.json.hbs @@ -24,12 +24,15 @@ "@backstage/backend-common": "^{{version}}", "@types/express": "^4.17.6", "express": "^4.17.1", - "express-promise-router": "^3.0.3" + "express-promise-router": "^3.0.3", + "yn": "^4.0.0", + "whatwg-fetch": "^3.2.0" }, "devDependencies": { "@backstage/cli": "^{{version}}", "@types/supertest": "^2.0.8", - "supertest": "^4.0.2" + "supertest": "^4.0.2", + "msw": "^0.19.5" }, "files": [ "dist" diff --git a/packages/cli/templates/default-backend-plugin/src/index.ts b/packages/cli/templates/default-backend-plugin/src/index.ts new file mode 100644 index 0000000000..7612c392a2 --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/src/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export * from './service/router'; diff --git a/packages/cli/templates/default-backend-plugin/src/run.ts.hbs b/packages/cli/templates/default-backend-plugin/src/run.ts.hbs new file mode 100644 index 0000000000..b96989e4b8 --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/src/run.ts.hbs @@ -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 { getRootLogger } from '@backstage/backend-common'; +import yn from 'yn'; +import { startStandaloneServer } from './service/standaloneServer'; + +const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7000; +const enableCors = yn(process.env.PLUGIN_CORS, { default: false }); +const logger = getRootLogger(); + +startStandaloneServer({ port, enableCors, logger }).catch(err => { + logger.error(err); + process.exit(1); +}); + +process.on('SIGINT', () => { + logger.info('CTRL+C pressed; exiting.'); + process.exit(0); +}); diff --git a/packages/cli/templates/default-backend-plugin/src/service/router.test.ts b/packages/cli/templates/default-backend-plugin/src/service/router.test.ts new file mode 100644 index 0000000000..fba6b0ccdf --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/src/service/router.test.ts @@ -0,0 +1,45 @@ +/* + * 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 { getVoidLogger } from '@backstage/backend-common'; +import express from 'express'; +import request from 'supertest'; + +import { createRouter } from './router'; + +describe('createRouter', () => { + let app: express.Express; + + beforeAll(async () => { + const router = await createRouter({ + logger: getVoidLogger(), + }); + app = express().use(router); + }); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + describe('GET /ping', () => { + it('returns ok', async () => { + const response = await request(app).get('/ping'); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ status: 'ok' }); + }); + }); +}); diff --git a/packages/cli/templates/default-backend-plugin/src/service/router.ts b/packages/cli/templates/default-backend-plugin/src/service/router.ts new file mode 100644 index 0000000000..3ea8219365 --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/src/service/router.ts @@ -0,0 +1,40 @@ +/* + * 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 { errorHandler } from '@backstage/backend-common'; +import express from 'express'; +import Router from 'express-promise-router'; +import { Logger } from 'winston'; + +export interface RouterOptions { + logger: Logger; +} + +export async function createRouter( + options: RouterOptions, +): Promise { + const { logger } = options; + + const router = Router(); + router.use(express.json()); + + router.get('/health', (_, response) => { + logger.info('PONG!'); + response.send({ status: 'ok' }); + }); + router.use(errorHandler()); + return router; +} diff --git a/packages/cli/templates/default-backend-plugin/src/service/standaloneServer.ts.hbs b/packages/cli/templates/default-backend-plugin/src/service/standaloneServer.ts.hbs new file mode 100644 index 0000000000..6e38965246 --- /dev/null +++ b/packages/cli/templates/default-backend-plugin/src/service/standaloneServer.ts.hbs @@ -0,0 +1,47 @@ +/* + * 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 { createServiceBuilder } from '@backstage/backend-common'; +import { Server } from 'http'; +import { Logger } from 'winston'; +import { createRouter } from './router'; + +export interface ServerOptions { + port: number; + enableCors: boolean; + logger: Logger; +} + +export async function startStandaloneServer( + options: ServerOptions, +): Promise { + const logger = options.logger.child({ service: '{{id}}-backend' }); + logger.debug('Starting application server...'); + const router = await createRouter({ + logger, + }); + + const service = createServiceBuilder(module) + .enableCors({ origin: 'http://localhost:3000' }) + .addRouter('/{{id}}', router); + + return await service.start().catch(err => { + logger.error(err); + process.exit(1); + }); +} + +module.hot?.accept(); From dd9cc6a666df8ef972e43dfe02ce8a60d06e091c Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 23 Jul 2020 10:42:55 +0200 Subject: [PATCH 05/38] chore(cli): polyfill whatwg-fetch in tests --- .../cli/templates/default-backend-plugin/src/setupTests.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/cli/templates/default-backend-plugin/src/setupTests.ts b/packages/cli/templates/default-backend-plugin/src/setupTests.ts index 8553642152..aa5bc44d68 100644 --- a/packages/cli/templates/default-backend-plugin/src/setupTests.ts +++ b/packages/cli/templates/default-backend-plugin/src/setupTests.ts @@ -13,7 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import '@testing-library/jest-dom'; - -require('jest-fetch-mock').enableMocks(); +require('whatwg-fetch'); From ffa1b38a7380b3c3aefe56b420971963eb74ac4a Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 23 Jul 2020 10:43:39 +0200 Subject: [PATCH 06/38] chore(cli): polyfill fetch in the server --- packages/cli/templates/default-backend-plugin/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/templates/default-backend-plugin/src/index.ts b/packages/cli/templates/default-backend-plugin/src/index.ts index 7612c392a2..7332f92d68 100644 --- a/packages/cli/templates/default-backend-plugin/src/index.ts +++ b/packages/cli/templates/default-backend-plugin/src/index.ts @@ -14,4 +14,5 @@ * limitations under the License. */ +require('whatwg-fetch'); export * from './service/router'; From 93306ace29cfb30b2c3db76581ff649bb0ae8bb8 Mon Sep 17 00:00:00 2001 From: Ben Lambert Date: Thu, 23 Jul 2020 11:53:50 +0200 Subject: [PATCH 07/38] chore(cli): update eslint rules --- packages/cli/templates/default-backend-plugin/.eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/templates/default-backend-plugin/.eslintrc.js b/packages/cli/templates/default-backend-plugin/.eslintrc.js index 13573efa9c..16a033dbc6 100644 --- a/packages/cli/templates/default-backend-plugin/.eslintrc.js +++ b/packages/cli/templates/default-backend-plugin/.eslintrc.js @@ -1,3 +1,3 @@ module.exports = { - extends: [require.resolve('@backstage/cli/config/eslint')], + extends: [require.resolve('@backstage/cli/config/eslint.backend')], }; From 407f62e40ac26824e37fdd54c4cfcb2c7b3efea4 Mon Sep 17 00:00:00 2001 From: Ben Lambert Date: Thu, 23 Jul 2020 11:54:25 +0200 Subject: [PATCH 08/38] chore(cli): need to make a module --- packages/cli/templates/default-backend-plugin/src/setupTests.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/cli/templates/default-backend-plugin/src/setupTests.ts b/packages/cli/templates/default-backend-plugin/src/setupTests.ts index aa5bc44d68..ac41ef995a 100644 --- a/packages/cli/templates/default-backend-plugin/src/setupTests.ts +++ b/packages/cli/templates/default-backend-plugin/src/setupTests.ts @@ -14,3 +14,5 @@ * limitations under the License. */ require('whatwg-fetch'); + +export {}; From 168f1df9359fc9a1cb960fb9a7de2248bc0cef0e Mon Sep 17 00:00:00 2001 From: Ben Lambert Date: Thu, 23 Jul 2020 11:56:35 +0200 Subject: [PATCH 09/38] chore(cli): fixing dependenceis:Wq --- .../cli/templates/default-backend-plugin/package.json.hbs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/default-backend-plugin/package.json.hbs index 4d347107de..a2328223db 100644 --- a/packages/cli/templates/default-backend-plugin/package.json.hbs +++ b/packages/cli/templates/default-backend-plugin/package.json.hbs @@ -25,8 +25,9 @@ "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^3.0.3", - "yn": "^4.0.0", - "whatwg-fetch": "^3.2.0" + "whatwg-fetch": "^3.2.0", + "winston": "^3.3.3", + "yn": "^4.0.0" }, "devDependencies": { "@backstage/cli": "^{{version}}", From f6296c078f6eace6ccbf30fbbb45e4a4781ead67 Mon Sep 17 00:00:00 2001 From: Ben Lambert Date: Thu, 23 Jul 2020 12:00:48 +0200 Subject: [PATCH 10/38] chore(cli): fixing winston dependency --- packages/cli/templates/default-backend-plugin/package.json.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/default-backend-plugin/package.json.hbs index a2328223db..92f0a9e10f 100644 --- a/packages/cli/templates/default-backend-plugin/package.json.hbs +++ b/packages/cli/templates/default-backend-plugin/package.json.hbs @@ -26,7 +26,7 @@ "express": "^4.17.1", "express-promise-router": "^3.0.3", "whatwg-fetch": "^3.2.0", - "winston": "^3.3.3", + "winston": "^3.2.1", "yn": "^4.0.0" }, "devDependencies": { From 7197ceba043ca96e23c22f8cdb0bd690f679053a Mon Sep 17 00:00:00 2001 From: Ben Lambert Date: Thu, 23 Jul 2020 12:02:33 +0200 Subject: [PATCH 11/38] chore(cli): updating endpoint for test --- .../default-backend-plugin/src/service/router.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/templates/default-backend-plugin/src/service/router.test.ts b/packages/cli/templates/default-backend-plugin/src/service/router.test.ts index fba6b0ccdf..0aaeafa379 100644 --- a/packages/cli/templates/default-backend-plugin/src/service/router.test.ts +++ b/packages/cli/templates/default-backend-plugin/src/service/router.test.ts @@ -34,9 +34,9 @@ describe('createRouter', () => { jest.resetAllMocks(); }); - describe('GET /ping', () => { + describe('GET /health', () => { it('returns ok', async () => { - const response = await request(app).get('/ping'); + const response = await request(app).get('/health'); expect(response.status).toEqual(200); expect(response.body).toEqual({ status: 'ok' }); From b958118d7497b1e9990ed2b9c471eb4310cb4e18 Mon Sep 17 00:00:00 2001 From: Ben Lambert Date: Thu, 23 Jul 2020 20:22:50 +0200 Subject: [PATCH 12/38] chore(cli): updating issues with wrong package version --- packages/cli/templates/default-backend-plugin/package.json.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/default-backend-plugin/package.json.hbs index 92f0a9e10f..1cf9ab1c5b 100644 --- a/packages/cli/templates/default-backend-plugin/package.json.hbs +++ b/packages/cli/templates/default-backend-plugin/package.json.hbs @@ -25,7 +25,7 @@ "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^3.0.3", - "whatwg-fetch": "^3.2.0", + "whatwg-fetch": "^2.0.0", "winston": "^3.2.1", "yn": "^4.0.0" }, From 0d07847859f1c6e664578ede645679b19a862be2 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 21 Sep 2020 00:46:02 +0200 Subject: [PATCH 13/38] feat(cli): adding the backend flag to the cli --- packages/cli/src/commands/create-plugin/createPlugin.ts | 9 ++++++--- packages/cli/src/commands/index.ts | 4 ++++ .../templates/default-backend-plugin/package.json.hbs | 6 +++--- .../default-backend-plugin/scripts/mock-data.sh | 2 -- .../cli/templates/default-backend-plugin/src/index.ts | 1 - .../templates/default-backend-plugin/src/setupTests.ts | 1 - 6 files changed, 13 insertions(+), 10 deletions(-) delete mode 100755 packages/cli/templates/default-backend-plugin/scripts/mock-data.sh diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 5e6882a4a0..8f75728544 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -175,7 +175,7 @@ export async function movePlugin( }); } -export default async () => { +export default async ({ backend }: { backend: boolean }) => { const codeownersPath = await getCodeownersFilePath(paths.targetRoot); const questions: Question[] = [ @@ -223,7 +223,9 @@ export default async () => { const answers: Answers = await inquirer.prompt(questions); const appPackage = paths.resolveTargetRoot('packages/app'); - const templateDir = paths.resolveOwn('templates/default-plugin'); + const templateDir = paths.resolveOwn( + backend ? 'templates/default-backend-plugin' : 'templates/default-plugin', + ); const tempDir = resolvePath(os.tmpdir(), answers.id); const pluginDir = paths.resolveTargetRoot('plugins', answers.id); const ownerIds = parseOwnerIds(answers.owner); @@ -240,6 +242,7 @@ export default async () => { await createTemporaryPluginFolder(tempDir); Task.section('Preparing files'); + await templatingTask(templateDir, tempDir, { ...answers, version, @@ -252,7 +255,7 @@ export default async () => { Task.section('Building the plugin'); await buildPlugin(pluginDir); - if (await fs.pathExists(appPackage)) { + if ((await fs.pathExists(appPackage)) && !backend) { Task.section('Adding plugin as dependency in app'); await addPluginDependencyToApp(paths.targetRoot, answers.id, version); diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 4e031cdbc1..a0b898db75 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -61,6 +61,10 @@ export function registerCommands(program: CommanderStatic) { program .command('create-plugin') + .option( + '--backend', + 'Create plugin with the backend dependencies as default', + ) .description('Creates a new plugin in the current repository') .action( lazy(() => import('./create-plugin/createPlugin').then(m => m.default)), diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/default-backend-plugin/package.json.hbs index 1cf9ab1c5b..8a1d5a9522 100644 --- a/packages/cli/templates/default-backend-plugin/package.json.hbs +++ b/packages/cli/templates/default-backend-plugin/package.json.hbs @@ -17,16 +17,16 @@ "test": "backstage-cli test", "prepack": "backstage-cli prepack", "postpack": "backstage-cli postpack", - "clean": "backstage-cli clean", - "mock-data": "./scripts/mock-data.sh" + "clean": "backstage-cli clean" }, "dependencies": { "@backstage/backend-common": "^{{version}}", + "@backstage/config": "^{{version}}", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^3.0.3", - "whatwg-fetch": "^2.0.0", "winston": "^3.2.1", + "node-fetch": "^2.6.0", "yn": "^4.0.0" }, "devDependencies": { diff --git a/packages/cli/templates/default-backend-plugin/scripts/mock-data.sh b/packages/cli/templates/default-backend-plugin/scripts/mock-data.sh deleted file mode 100755 index ff30921715..0000000000 --- a/packages/cli/templates/default-backend-plugin/scripts/mock-data.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -echo "use this script to load your service with some mock data if needed!" diff --git a/packages/cli/templates/default-backend-plugin/src/index.ts b/packages/cli/templates/default-backend-plugin/src/index.ts index 7332f92d68..7612c392a2 100644 --- a/packages/cli/templates/default-backend-plugin/src/index.ts +++ b/packages/cli/templates/default-backend-plugin/src/index.ts @@ -14,5 +14,4 @@ * limitations under the License. */ -require('whatwg-fetch'); export * from './service/router'; diff --git a/packages/cli/templates/default-backend-plugin/src/setupTests.ts b/packages/cli/templates/default-backend-plugin/src/setupTests.ts index ac41ef995a..ba33cf996b 100644 --- a/packages/cli/templates/default-backend-plugin/src/setupTests.ts +++ b/packages/cli/templates/default-backend-plugin/src/setupTests.ts @@ -13,6 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -require('whatwg-fetch'); export {}; From 8953211c5bc77711058654dd7395f613a14dad64 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 21 Sep 2020 01:08:04 +0200 Subject: [PATCH 14/38] chore(cli): fixed the failing tests --- packages/app/package.json | 1 + packages/app/src/plugins.ts | 1 + packages/cli/src/commands/create-plugin/createPlugin.ts | 1 + .../ExampleComponent/ExampleComponent.test.tsx.hbs | 9 ++++----- .../ExampleFetchComponent.test.tsx.hbs | 2 -- packages/cli/templates/default-plugin/src/setupTests.ts | 2 -- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index cb61e1ed8c..84a8dd361c 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -8,6 +8,7 @@ "@backstage/cli": "^0.1.1-alpha.22", "@backstage/core": "^0.1.1-alpha.22", "@backstage/plugin-api-docs": "^0.1.1-alpha.22", + "@backstage/plugin-asd": "^0.1.1-alpha.22", "@backstage/plugin-catalog": "^0.1.1-alpha.22", "@backstage/plugin-circleci": "^0.1.1-alpha.22", "@backstage/plugin-explore": "^0.1.1-alpha.22", diff --git a/packages/app/src/plugins.ts b/packages/app/src/plugins.ts index b36d0323e0..f7a3834bfc 100644 --- a/packages/app/src/plugins.ts +++ b/packages/app/src/plugins.ts @@ -33,3 +33,4 @@ export { plugin as Jenkins } from '@backstage/plugin-jenkins'; export { plugin as ApiDocs } from '@backstage/plugin-api-docs'; export { plugin as GithubPullRequests } from '@roadiehq/backstage-plugin-github-pull-requests'; export { plugin as GcpProjects } from '@backstage/plugin-gcp-projects'; +export { plugin as Asd } from '@backstage/plugin-asd'; diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 8f75728544..b42edc99ec 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -176,6 +176,7 @@ export async function movePlugin( } export default async ({ backend }: { backend: boolean }) => { + console.warn(backend); const codeownersPath = await getCodeownersFilePath(paths.targetRoot); const questions: Question[] = [ diff --git a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs index 37759313ee..4d52fa82b5 100644 --- a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs +++ b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs @@ -1,18 +1,17 @@ import React from 'react'; import { render } from '@testing-library/react'; -import mockFetch from 'jest-fetch-mock'; import ExampleComponent from './ExampleComponent'; import { ThemeProvider } from '@material-ui/core'; import { lightTheme } from '@backstage/theme'; describe('ExampleComponent', () => { it('should render', () => { - mockFetch.mockResponse(() => new Promise(() => {})); const rendered = render( - + , - ); - expect(rendered.getByText('Welcome to {{ id }}!')).toBeInTheDocument(); + ); + expect(rendered.getByText('Welcome to {{ id }}!')).toBeInTheDocument(); }); }); + \ No newline at end of file diff --git a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs index cc61c215cd..aad4fac10a 100644 --- a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs +++ b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs @@ -1,11 +1,9 @@ import React from 'react'; import { render } from '@testing-library/react'; -import mockFetch from 'jest-fetch-mock'; import ExampleFetchComponent from './ExampleFetchComponent'; describe('ExampleFetchComponent', () => { it('should render', async () => { - mockFetch.mockResponse(() => new Promise(() => {})); const rendered = render(); expect(await rendered.findByTestId('progress')).toBeInTheDocument(); }); diff --git a/packages/cli/templates/default-plugin/src/setupTests.ts b/packages/cli/templates/default-plugin/src/setupTests.ts index 3fa8703ac4..7b0828bfa8 100644 --- a/packages/cli/templates/default-plugin/src/setupTests.ts +++ b/packages/cli/templates/default-plugin/src/setupTests.ts @@ -1,3 +1 @@ import '@testing-library/jest-dom'; - -require('jest-fetch-mock').enableMocks(); From 9f8f07d483e54b5c2d1c85775ce795bcbf654f3d Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 21 Sep 2020 01:13:57 +0200 Subject: [PATCH 15/38] chore(cli): fixing issues --- packages/app/package.json | 1 - packages/app/src/plugins.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index 84a8dd361c..cb61e1ed8c 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -8,7 +8,6 @@ "@backstage/cli": "^0.1.1-alpha.22", "@backstage/core": "^0.1.1-alpha.22", "@backstage/plugin-api-docs": "^0.1.1-alpha.22", - "@backstage/plugin-asd": "^0.1.1-alpha.22", "@backstage/plugin-catalog": "^0.1.1-alpha.22", "@backstage/plugin-circleci": "^0.1.1-alpha.22", "@backstage/plugin-explore": "^0.1.1-alpha.22", diff --git a/packages/app/src/plugins.ts b/packages/app/src/plugins.ts index f7a3834bfc..b36d0323e0 100644 --- a/packages/app/src/plugins.ts +++ b/packages/app/src/plugins.ts @@ -33,4 +33,3 @@ export { plugin as Jenkins } from '@backstage/plugin-jenkins'; export { plugin as ApiDocs } from '@backstage/plugin-api-docs'; export { plugin as GithubPullRequests } from '@roadiehq/backstage-plugin-github-pull-requests'; export { plugin as GcpProjects } from '@backstage/plugin-gcp-projects'; -export { plugin as Asd } from '@backstage/plugin-asd'; From cd0f2bd3db9c389b4b1eea2b2b52e4892de3b5cf Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 21 Sep 2020 15:48:55 +0200 Subject: [PATCH 16/38] chore(cli/backend): fixing some drit --- packages/cli/src/commands/create-plugin/createPlugin.ts | 1 - packages/cli/templates/default-backend-plugin/package.json.hbs | 2 +- .../components/ExampleComponent/ExampleComponent.test.tsx.hbs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index b42edc99ec..8f75728544 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -176,7 +176,6 @@ export async function movePlugin( } export default async ({ backend }: { backend: boolean }) => { - console.warn(backend); const codeownersPath = await getCodeownersFilePath(paths.targetRoot); const questions: Question[] = [ diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/default-backend-plugin/package.json.hbs index 8a1d5a9522..fd2c57f87d 100644 --- a/packages/cli/templates/default-backend-plugin/package.json.hbs +++ b/packages/cli/templates/default-backend-plugin/package.json.hbs @@ -7,7 +7,7 @@ "private": true, "publishConfig": { "access": "public", - "main": "dist/index.esm.js", + "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, "scripts": { diff --git a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs index 4d52fa82b5..f49457490f 100644 --- a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs +++ b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs @@ -8,7 +8,7 @@ describe('ExampleComponent', () => { it('should render', () => { const rendered = render( - + , ); expect(rendered.getByText('Welcome to {{ id }}!')).toBeInTheDocument(); From 6f70008386fb128a90b88c756995a1dc402520f3 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 21 Sep 2020 23:37:35 +0200 Subject: [PATCH 17/38] chore(cli): fixing issues with the new dependency --- packages/cli/src/commands/create-plugin/createPlugin.ts | 2 +- packages/cli/templates/default-backend-plugin/package.json.hbs | 2 +- packages/cli/templates/default-plugin/package.json.hbs | 2 +- plugins/api-docs/package.json | 3 ++- plugins/app-backend/package.json | 2 +- plugins/auth-backend/package.json | 3 ++- plugins/catalog-graphql/package.json | 2 +- plugins/circleci/package.json | 3 ++- plugins/explore/package.json | 3 ++- plugins/gcp-projects/package.json | 3 ++- plugins/github-actions/package.json | 3 ++- plugins/gitops-profiles/package.json | 3 ++- plugins/graphiql/package.json | 3 ++- plugins/identity-backend/package.json | 3 ++- plugins/jenkins/package.json | 3 ++- plugins/lighthouse/package.json | 3 ++- plugins/newrelic/package.json | 3 ++- plugins/register-component/package.json | 3 ++- plugins/rollbar/package.json | 3 ++- plugins/scaffolder/package.json | 3 ++- plugins/sentry/package.json | 3 ++- plugins/tech-radar/package.json | 3 ++- plugins/techdocs/package.json | 3 ++- plugins/welcome/package.json | 3 ++- 24 files changed, 43 insertions(+), 24 deletions(-) diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts index 37c026ecae..cbaf956bdb 100644 --- a/packages/cli/src/commands/create-plugin/createPlugin.ts +++ b/packages/cli/src/commands/create-plugin/createPlugin.ts @@ -272,7 +272,7 @@ export default async (cmd: Command) => { Task.section('Building the plugin'); await buildPlugin(pluginDir); - if ((await fs.pathExists(appPackage)) && !backend) { + if ((await fs.pathExists(appPackage)) && !cmd.backend) { Task.section('Adding plugin as dependency in app'); await addPluginDependencyToApp(paths.targetRoot, name, version); diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/default-backend-plugin/package.json.hbs index 2ecdbcef01..a4b6e7aaf9 100644 --- a/packages/cli/templates/default-backend-plugin/package.json.hbs +++ b/packages/cli/templates/default-backend-plugin/package.json.hbs @@ -36,7 +36,7 @@ "@backstage/cli": "^{{version}}", "@types/supertest": "^2.0.8", "supertest": "^4.0.2", - "msw": "^0.19.5" + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index 7597d3dc28..0d26339843 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -41,7 +41,7 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", - "msw": "^0.19.5" + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 378ceb2d08..254dc9aba0 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -49,7 +49,8 @@ "@types/node": "^12.0.0", "@types/react": "^16.9", "@types/swagger-ui-react": "^3.23.3", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index c4738c7105..2854425e75 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -32,7 +32,7 @@ "devDependencies": { "@backstage/cli": "^0.1.1-alpha.22", "@types/supertest": "^2.0.8", - "msw": "^0.19.5", + "msw": "^0.20.5", "supertest": "^4.0.2" }, "files": [ diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 91fe08c0fa..aff6470e71 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -58,7 +58,8 @@ "@types/passport-google-oauth20": "^2.0.3", "@types/passport-microsoft": "^0.0.0", "@types/passport-saml": "^1.1.2", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist", diff --git a/plugins/catalog-graphql/package.json b/plugins/catalog-graphql/package.json index c508215f29..505f7bb4df 100644 --- a/plugins/catalog-graphql/package.json +++ b/plugins/catalog-graphql/package.json @@ -40,7 +40,7 @@ "@graphql-codegen/typescript-resolvers": "^1.17.7", "ts-node": "^8.10.2", "eslint-plugin-graphql": "^4.0.0", - "msw": "^0.19.5", + "msw": "^0.20.5", "supertest": "^4.0.2" }, "files": [ diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 10278fc065..47600d5452 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -46,7 +46,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "@types/react-lazylog": "^4.5.0", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/explore/package.json b/plugins/explore/package.json index c889f96153..f094c4f908 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -41,7 +41,8 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index b71cebf6e4..fa72f0e66d 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -39,7 +39,8 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 9d1538ce99..1ee1e44074 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -47,7 +47,8 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index cb25167ba6..7805f35f85 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -39,7 +39,8 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index e6122eb38b..5747ff5726 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -53,7 +53,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "jest-fetch-mock": "^3.0.3", - "react-router-dom": "6.0.0-beta.0" + "react-router-dom": "6.0.0-beta.0", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/identity-backend/package.json b/plugins/identity-backend/package.json index a34049fb91..ab006d3ecc 100644 --- a/plugins/identity-backend/package.json +++ b/plugins/identity-backend/package.json @@ -34,7 +34,8 @@ }, "devDependencies": { "@backstage/cli": "^0.1.1-alpha.22", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index 398161f270..07ec728b34 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -44,7 +44,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "@types/testing-library__jest-dom": "^5.9.1", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index c5522ae67a..ea73010491 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -42,7 +42,8 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index 0cadf440f7..63a6d0a1b4 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -38,7 +38,8 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/register-component/package.json b/plugins/register-component/package.json index 8ccaae9914..c2d9709643 100644 --- a/plugins/register-component/package.json +++ b/plugins/register-component/package.json @@ -43,7 +43,8 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index cb37c6187a..c2154a2c85 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -47,7 +47,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "@types/react": "^16.9", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index ab95e83201..21b95a6aaf 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -49,7 +49,8 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 6e7daea15c..1fd836b83f 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -43,7 +43,8 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index f25ec752e1..36318be766 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -45,7 +45,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "@types/react": "^16.9", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index d2f99cb824..d467f8ef5f 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -48,7 +48,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "canvas": "^2.6.1", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 741a680cd9..7784d8ff97 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -39,7 +39,8 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5" }, "files": [ "dist" From 65664e2c30cb263c0ebcd821bd94e1d1e5310482 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 22 Sep 2020 01:54:38 +0200 Subject: [PATCH 18/38] chore(cli): fixing create again --- .../ExampleComponent.test.tsx.hbs | 18 +++++++++ .../ExampleFetchComponent.test.tsx.hbs | 16 ++++++++ packages/test-utils-core/package.json | 4 +- yarn.lock | 37 ++----------------- 4 files changed, 40 insertions(+), 35 deletions(-) diff --git a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs index f49457490f..1069790218 100644 --- a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs +++ b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs @@ -3,8 +3,26 @@ import { render } from '@testing-library/react'; import ExampleComponent from './ExampleComponent'; import { ThemeProvider } from '@material-ui/core'; import { lightTheme } from '@backstage/theme'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; + describe('ExampleComponent', () => { + const server = setupServer(); + // Enable API mocking before tests. + beforeAll(() => server.listen()) + + // Reset any runtime request handlers we may add during the tests. + afterEach(() => server.resetHandlers()) + + // Disable API mocking after the tests are done. + afterAll(() => server.close()) + + // setup mock response + beforeEach(() => { + server.use(rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({})))) + }) + it('should render', () => { const rendered = render( diff --git a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs index aad4fac10a..309fa34aee 100644 --- a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs +++ b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs @@ -1,8 +1,24 @@ import React from 'react'; import { render } from '@testing-library/react'; import ExampleFetchComponent from './ExampleFetchComponent'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; describe('ExampleFetchComponent', () => { + const server = setupServer(); + // Enable API mocking before tests. + beforeAll(() => server.listen()) + + // Reset any runtime request handlers we may add during the tests. + afterEach(() => server.resetHandlers()) + + // Disable API mocking after the tests are done. + afterAll(() => server.close()) + + // setup mock response + beforeEach(() => { + server.use(rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.delay(2000), ctx.json({})))) + }) it('should render', async () => { const rendered = render(); expect(await rendered.findByTestId('progress')).toBeInTheDocument(); diff --git a/packages/test-utils-core/package.json b/packages/test-utils-core/package.json index d79acf80a8..c279656736 100644 --- a/packages/test-utils-core/package.json +++ b/packages/test-utils-core/package.json @@ -18,8 +18,8 @@ "backstage" ], "license": "Apache-2.0", - "main": "src/index.ts", - "types": "src/index.ts", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts", "scripts": { "build": "backstage-cli build --outputs types,esm", "lint": "backstage-cli lint", diff --git a/yarn.lock b/yarn.lock index 0cdce51f95..df2c58400e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3287,7 +3287,7 @@ dependencies: "@types/node" ">= 8" -"@open-draft/until@^1.0.0", "@open-draft/until@^1.0.3": +"@open-draft/until@^1.0.3": version "1.0.3" resolved "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q== @@ -4507,11 +4507,6 @@ dependencies: "@types/express" "*" -"@types/cookie@^0.3.3": - version "0.3.3" - resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803" - integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow== - "@types/cookie@^0.4.0": version "0.4.0" resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz#14f854c0f93d326e39da6e3b6f34f7d37513d108" @@ -11817,7 +11812,7 @@ graphql-upload@^8.0.2: http-errors "^1.7.3" object-path "^0.11.4" -graphql@15.3.0, graphql@^15.0.0, graphql@^15.3.0: +graphql@15.3.0, graphql@^15.3.0: version "15.3.0" resolved "https://registry.npmjs.org/graphql/-/graphql-15.3.0.tgz#3ad2b0caab0d110e3be4a5a9b2aa281e362b5278" integrity sha512-GTCJtzJmkFLWRfFJuoo9RWWa/FfamUHgiFosxi/X1Ani4AVWbeyBenZTNX6dM+7WSbbFfTo/25eh0LLkwHMw2w== @@ -11993,7 +11988,7 @@ he@^1.1.0, he@^1.2.0: resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -headers-utils@^1.1.9, headers-utils@^1.2.0: +headers-utils@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/headers-utils/-/headers-utils-1.2.0.tgz#5e10d1bc9d2bccf789547afca5b991a3167241e8" integrity sha512-4/BMXcWrJErw7JpM87gF8MNEXcIMLzepYZjNRv/P9ctgupl2Ywa3u1PgHtNhSRq84bHH9Ndlkdy7bSi+bZ9I9A== @@ -15555,22 +15550,6 @@ ms@^2.0.0, ms@^2.1.1: resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -msw@^0.19.5: - version "0.19.5" - resolved "https://registry.npmjs.org/msw/-/msw-0.19.5.tgz#7d2a1a852ccf1644d3db6735d69fff6777aac33f" - integrity sha512-J5eQ++gDVZoHPC8gVXtWcakLjgmPipvFj/sEnlRV/WViXuiq2CamSqO3Wbh6H8bAmj+k2vUWCfcVT1HjMdKB2Q== - dependencies: - "@open-draft/until" "^1.0.0" - "@types/cookie" "^0.3.3" - chalk "^4.0.0" - cookie "^0.4.1" - graphql "^15.0.0" - headers-utils "^1.1.9" - node-match-path "^0.4.2" - node-request-interceptor "^0.2.5" - statuses "^2.0.0" - yargs "^15.3.1" - msw@^0.20.5: version "0.20.5" resolved "https://registry.npmjs.org/msw/-/msw-0.20.5.tgz#b6141080c0d8b17c451d9ca36c28cc47b4ac487a" @@ -15830,7 +15809,7 @@ node-libs-browser@^2.2.1: util "^0.11.0" vm-browserify "^1.0.1" -node-match-path@^0.4.2, node-match-path@^0.4.4: +node-match-path@^0.4.4: version "0.4.4" resolved "https://registry.npmjs.org/node-match-path/-/node-match-path-0.4.4.tgz#516a10926093c0cc6f237d020685b593b19baebb" integrity sha512-pBq9gp7TG0r0VXuy/oeZmQsjBSnYQo7G886Ly/B3azRwZuEtHCY155dzmfoKWcDPGgyfIGD8WKVC7h3+6y7yTg== @@ -15889,14 +15868,6 @@ node-releases@^1.1.52, node-releases@^1.1.58: resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084" integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA== -node-request-interceptor@^0.2.5: - version "0.2.6" - resolved "https://registry.npmjs.org/node-request-interceptor/-/node-request-interceptor-0.2.6.tgz#541278d7033bb6a8befb5dd793f83428cf6446a2" - integrity sha512-aJW1tPSM7nzuZFRe+C/KSz22GJO3CVFMxHHmMGX8Z+tjP7TCIVbzeckLFVfJG68BdVgrdOOP7Ejc57ag820eyA== - dependencies: - debug "^4.1.1" - headers-utils "^1.2.0" - node-request-interceptor@^0.3.5: version "0.3.5" resolved "https://registry.npmjs.org/node-request-interceptor/-/node-request-interceptor-0.3.5.tgz#4b26159617829c9a70643012c0fdc3ae4c78ae43" From 23275680fa3693ad68991bd1b064e3713fd1b8fe Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 22 Sep 2020 02:53:27 +0200 Subject: [PATCH 19/38] chore(cli): revert some core testing utils --- packages/test-utils-core/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/test-utils-core/package.json b/packages/test-utils-core/package.json index c279656736..d79acf80a8 100644 --- a/packages/test-utils-core/package.json +++ b/packages/test-utils-core/package.json @@ -18,8 +18,8 @@ "backstage" ], "license": "Apache-2.0", - "main": "dist/index.esm.js", - "types": "dist/index.d.ts", + "main": "src/index.ts", + "types": "src/index.ts", "scripts": { "build": "backstage-cli build --outputs types,esm", "lint": "backstage-cli lint", From a9adf229ff8ae800db0ed8a1efeb5d02f05b363a Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 22 Sep 2020 03:25:08 +0200 Subject: [PATCH 20/38] chore(cli): fixing cli issues --- .../ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs index 309fa34aee..c584289077 100644 --- a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs +++ b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs @@ -17,7 +17,7 @@ describe('ExampleFetchComponent', () => { // setup mock response beforeEach(() => { - server.use(rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.delay(2000), ctx.json({})))) + server.use(rest.get('https://randomuser.me/*', (_, res, ctx) => res(ctx.status(200), ctx.delay(2000), ctx.json({})))) }) it('should render', async () => { const rendered = render(); From b60086db6b5968be2f87b68f338d8c28a6aa7046 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 22 Sep 2020 03:56:16 +0200 Subject: [PATCH 21/38] chore(cli): updating nodefetch --- packages/cli/templates/default-backend-plugin/package.json.hbs | 2 +- .../cli/templates/default-backend-plugin/src/setupTests.ts | 1 + packages/cli/templates/default-plugin/package.json.hbs | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/default-backend-plugin/package.json.hbs index a4b6e7aaf9..40e612609a 100644 --- a/packages/cli/templates/default-backend-plugin/package.json.hbs +++ b/packages/cli/templates/default-backend-plugin/package.json.hbs @@ -29,7 +29,7 @@ "express": "^4.17.1", "express-promise-router": "^3.0.3", "winston": "^3.2.1", - "node-fetch": "^2.6.0", + "node-fetch": "^2.6.1", "yn": "^4.0.0" }, "devDependencies": { diff --git a/packages/cli/templates/default-backend-plugin/src/setupTests.ts b/packages/cli/templates/default-backend-plugin/src/setupTests.ts index ba33cf996b..a5907fd52f 100644 --- a/packages/cli/templates/default-backend-plugin/src/setupTests.ts +++ b/packages/cli/templates/default-backend-plugin/src/setupTests.ts @@ -15,3 +15,4 @@ */ export {}; +global.fetch = require('node-fetch'); diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index 0d26339843..e024884547 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -41,7 +41,8 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" From 521728348d0939f4695d64f7793d409dd598e6ca Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 22 Sep 2020 14:56:52 +0200 Subject: [PATCH 22/38] backend: change the default backend plugin mount point to /api --- CHANGELOG.md | 4 +++ app-config.yaml | 4 +-- docs/auth/add-auth-provider.md | 7 ++--- .../software-templates/adding-templates.md | 4 +-- docs/features/techdocs/getting-started.md | 4 +-- docs/openapi/definitions/auth.yaml | 6 ++--- docs/tutorials/quickstart-app-auth.md | 2 +- packages/app/src/App.test.tsx | 2 +- packages/app/src/apis.ts | 12 --------- packages/backend/package.json | 1 + packages/backend/src/index.ts | 26 ++++++++++++------- packages/backend/src/plugins/auth.ts | 11 ++++---- .../templates/default-app/app-config.yaml.hbs | 4 +-- .../default-app/packages/app/src/App.test.tsx | 2 +- .../packages/backend/package.json.hbs | 1 + .../default-app/packages/backend/src/index.ts | 18 ++++++++----- packages/e2e-test/src/e2e-test.ts | 2 +- plugins/auth-backend/README.md | 6 ++--- .../auth-backend/scripts/start-saml-idp.sh | 2 +- plugins/auth-backend/src/service/router.ts | 4 ++- plugins/techdocs-backend/README.md | 4 +-- .../src/techdocs/stages/publish/local.ts | 2 +- 22 files changed, 68 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25360f3e1e..d360dd995d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ If you encounter issues while upgrading to a newer version, don't hesitate to re - Renamed `SessionStateApi` to `SessionApi` and `logout` to `signOut`. Custom implementations of the `SingInPage` app-component will need to rename their `logout` function. The different auth provider items for the `UserSettingsMenu` have been consolidated into a single `ProviderSettingsItem`, meaning you need to replace existing usages of `OAuthProviderSettings` and `OIDCProviderSettings`. [#2555](https://github.com/spotify/backstage/pull/2555). +### @backstage/auth-backend + +- The default mount path of backend plugins was changed to `/api/:pluginId`, and as part of that it was needed to enable configuration of the base path of the auth backend, so that it can construct redirect URLs correctly. The default base path is `/api/auth`, but you need to set this to `/auth` if you want to keep using the old path. Note that you will also need to reconfigure any allowed redirect URLs to include `/api` if you switch to the new recommended pattern. [#2562](https://github.com/spotify/backstage/pull/2562) + ## v0.1.1-alpha.22 ### @backstage/core diff --git a/app-config.yaml b/app-config.yaml index 60ecb813f7..fca0a39496 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -33,8 +33,8 @@ organization: name: Spotify techdocs: - storageUrl: http://localhost:7000/techdocs/static/docs - requestUrl: http://localhost:7000/techdocs/docs + storageUrl: http://localhost:7000/api/techdocs/static/docs + requestUrl: http://localhost:7000/api/techdocs/docs generators: techdocs: 'docker' diff --git a/docs/auth/add-auth-provider.md b/docs/auth/add-auth-provider.md index c5f7645a49..56586cbfc6 100644 --- a/docs/auth/add-auth-provider.md +++ b/docs/auth/add-auth-provider.md @@ -229,9 +229,10 @@ name. ### Test the new provider -You can `curl -i localhost:7000/auth/providerA/start` and which should provide a -`302` redirect with a `Location` header. Paste the url from that header into a -web browser and you should be able to trigger the authorization flow. +You can `curl -i localhost:7000/api/auth/providerA/start` and which should +provide a `302` redirect with a `Location` header. Paste the url from that +header into a web browser and you should be able to trigger the authorization +flow. --- diff --git a/docs/features/software-templates/adding-templates.md b/docs/features/software-templates/adding-templates.md index fad5c68e41..59019e754e 100644 --- a/docs/features/software-templates/adding-templates.md +++ b/docs/features/software-templates/adding-templates.md @@ -88,7 +88,7 @@ running: ```sh curl \ --location \ - --request POST 'localhost:7000/catalog/locations' \ + --request POST 'localhost:7000/api/catalog/locations' \ --header 'Content-Type: application/json' \ --data-raw "{\"type\": \"file\", \"target\": \"${YOUR PATH HERE}/template.yaml\"}" ``` @@ -98,7 +98,7 @@ If loading from a Git location, you can run the following ```sh curl \ --location \ - --request POST 'localhost:7000/catalog/locations' \ + --request POST 'localhost:7000/api/catalog/locations' \ --header 'Content-Type: application/json' \ --data-raw "{\"type\": \"github\", \"target\": \"https://${YOUR GITHUB REPO}blob/master/${PATH TO FOLDER}/template.yaml\"}" ``` diff --git a/docs/features/techdocs/getting-started.md b/docs/features/techdocs/getting-started.md index b9d215fd8a..03494564c3 100644 --- a/docs/features/techdocs/getting-started.md +++ b/docs/features/techdocs/getting-started.md @@ -57,8 +57,8 @@ The default storage and request URLs: ```yaml techdocs: - storageUrl: http://localhost:7000/techdocs/static/docs - requestUrl: http://localhost:7000/techdocs/docs + storageUrl: http://localhost:7000/api/techdocs/static/docs + requestUrl: http://localhost:7000/api/techdocs/docs ``` If you want `techdocs-backend` to manage building and publishing, you want diff --git a/docs/openapi/definitions/auth.yaml b/docs/openapi/definitions/auth.yaml index eb96d434c2..cb736fd2a9 100644 --- a/docs/openapi/definitions/auth.yaml +++ b/docs/openapi/definitions/auth.yaml @@ -6,10 +6,10 @@ info: **Provided by `@backstage/auth-backend`.** - The purpose of the Auth APIs in Backstage are to identify the user, and to provide a way for plugins + The purpose of the Auth APIs in Backstage are to identify the user, and to provide a way for plugins to request access to 3rd party services on behalf of that user. - The API is supplied with a list of providers - such as `Google` or `Github` - and will add the endpoints + The API is supplied with a list of providers - such as `Google` or `Github` - and will add the endpoints described below to each of those providers. Read more about [User Authentication and Authorization in Backstage](https://github.com/spotify/backstage/blob/master/docs/auth/overview.md). @@ -21,7 +21,7 @@ externalDocs: description: Backstage official documentation url: https://github.com/spotify/backstage/blob/master/docs/README.md servers: - - url: http://localhost:7000/auth/ + - url: http://localhost:7000/api/auth/ tags: - name: provider description: List of endpoints per provider diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 2615e37633..ad2bf1af88 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -115,7 +115,7 @@ export AUTH_GITHUB_CLIENT_SECRET=xxx > Log into http://github.com > Navigate to (Settings > Developer Settings > OAuth Apps > New OAuth App)[https://github.com/settings/applications/new] > Set Homepage URL = http://localhost:3000 -> Set Callback URL = http://localhost:7000/auth/github +> Set Callback URL = http://localhost:7000/api/auth/github > Click [Register application] > On the next page, copy and paste your new Client ID and Client Secret to the environment variables above, `AUTH_GITHUB_CLIENT_ID` & `AUTH_GITHUB_CLIENT_SECRET` > Don't forget to `source` that profile file again if necessary. diff --git a/packages/app/src/App.test.tsx b/packages/app/src/App.test.tsx index cddd093855..40c49416bf 100644 --- a/packages/app/src/App.test.tsx +++ b/packages/app/src/App.test.tsx @@ -31,7 +31,7 @@ describe('App', () => { baseUrl: 'http://localhost:3003', }, techdocs: { - storageUrl: 'http://localhost:7000/techdocs/static/docs', + storageUrl: 'http://localhost:7000/api/techdocs/static/docs', }, }, context: 'test', diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index 7f57f6f970..581a591152 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -16,11 +16,8 @@ import { errorApiRef, - discoveryApiRef, - UrlPatternDiscovery, githubAuthApiRef, createApiFactory, - configApiRef, } from '@backstage/core'; import { @@ -38,15 +35,6 @@ import { } from '@roadiehq/backstage-plugin-github-pull-requests'; export const apis = [ - // TODO(Rugvip): migrate to use /api - createApiFactory({ - api: discoveryApiRef, - deps: { configApi: configApiRef }, - factory: ({ configApi }) => - UrlPatternDiscovery.compile( - `${configApi.getString('backend.baseUrl')}/{{ pluginId }}`, - ), - }), createApiFactory({ api: graphQlBrowseApiRef, deps: { errorApi: errorApiRef, githubAuthApi: githubAuthApiRef }, diff --git a/packages/backend/package.json b/packages/backend/package.json index 147ea630c2..9e14062992 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -37,6 +37,7 @@ "dockerode": "^3.2.0", "example-app": "^0.1.1-alpha.22", "express": "^4.17.1", + "express-promise-router": "^3.0.3", "knex": "^0.21.1", "pg": "^8.3.0", "pg-connection-string": "^2.3.0", diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 7c52175d76..9d4a738ecf 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -22,12 +22,14 @@ * Happy hacking! */ +import Router from 'express-promise-router'; import { createDatabaseClient, createServiceBuilder, loadBackendConfig, getRootLogger, useHotMemoize, + notFoundHandler, } from '@backstage/backend-common'; import { ConfigReader, AppConfig } from '@backstage/config'; import healthcheck from './plugins/healthcheck'; @@ -78,19 +80,23 @@ async function main() { const graphqlEnv = useHotMemoize(module, () => createEnv('graphql')); const appEnv = useHotMemoize(module, () => createEnv('app')); + const apiRouter = Router(); + apiRouter.use('/catalog', await catalog(catalogEnv)); + apiRouter.use('/rollbar', await rollbar(rollbarEnv)); + apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv)); + apiRouter.use('/sentry', await sentry(sentryEnv)); + apiRouter.use('/auth', await auth(authEnv, '/api/auth')); + apiRouter.use('/identity', await identity(identityEnv)); + apiRouter.use('/techdocs', await techdocs(techdocsEnv)); + apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv)); + apiRouter.use('/proxy', await proxy(proxyEnv, '/api/proxy')); + apiRouter.use('/graphql', await graphql(graphqlEnv)); + apiRouter.use(notFoundHandler()); + const service = createServiceBuilder(module) .loadConfig(configReader) .addRouter('', await healthcheck(healthcheckEnv)) - .addRouter('/catalog', await catalog(catalogEnv)) - .addRouter('/rollbar', await rollbar(rollbarEnv)) - .addRouter('/scaffolder', await scaffolder(scaffolderEnv)) - .addRouter('/sentry', await sentry(sentryEnv)) - .addRouter('/auth', await auth(authEnv)) - .addRouter('/identity', await identity(identityEnv)) - .addRouter('/techdocs', await techdocs(techdocsEnv)) - .addRouter('/kubernetes', await kubernetes(kubernetesEnv)) - .addRouter('/proxy', await proxy(proxyEnv, '/proxy')) - .addRouter('/graphql', await graphql(graphqlEnv)) + .addRouter('/api', apiRouter) .addRouter('', await app(appEnv)); await service.start().catch(err => { diff --git a/packages/backend/src/plugins/auth.ts b/packages/backend/src/plugins/auth.ts index b24dd6ca6b..60baaff6b7 100644 --- a/packages/backend/src/plugins/auth.ts +++ b/packages/backend/src/plugins/auth.ts @@ -17,10 +17,9 @@ import { createRouter } from '@backstage/plugin-auth-backend'; import { PluginEnvironment } from '../types'; -export default async function createPlugin({ - logger, - database, - config, -}: PluginEnvironment) { - return await createRouter({ logger, config, database }); +export default async function createPlugin( + { logger, database, config }: PluginEnvironment, + basePath: string, +) { + return await createRouter({ logger, config, database, basePath }); } diff --git a/packages/create-app/templates/default-app/app-config.yaml.hbs b/packages/create-app/templates/default-app/app-config.yaml.hbs index a7d7aa8010..92c517bf93 100644 --- a/packages/create-app/templates/default-app/app-config.yaml.hbs +++ b/packages/create-app/templates/default-app/app-config.yaml.hbs @@ -44,8 +44,8 @@ proxy: changeOrigin: true techdocs: - storageUrl: http://localhost:7000/techdocs/static/docs - requestUrl: http://localhost:7000/techdocs/docs + storageUrl: http://localhost:7000/api/techdocs/static/docs + requestUrl: http://localhost:7000/api/techdocs/docs generators: techdocs: 'docker' diff --git a/packages/create-app/templates/default-app/packages/app/src/App.test.tsx b/packages/create-app/templates/default-app/packages/app/src/App.test.tsx index e88c3b47dd..21a2eaa703 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.test.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.test.tsx @@ -12,7 +12,7 @@ describe('App', () => { app: { title: 'Test' }, backend: { baseUrl: 'http://localhost:7000' }, techdocs: { - storageUrl: 'http://localhost:7000/techdocs/static/docs', + storageUrl: 'http://localhost:7000/api/techdocs/static/docs', }, }, context: 'test', diff --git a/packages/create-app/templates/default-app/packages/backend/package.json.hbs b/packages/create-app/templates/default-app/packages/backend/package.json.hbs index adde81e830..b70f36650e 100644 --- a/packages/create-app/templates/default-app/packages/backend/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/backend/package.json.hbs @@ -31,6 +31,7 @@ "@gitbeaker/node": "^23.5.0", "dockerode": "^3.2.0", "express": "^4.17.1", + "express-promise-router": "^3.0.3", "knex": "^0.21.1", {{#if dbTypePG}} "pg": "^8.3.0", diff --git a/packages/create-app/templates/default-app/packages/backend/src/index.ts b/packages/create-app/templates/default-app/packages/backend/src/index.ts index 6a014727e9..457b95a7a9 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/index.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/index.ts @@ -6,12 +6,14 @@ * Happy hacking! */ +import Router from 'express-promise-router'; import { createDatabaseClient, createServiceBuilder, loadBackendConfig, getRootLogger, useHotMemoize, + notFoundHandler, } from '@backstage/backend-common'; import { ConfigReader, AppConfig } from '@backstage/config'; import auth from './plugins/auth'; @@ -51,14 +53,18 @@ async function main() { const proxyEnv = useHotMemoize(module, () => createEnv('proxy')); const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs')); + const apiRouter = Router(); + apiRouter.use('/catalog', await catalog(catalogEnv)) + apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv)) + apiRouter.use('/auth', await auth(authEnv)) + apiRouter.use('/identity', await identity(identityEnv)) + apiRouter.use('/techdocs', await techdocs(techdocsEnv)) + apiRouter.use('/proxy', await proxy(proxyEnv, '/api/proxy')) + apiRouter.use(notFoundHandler()); + const service = createServiceBuilder(module) .loadConfig(configReader) - .addRouter('/catalog', await catalog(catalogEnv)) - .addRouter('/scaffolder', await scaffolder(scaffolderEnv)) - .addRouter('/auth', await auth(authEnv)) - .addRouter('/identity', await identity(identityEnv)) - .addRouter('/techdocs', await techdocs(techdocsEnv)) - .addRouter('/proxy', await proxy(proxyEnv, '/proxy')); + .addRouter('/api', apiRouter) await service.start().catch(err => { console.log(err); diff --git a/packages/e2e-test/src/e2e-test.ts b/packages/e2e-test/src/e2e-test.ts index 969a77cb2a..ec34f48959 100644 --- a/packages/e2e-test/src/e2e-test.ts +++ b/packages/e2e-test/src/e2e-test.ts @@ -384,7 +384,7 @@ async function testBackendStart(appDir: string, isPostgres: boolean) { print('Try to fetch entities from the backend'); // Try fetch entities, should be ok - await fetch('http://localhost:7000/catalog/entities').then(res => + await fetch('http://localhost:7000/api/catalog/entities').then(res => res.json(), ); print('Entities fetched successfully'); diff --git a/plugins/auth-backend/README.md b/plugins/auth-backend/README.md index 9af90df3f2..868d88b3d0 100644 --- a/plugins/auth-backend/README.md +++ b/plugins/auth-backend/README.md @@ -34,7 +34,7 @@ Follow this link, [Create new OAuth App](https://github.com/settings/application 1. Set Application Name to `backstage-dev` or something along those lines. 1. You can set the Homepage URL to whatever you want to. 1. The Authorization Callback URL should match the redirect URI set in Backstage. - 1. Set this to `http://localhost:7000/auth/github` for local development. + 1. Set this to `http://localhost:7000/api/auth/github` for local development. 1. Set this to `http://{APP_FQDN}:{APP_BACKEND_PORT}/auth/github` for non-local deployments. ```bash @@ -78,14 +78,14 @@ export AUTH_AUTH0_CLIENT_SECRET=x #### Creating an Azure AD App Registration -An Azure AD App Registration is required to be able to sign in using Azure AD and the Microsoft Graph API. +An Azure AD App Registration is required to be able to sign in using Azure AD and the Microsoft Graph API. Click [here](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps) to create a new one. - Click on the `New Registration` button. - Give the app a name. e.g. `backstage-dev` - Select `Accounts in this organizational directory only` under supported account types. - Enter the callback URL for your backstage backend instance: - - For local development, this is likely `http://localhost:7000/auth/microsoft/handler/frame` + - For local development, this is likely `http://localhost:7000/api/auth/microsoft/handler/frame` - For non-local deployments, this will be `https://{APP_FQDN}:{APP_BACKEND_PORT}/auth/microsoft/handler/frame` - Click `Register`. diff --git a/plugins/auth-backend/scripts/start-saml-idp.sh b/plugins/auth-backend/scripts/start-saml-idp.sh index 33217f7978..b312a4b85e 100755 --- a/plugins/auth-backend/scripts/start-saml-idp.sh +++ b/plugins/auth-backend/scripts/start-saml-idp.sh @@ -18,4 +18,4 @@ fi echo "Downloading and starting SAML-IdP" export NPM_CONFIG_REGISTRY=https://registry.npmjs.org -exec npx saml-idp --acsUrl "http://localhost:7000/auth/saml/handler/frame" --audience "http://localhost:7000" --port 7001 +exec npx saml-idp --acsUrl "http://localhost:7000/api/auth/saml/handler/frame" --audience "http://localhost:7000" --port 7001 diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index 19a74d47c5..e70e687879 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -28,6 +28,7 @@ export interface RouterOptions { logger: Logger; database: Knex; config: Config; + basePath?: string; } export async function createRouter( @@ -38,7 +39,8 @@ export async function createRouter( const appUrl = options.config.getString('app.baseUrl'); const backendUrl = options.config.getString('backend.baseUrl'); - const authUrl = `${backendUrl}/auth`; + // TODO(Rugvip): Replace with service discovery of external URL + const authUrl = backendUrl + (options.basePath ?? '/api/auth'); const keyDurationSeconds = 3600; diff --git a/plugins/techdocs-backend/README.md b/plugins/techdocs-backend/README.md index 2185c3b3e4..b7eca2c5cb 100644 --- a/plugins/techdocs-backend/README.md +++ b/plugins/techdocs-backend/README.md @@ -18,11 +18,11 @@ yarn start ## What techdocs-backend does -This plugin is the backend part of the techdocs plugin. It provides building and serving of your docs without having to use another service and hosting provider. To use it set your techdocs storageUrl in your `app-config.yml` to `http://localhost:7000/techdocs/static/docs`. +This plugin is the backend part of the techdocs plugin. It provides building and serving of your docs without having to use another service and hosting provider. To use it set your techdocs storageUrl in your `app-config.yml` to `http://localhost:7000/api/techdocs/static/docs`. ```yaml techdocs: - storageUrl: http://localhost:7000/techdocs/static/docs + storageUrl: http://localhost:7000/api/techdocs/static/docs ``` ## Extending techdocs-backend diff --git a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts index e33c6fa17d..f328443965 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/publish/local.ts @@ -64,7 +64,7 @@ export class LocalPublish implements PublisherBase { } resolve({ - remoteUrl: `http://localhost:7000/techdocs/static/docs/${entity.metadata.name}`, + remoteUrl: `http://localhost:7000/api/techdocs/static/docs/${entity.metadata.name}`, }); }); }); From 5f71c826b91b306d05ed97973057fd0f97fbf16d Mon Sep 17 00:00:00 2001 From: Roberto Devesa Date: Thu, 24 Sep 2020 06:46:08 +0200 Subject: [PATCH 23/38] Fix typo (#2584) --- docs/overview/what-is-backstage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/what-is-backstage.md b/docs/overview/what-is-backstage.md index 0d16943c9b..1a5e80a302 100644 --- a/docs/overview/what-is-backstage.md +++ b/docs/overview/what-is-backstage.md @@ -1,7 +1,7 @@ --- id: what-is-backstage title: What is Backstage? -description: Backsatge is an open platform for building developer portals. +description: Backstage is an open platform for building developer portals. Powered by a centralized service catalog, Backstage restores order to your microservices and infrastructure --- From 56b82f523d26723ec55c3c2327dfa7425c50a60d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 24 Sep 2020 07:55:54 +0200 Subject: [PATCH 24/38] chore: add contrib to CONTRIBUTING.md (#2585) --- CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a6f7e5e5e1..d3c2780967 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,6 +32,10 @@ If you start developing a plugin that you aim to release as open source, we sugg You can also use this process if you have an idea for a good plugin but you hope that someone else will pick up the work. +## Adding Non-code Contributions + +Since there is such a large landscape of possible development, build, and deployment environments, we welcome community contributions in these areas in the [`/contrib`](https://github.com/spotify/backstage/tree/master/contrib) folder of the project. This is an excellent place to put things that help out the community at large, but which may not fit within the scope of the core product to support natively. Here, you will find Helm charts, alternative Docker images, and much more. + ## Write Documentation The current documentation is very limited. Help us make the `/docs` folder come alive. From 4ee2c6a16d2f8111769a0600b73c0f8343e81633 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Thu, 24 Sep 2020 09:03:33 +0200 Subject: [PATCH 25/38] fix(techdocs-core): use the content of the readme as long description for the pypi package (#2578) --- packages/techdocs-container/techdocs-core/setup.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/techdocs-container/techdocs-core/setup.py b/packages/techdocs-container/techdocs-core/setup.py index e5a8206150..00e49dd366 100644 --- a/packages/techdocs-container/techdocs-core/setup.py +++ b/packages/techdocs-container/techdocs-core/setup.py @@ -14,13 +14,19 @@ See the License for the specific language governing permissions and limitations under the License. """ from setuptools import setup, find_packages +from os import path +# read the contents of the README file in the current directory +this_dir = path.abspath(path.dirname(__file__)) +with open(path.join(this_dir, "README.md"), encoding="utf-8") as file: + long_description = file.read() setup( name="mkdocs-techdocs-core", version="0.0.8", description="A Mkdocs package that contains TechDocs defaults", - long_description="", + long_description=long_description, + long_description_content_type="text/markdown", keywords="mkdocs", url="https://github.com/spotify/backstage", author="TechDocs Core", From 5be5d6bca565f7a9c1ab0b4f2432f97da45c7b00 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 24 Sep 2020 10:48:44 +0200 Subject: [PATCH 26/38] chore(gcp): updating gcp projects npm visibility --- plugins/gcp-projects/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index b71cebf6e4..8c7f79fd96 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -4,7 +4,6 @@ "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", - "private": true, "publishConfig": { "access": "public", "main": "dist/index.esm.js", From 37a4a3afd0ed498ac35e983ffb1cb56b0662468e Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 24 Sep 2020 10:52:26 +0200 Subject: [PATCH 27/38] chore(deps): updating dependencies for plugin --- plugins/api-docs/package.json | 3 ++- plugins/catalog/package.json | 3 ++- plugins/circleci/package.json | 3 ++- plugins/explore/package.json | 3 ++- plugins/gcp-projects/package.json | 3 ++- plugins/github-actions/package.json | 3 ++- plugins/gitops-profiles/package.json | 3 ++- plugins/graphiql/package.json | 3 ++- plugins/jenkins/package.json | 3 ++- plugins/lighthouse/package.json | 3 ++- plugins/newrelic/package.json | 3 ++- plugins/register-component/package.json | 3 ++- plugins/rollbar/package.json | 3 ++- plugins/scaffolder/package.json | 3 ++- plugins/sentry/package.json | 3 ++- plugins/tech-radar/package.json | 3 ++- plugins/techdocs/package.json | 3 ++- plugins/welcome/package.json | 3 ++- 18 files changed, 36 insertions(+), 18 deletions(-) diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 254dc9aba0..ee86e29183 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -50,7 +50,8 @@ "@types/react": "^16.9", "@types/swagger-ui-react": "^3.23.3", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index f5520f39f9..9d777c9bcd 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -53,7 +53,8 @@ "jest-fetch-mock": "^3.0.3", "msw": "^0.20.5", "react-test-renderer": "^16.13.1", - "whatwg-fetch": "^3.4.0" + "whatwg-fetch": "^3.4.0", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 47600d5452..7dce54f8b3 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -47,7 +47,8 @@ "@types/node": "^12.0.0", "@types/react-lazylog": "^4.5.0", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/explore/package.json b/plugins/explore/package.json index f094c4f908..7a46a2954f 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -42,7 +42,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index fa72f0e66d..c6424056c8 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -40,7 +40,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 1ee1e44074..8ee00bef06 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -48,7 +48,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index 7805f35f85..79b9b3a708 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -40,7 +40,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 5747ff5726..ab80901fa0 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -54,7 +54,8 @@ "@types/node": "^12.0.0", "jest-fetch-mock": "^3.0.3", "react-router-dom": "6.0.0-beta.0", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index 07ec728b34..091ef9356a 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -45,7 +45,8 @@ "@types/node": "^12.0.0", "@types/testing-library__jest-dom": "^5.9.1", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index ea73010491..a5e7ae7b62 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -43,7 +43,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index 63a6d0a1b4..7ad697a254 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -39,7 +39,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/register-component/package.json b/plugins/register-component/package.json index c2d9709643..58fe9855a2 100644 --- a/plugins/register-component/package.json +++ b/plugins/register-component/package.json @@ -44,7 +44,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index c2154a2c85..3faa06d0d3 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -48,7 +48,8 @@ "@types/node": "^12.0.0", "@types/react": "^16.9", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 21b95a6aaf..db8f335156 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -50,7 +50,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 1fd836b83f..8ac1ab2838 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -44,7 +44,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 36318be766..a8eded052c 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -46,7 +46,8 @@ "@types/node": "^12.0.0", "@types/react": "^16.9", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index d467f8ef5f..ab2e0dd038 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -49,7 +49,8 @@ "@types/node": "^12.0.0", "canvas": "^2.6.1", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 7784d8ff97..61406087aa 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -40,7 +40,8 @@ "@types/jest": "^26.0.7", "@types/node": "^12.0.0", "jest-fetch-mock": "^3.0.3", - "msw": "^0.20.5" + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" From 95c4d68e511243755da2747b0cf0d541555f9e24 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 24 Sep 2020 11:20:07 +0200 Subject: [PATCH 28/38] chore(cli): added node-fetch as a setup dependeny --- packages/cli/templates/default-plugin/src/setupTests.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/templates/default-plugin/src/setupTests.ts b/packages/cli/templates/default-plugin/src/setupTests.ts index 7b0828bfa8..cc559f672e 100644 --- a/packages/cli/templates/default-plugin/src/setupTests.ts +++ b/packages/cli/templates/default-plugin/src/setupTests.ts @@ -1 +1,2 @@ import '@testing-library/jest-dom'; +global.fetch = require('node-fetch'); From 6b8a6d8d52749dd7acbe749c5a24053a3577a8fd Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Thu, 24 Sep 2020 11:26:25 +0200 Subject: [PATCH 29/38] fix(techdocs-core): change development status of techdocs core package (#2589) --- packages/techdocs-container/techdocs-core/README.md | 4 ++++ packages/techdocs-container/techdocs-core/setup.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/techdocs-container/techdocs-core/README.md b/packages/techdocs-container/techdocs-core/README.md index 49b9fce4e4..7f34722e81 100644 --- a/packages/techdocs-container/techdocs-core/README.md +++ b/packages/techdocs-container/techdocs-core/README.md @@ -82,6 +82,10 @@ Extensions: ## Changelog +### 0.0.9 + +- Change development status to 3 - Alpha + ### 0.0.8 - Superfences and Codehilite doesn't work very well together (squidfunk/mkdocs-material#1604) so therefore the codehilite extension is replaced by pymdownx.highlight diff --git a/packages/techdocs-container/techdocs-core/setup.py b/packages/techdocs-container/techdocs-core/setup.py index 00e49dd366..4385bc2e38 100644 --- a/packages/techdocs-container/techdocs-core/setup.py +++ b/packages/techdocs-container/techdocs-core/setup.py @@ -23,7 +23,7 @@ with open(path.join(this_dir, "README.md"), encoding="utf-8") as file: setup( name="mkdocs-techdocs-core", - version="0.0.8", + version="0.0.9", description="A Mkdocs package that contains TechDocs defaults", long_description=long_description, long_description_content_type="text/markdown", @@ -43,7 +43,7 @@ setup( "pymdown-extensions==7.1", ], classifiers=[ - "Development Status :: 1 - Planning", + "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "License :: OSI Approved :: Apache Software License", From f77e21ea8db430ba1f34c6bc1f899bd862b016e3 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 24 Sep 2020 11:46:52 +0200 Subject: [PATCH 30/38] chore(diff): updating plugin diff for new k8s plugin --- plugins/kubernetes/package.json | 4 +++- yarn.lock | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index d9311deb85..ffe6207680 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -39,7 +39,9 @@ "@testing-library/user-event": "^12.0.7", "@types/jest": "^26.0.7", "@types/node": "^12.0.0", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "msw": "^0.20.5", + "node-fetch": "^2.6.1" }, "files": [ "dist" diff --git a/yarn.lock b/yarn.lock index 24050ef3d4..0b6a8c83f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16110,7 +16110,7 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" -node-fetch@^2.1.2, node-fetch@^2.2.0, node-fetch@^2.3.0, node-fetch@^2.5.0, node-fetch@^2.6.0: +node-fetch@^2.1.2, node-fetch@^2.2.0, node-fetch@^2.3.0, node-fetch@^2.5.0, node-fetch@^2.6.0, node-fetch@^2.6.1: version "2.6.1" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== From d432037459c195060ce2ffb673df1abad155148c Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 24 Sep 2020 12:37:48 +0200 Subject: [PATCH 31/38] chore(cli): addding backstage backend plugin creation --- packages/e2e-test/src/e2e-test.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/e2e-test/src/e2e-test.ts b/packages/e2e-test/src/e2e-test.ts index 969a77cb2a..7efcce567d 100644 --- a/packages/e2e-test/src/e2e-test.ts +++ b/packages/e2e-test/src/e2e-test.ts @@ -55,6 +55,9 @@ async function main() { print('Creating a Backstage Plugin'); const pluginName = await createPlugin('test-plugin', appDir); + print('Creating a Backstage Backend Plugin'); + await createPlugin('test-backend-plugin', appDir, ['--backend']); + print('Starting the app'); await testAppServe(pluginName, appDir); @@ -238,8 +241,12 @@ async function overrideModuleResolutions(appDir: string, workspaceDir: string) { /** * Uses create-plugin command to create a new plugin in the app */ -async function createPlugin(pluginName: string, appDir: string) { - const child = spawnPiped(['yarn', 'create-plugin'], { +async function createPlugin( + pluginName: string, + appDir: string, + options: string[] = [], +) { + const child = spawnPiped(['yarn', 'create-plugin', ...options], { cwd: appDir, }); From 06ae3734e7f2e9533b64ac60b855b45af0a96843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Thu, 24 Sep 2020 14:33:30 +0200 Subject: [PATCH 32/38] [docs] Add links to CNCF information (#2570) * [docs] restructure * Add CNCF links * Update background.md * Update Footer.js * Update README.md --- README.md | 6 +++--- docs/overview/architecture-overview.md | 17 +++++++++++++++++ docs/overview/architecture-terminology.md | 20 -------------------- docs/overview/background.md | 9 ++++++++- docs/overview/support.md | 7 ++----- docs/overview/what-is-backstage.md | 9 ++++++++- microsite/core/Footer.js | 1 + microsite/sidebars.json | 1 - mkdocs.yml | 1 - 9 files changed, 39 insertions(+), 32 deletions(-) delete mode 100644 docs/overview/architecture-terminology.md diff --git a/README.md b/README.md index 9ef09aee06..e05c1eb16e 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ Out of the box, Backstage includes: For more information go to [backstage.io](https://backstage.io) or join our [Discord chatroom](https://discord.gg/EBHEGzX). +🎉 Backstage is a CNCF Sandbox project. Read the announcement [here](https://backstage.io/blog/2020/09/23/backstage-cncf-sandbox). + ## Project roadmap A detailed project roadmap, including already delivered milestones, is available [here](https://backstage.io/docs/overview/roadmap). @@ -51,11 +53,9 @@ Check out [the documentation](https://backstage.io/docs/getting-started) on how - [Code of Conduct](CODE_OF_CONDUCT.md) - This is how we roll - [Adopters](ADOPTERS.md) - Companies already using Backstage - [Blog](https://backstage.io/blog/) - Announcements and updates -- [Newsletter](https://mailchi.mp/spotify/backstage-community) +- [Newsletter](https://mailchi.mp/spotify/backstage-community) - Subscribe to our email newsletter - Give us a star ⭐️ - If you are using Backstage or think it is an interesting project, we would love a star ❤️ -Or, if you are an open source developer and are interested in joining our team, please reach out to [foss-opportunities@spotify.com ](mailto:foss-opportunities@spotify.com) - ## License Copyright 2020 Spotify AB. diff --git a/docs/overview/architecture-overview.md b/docs/overview/architecture-overview.md index f2c6dad61f..8edf3e9e4c 100644 --- a/docs/overview/architecture-overview.md +++ b/docs/overview/architecture-overview.md @@ -4,6 +4,23 @@ title: Architecture overview description: Documentation on Architecture overview --- +## Terminology + +Backstage is constructed out of three parts. We separate Backstage in this way +because we see three groups of contributors that work with Backstage in three +different ways. + +- Core - Base functionality built by core developers in the open source project. +- App - The app is an instance of a Backstage app that is deployed and tweaked. + The app ties together core functionality with additional plugins. The app is + built and maintained by app developers, usually a productivity team within a + company. +- Plugins - Additional functionality to make your Backstage app useful for your + company. Plugins can be specific to a company or open sourced and reusable. At + Spotify we have over 100 plugins built by over 50 different teams. It has been + very powerful to get contributions from various infrastructure teams added + into a single unified developer experience. + ## Overview The following diagram shows how Backstage might look when deployed inside a diff --git a/docs/overview/architecture-terminology.md b/docs/overview/architecture-terminology.md deleted file mode 100644 index 092ca7fb01..0000000000 --- a/docs/overview/architecture-terminology.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: architecture-terminology -title: Architecture terminology -description: Documentation on Architecture terminology ---- - -Backstage is constructed out of three parts. We separate Backstage in this way -because we see three groups of contributors that work with Backstage in three -different ways. - -- Core - Base functionality built by core devs in the open source project. -- App - The app is an instance of a Backstage app that is deployed and tweaked. - The app ties together core functionality with additional plugins. The app is - built and maintained by app developers, usually a productivity team within a - company. -- Plugins - Additional functionality to make your Backstage app useful for your - company. Plugins can be specific to a company or open sourced and reusable. At - Spotify we have over 100 plugins built by over 50 different teams. It has been - very powerful to get contributions from various infrastructure teams added - into a single unified developer experience. diff --git a/docs/overview/background.md b/docs/overview/background.md index 486260bac3..aca46614c2 100644 --- a/docs/overview/background.md +++ b/docs/overview/background.md @@ -1,7 +1,8 @@ --- id: background title: The Spotify Story -description: Documentation on Background and Story behind making of Backstage +description: Backstage was born out of necessity at Spotify. We found that as we grew, our +infrastructure was becoming more fragmented, our engineers less productive. --- Backstage was born out of necessity at Spotify. We found that as we grew, our @@ -29,3 +30,9 @@ building a new microservice using an automated template in Backstage. Create, maintain, and find the documentation for all that software in Backstage. One place for everything. Accessible to everyone. + +Backstage was originally built by Spotify and then donated to the CNCF. +Backstage is currently in the Sandbox phase. Read the announcement +[here](https://backstage.io/blog/2020/09/23/backstage-cncf-sandbox). + + diff --git a/docs/overview/support.md b/docs/overview/support.md index 6e00c17fab..12bc7cfaf9 100644 --- a/docs/overview/support.md +++ b/docs/overview/support.md @@ -13,10 +13,7 @@ description: Support and Community Details and Links - [FAQ](../FAQ.md) - Frequently Asked Questions - [Code of Conduct](../../CODE_OF_CONDUCT.md) - This is how we roll - [Blog](https://backstage.io/blog/) - Announcements and updates -- [Newsletter](https://mailchi.mp/spotify/backstage-community) +- [Newsletter](https://mailchi.mp/spotify/backstage-community) - Subscribe to + our email newsletter - Give us a star ⭐️ - If you are using Backstage or think it is an interesting project, we would love a star ❤️ - -Or, if you are an open source developer and are interested in joining our team, -please reach out to -[foss-opportunities@spotify.com ](mailto:foss-opportunities@spotify.com) diff --git a/docs/overview/what-is-backstage.md b/docs/overview/what-is-backstage.md index 1a5e80a302..3d41c714ee 100644 --- a/docs/overview/what-is-backstage.md +++ b/docs/overview/what-is-backstage.md @@ -33,7 +33,14 @@ Out of the box, Backstage includes: [open source plugins](https://github.com/spotify/backstage/tree/master/plugins) that further expand Backstage’s customizability and functionality -### Benefits +## Backstage and the CNCF + +Backstage is a CNCF Sandbox project. Read the announcement +[here](https://backstage.io/blog/2020/09/23/backstage-cncf-sandbox). + + + +## Benefits - For _engineering managers_, it allows you to maintain standards and best practices across the organization, and can help you manage your whole tech diff --git a/microsite/core/Footer.js b/microsite/core/Footer.js index 761862c1fc..8e3c4c086d 100644 --- a/microsite/core/Footer.js +++ b/microsite/core/Footer.js @@ -51,6 +51,7 @@ class Footer extends React.Component { Subscribe to our newsletter + CNCF Sandbox
More
diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 8fa4935a38..e9993b2e8b 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -3,7 +3,6 @@ "Overview": [ "overview/what-is-backstage", "overview/architecture-overview", - "overview/architecture-terminology", "overview/roadmap", "overview/vision", "overview/background", diff --git a/mkdocs.yml b/mkdocs.yml index ba607f8a6e..f85044822e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -5,7 +5,6 @@ nav: - Overview: - What is Backstage?: 'overview/what-is-backstage.md' - Backstage architecture: 'overview/architecture-overview.md' - - Architecture and terminology: 'overview/architecture-terminology.md' - Roadmap: 'overview/roadmap.md' - Vision: 'overview/vision.md' - The Spotify story: 'overview/background.md' From 7cae75590962e2c36c85bced303b5b788bcd6b92 Mon Sep 17 00:00:00 2001 From: Julien Lind Date: Thu, 24 Sep 2020 16:26:30 +0200 Subject: [PATCH 33/38] Update README.md (#2591) API Path missing on non-local deployment --- plugins/auth-backend/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auth-backend/README.md b/plugins/auth-backend/README.md index 868d88b3d0..355fcc1536 100644 --- a/plugins/auth-backend/README.md +++ b/plugins/auth-backend/README.md @@ -35,7 +35,7 @@ Follow this link, [Create new OAuth App](https://github.com/settings/application 1. You can set the Homepage URL to whatever you want to. 1. The Authorization Callback URL should match the redirect URI set in Backstage. 1. Set this to `http://localhost:7000/api/auth/github` for local development. - 1. Set this to `http://{APP_FQDN}:{APP_BACKEND_PORT}/auth/github` for non-local deployments. + 1. Set this to `http://{APP_FQDN}:{APP_BACKEND_PORT}/api/auth/github` for non-local deployments. ```bash export AUTH_GITHUB_CLIENT_ID=x From b41ffc8c0fb3b3430700cb48a05e99a8f96a60c1 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Thu, 24 Sep 2020 17:26:34 +0200 Subject: [PATCH 34/38] add /api to catalog endpoint (#2595) --- plugins/techdocs-backend/src/service/router.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/techdocs-backend/src/service/router.ts b/plugins/techdocs-backend/src/service/router.ts index e0a066c168..14820da4e9 100644 --- a/plugins/techdocs-backend/src/service/router.ts +++ b/plugins/techdocs-backend/src/service/router.ts @@ -63,7 +63,7 @@ export async function createRouter({ const entity = (await ( await fetch( - `${baseUrl}/catalog/entities/by-name/${kind}/${namespace}/${name}`, + `${baseUrl}/api/catalog/entities/by-name/${kind}/${namespace}/${name}`, ) ).json()) as Entity; From b16e164815087f18c550a85ff333e7e3f3b492e9 Mon Sep 17 00:00:00 2001 From: Matthew Clarke Date: Thu, 24 Sep 2020 16:30:48 +0100 Subject: [PATCH 35/38] K8s add cluster lookup (#2571) * Kubernetes plugins boilerplate * pr feedback; check for k8s annotation * temp * fix config * add example local setup * dice rolelr * rever yarn lock update * forgotten prettier * prettier again * master lock * update lock with k8s deps * prettier * router tests * internal error * fix package.json * PR feedback * more pr feedback * rename component file * kubernetes fetcher tests * fix test * option constructor --- app-config.yaml | 4 + packages/backend/src/plugins/kubernetes.ts | 7 +- .../examples/dice-roller/README.md | 42 +++ .../examples/dice-roller/catalog-info.yaml | 13 + .../dice-roller/dice-roller-manifests.yaml | 79 ++++++ plugins/kubernetes-backend/package.json | 8 +- .../MultiTenantConfigClusterLocator.test.ts | 105 ++++++++ .../MultiTenantConfigClusterLocator.ts | 47 ++++ .../src/cluster-locator/types.ts | 17 ++ plugins/kubernetes-backend/src/index.ts | 1 + .../service/KubernetesClientProvider.test.ts | 136 ++++++++++ .../src/service/KubernetesClientProvider.ts | 96 +++++++ .../src/service/KubernetesFetcher.test.ts | 108 ++++++++ .../src/service/KubernetesFetcher.ts | 212 +++++++++++++++ ...ubernetesObjectsByServiceIdHandler.test.ts | 242 ++++++++++++++++++ .../getKubernetesObjectsByServiceIdHandler.ts | 69 +++++ .../src/service/router.test.ts | 87 +++++++ .../kubernetes-backend/src/service/router.ts | 67 ++++- .../src/service/standaloneApplication.ts | 4 +- plugins/kubernetes-backend/src/types/types.ts | 103 ++++++++ plugins/kubernetes/package.json | 1 + plugins/kubernetes/src/Router.tsx | 2 +- .../src/api/KubernetesBackendClient.ts | 5 +- plugins/kubernetes/src/api/types.ts | 3 +- .../KubernetesContent/KubernetesContent.tsx | 53 ++-- yarn.lock | 216 ++++++++++++++-- 26 files changed, 1671 insertions(+), 56 deletions(-) create mode 100644 plugins/kubernetes-backend/examples/dice-roller/README.md create mode 100644 plugins/kubernetes-backend/examples/dice-roller/catalog-info.yaml create mode 100644 plugins/kubernetes-backend/examples/dice-roller/dice-roller-manifests.yaml create mode 100644 plugins/kubernetes-backend/src/cluster-locator/MultiTenantConfigClusterLocator.test.ts create mode 100644 plugins/kubernetes-backend/src/cluster-locator/MultiTenantConfigClusterLocator.ts create mode 100644 plugins/kubernetes-backend/src/cluster-locator/types.ts create mode 100644 plugins/kubernetes-backend/src/service/KubernetesClientProvider.test.ts create mode 100644 plugins/kubernetes-backend/src/service/KubernetesClientProvider.ts create mode 100644 plugins/kubernetes-backend/src/service/KubernetesFetcher.test.ts create mode 100644 plugins/kubernetes-backend/src/service/KubernetesFetcher.ts create mode 100644 plugins/kubernetes-backend/src/service/getKubernetesObjectsByServiceIdHandler.test.ts create mode 100644 plugins/kubernetes-backend/src/service/getKubernetesObjectsByServiceIdHandler.ts create mode 100644 plugins/kubernetes-backend/src/service/router.test.ts create mode 100644 plugins/kubernetes-backend/src/types/types.ts diff --git a/app-config.yaml b/app-config.yaml index fca0a39496..01925c21dd 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -55,6 +55,10 @@ newrelic: lighthouse: baseUrl: http://localhost:3003 +kubernetes: + clusterLocatorMethod: 'configMultiTenant' + clusters: [] + catalog: rules: - allow: [Component, API, Group, Template, Location] diff --git a/packages/backend/src/plugins/kubernetes.ts b/packages/backend/src/plugins/kubernetes.ts index 42b7722ff6..7906765533 100644 --- a/packages/backend/src/plugins/kubernetes.ts +++ b/packages/backend/src/plugins/kubernetes.ts @@ -17,6 +17,9 @@ import { createRouter } from '@backstage/plugin-kubernetes-backend'; import { PluginEnvironment } from '../types'; -export default async function createPlugin({ logger }: PluginEnvironment) { - return await createRouter({ logger }); +export default async function createPlugin({ + logger, + config, +}: PluginEnvironment) { + return await createRouter({ logger, config }); } diff --git a/plugins/kubernetes-backend/examples/dice-roller/README.md b/plugins/kubernetes-backend/examples/dice-roller/README.md new file mode 100644 index 0000000000..b64e2df37f --- /dev/null +++ b/plugins/kubernetes-backend/examples/dice-roller/README.md @@ -0,0 +1,42 @@ +# Dice roller + +An app to roll dice (it doesn't actually do that). + +# Viewing in local Minikube running Backstage locally + +## Prerequisites + +- kubectl installed +- Minikube installed +- jq installed +- Backstage locally built and ready to run + +## Steps + +1. Start minikube +2. Get the Kubernetes master base url `kubectl cluster-info` +3. Apply manifests `kubectl apply -f dice-roller-manifests.yaml` +4. Get service account token (see below) +5. Start Backstage UI and backend +6. Register existing component in Backstage + - https://github.com/mclarke47/dice-roller/blob/master/catalog-info.yaml + +Update `app-config.yaml` as follows. + +```yaml +--- +kubernetes: + clusterLocatorMethod: 'configMultiTenant' + clusters: + - url: + name: minikube + serviceAccountToken: +``` + +### Getting the service account token + +``` +kubectl get secret DICE_ROLLER_TOKEN_NAME -o=json | jq -r '.data["token"]' | base64 --decode | pbcopy +``` + +Paste into `app-config.yaml` `kubernetes.clusters[].serviceAccountToken` diff --git a/plugins/kubernetes-backend/examples/dice-roller/catalog-info.yaml b/plugins/kubernetes-backend/examples/dice-roller/catalog-info.yaml new file mode 100644 index 0000000000..a944232224 --- /dev/null +++ b/plugins/kubernetes-backend/examples/dice-roller/catalog-info.yaml @@ -0,0 +1,13 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: dice-roller + description: It rolls dice + tags: + - go + annotations: + 'backstage.io/kubernetes-id': dice-roller +spec: + type: service + lifecycle: production + owner: guest diff --git a/plugins/kubernetes-backend/examples/dice-roller/dice-roller-manifests.yaml b/plugins/kubernetes-backend/examples/dice-roller/dice-roller-manifests.yaml new file mode 100644 index 0000000000..ef448c1688 --- /dev/null +++ b/plugins/kubernetes-backend/examples/dice-roller/dice-roller-manifests.yaml @@ -0,0 +1,79 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dice-roller + labels: + 'backstage.io/kubernetes-id': dice-roller +spec: + selector: + matchLabels: + app: dice-roller + replicas: 2 + template: + metadata: + labels: + app: dice-roller + 'backstage.io/kubernetes-id': dice-roller + spec: + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: dice-roller + namespace: default + labels: + 'backstage.io/kubernetes-id': dice-roller +data: + foo: bar + +--- +apiVersion: v1 +kind: Secret +metadata: + name: dice-roller + labels: + 'backstage.io/kubernetes-id': dice-roller +type: Opaque +data: + username: YWRtaW4= +--- +apiVersion: v1 +kind: Service +metadata: + name: dice-roller + labels: + 'backstage.io/kubernetes-id': dice-roller +spec: + selector: + app: dice-roller + ports: + - protocol: TCP + port: 80 + targetPort: 9376 + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: dice-roller +automountServiceAccountToken: false + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: sa-admin +roleRef: + kind: ClusterRole + name: cluster-admin + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: dice-roller + namespace: default diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 406d9c8d7f..e1f7a39592 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -21,7 +21,9 @@ }, "dependencies": { "@backstage/backend-common": "^0.1.1-alpha.23", + "@backstage/config": "^0.1.1-alpha.23", "@types/express": "^4.17.6", + "@kubernetes/client-node": "^0.12.1", "compression": "^1.7.4", "cors": "^2.8.5", "express": "^4.17.1", @@ -29,12 +31,14 @@ "fs-extra": "^9.0.0", "helmet": "^4.0.0", "morgan": "^1.10.0", + "stream-buffers": "^3.0.2", "winston": "^3.2.1", "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.23", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "supertest": "^4.0.2", + "@backstage/cli": "^0.1.1-alpha.23" }, "files": [ "dist" diff --git a/plugins/kubernetes-backend/src/cluster-locator/MultiTenantConfigClusterLocator.test.ts b/plugins/kubernetes-backend/src/cluster-locator/MultiTenantConfigClusterLocator.test.ts new file mode 100644 index 0000000000..34788d16cb --- /dev/null +++ b/plugins/kubernetes-backend/src/cluster-locator/MultiTenantConfigClusterLocator.test.ts @@ -0,0 +1,105 @@ +/* + * 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 '@backstage/backend-common'; +import { MultiTenantConfigClusterLocator } from './MultiTenantConfigClusterLocator'; +import { ConfigReader, Config } from '@backstage/config'; + +describe('MultiTenantConfigClusterLocator', () => { + it('empty clusters returns empty cluster details', async () => { + const config: Config = new ConfigReader( + { + clusters: [], + }, + 'ctx', + ); + + const sut = MultiTenantConfigClusterLocator.fromConfig( + config.getConfigArray('clusters'), + ); + + const result = await sut.getClusterByServiceId('ignored'); + + expect(result).toStrictEqual([]); + }); + + it('one clusters returns one cluster details', async () => { + const config: Config = new ConfigReader( + { + clusters: [ + { + name: 'cluster1', + url: 'http://localhost:8080', + }, + ], + }, + 'ctx', + ); + + const sut = MultiTenantConfigClusterLocator.fromConfig( + config.getConfigArray('clusters'), + ); + + const result = await sut.getClusterByServiceId('ignored'); + + expect(result).toStrictEqual([ + { + name: 'cluster1', + serviceAccountToken: undefined, + url: 'http://localhost:8080', + }, + ]); + }); + + it('two clusters returns two cluster details', async () => { + const config: Config = new ConfigReader( + { + clusters: [ + { + name: 'cluster1', + serviceAccountToken: undefined, + url: 'http://localhost:8080', + }, + { + name: 'cluster2', + serviceAccountToken: undefined, + url: 'http://localhost:8081', + }, + ], + }, + 'ctx', + ); + + const sut = MultiTenantConfigClusterLocator.fromConfig( + config.getConfigArray('clusters'), + ); + + const result = await sut.getClusterByServiceId('ignored'); + + expect(result).toStrictEqual([ + { + name: 'cluster1', + serviceAccountToken: undefined, + url: 'http://localhost:8080', + }, + { + name: 'cluster2', + serviceAccountToken: undefined, + url: 'http://localhost:8081', + }, + ]); + }); +}); diff --git a/plugins/kubernetes-backend/src/cluster-locator/MultiTenantConfigClusterLocator.ts b/plugins/kubernetes-backend/src/cluster-locator/MultiTenantConfigClusterLocator.ts new file mode 100644 index 0000000000..3d9e2dd8b4 --- /dev/null +++ b/plugins/kubernetes-backend/src/cluster-locator/MultiTenantConfigClusterLocator.ts @@ -0,0 +1,47 @@ +/* + * 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 { Config } from '@backstage/config'; +import { ClusterDetails, KubernetesClusterLocator } from '..'; + +// This cluster locator assumes that every service is located on every cluster +// Therefore it will always return all clusters in an app configuration file +export class MultiTenantConfigClusterLocator + implements KubernetesClusterLocator { + private readonly clusterDetails: ClusterDetails[]; + + constructor(clusterDetails: ClusterDetails[]) { + this.clusterDetails = clusterDetails; + } + + static fromConfig(config: Config[]): MultiTenantConfigClusterLocator { + return new MultiTenantConfigClusterLocator( + config.map(c => { + return { + name: c.getString('name'), + url: c.getString('url'), + serviceAccountToken: c.getOptionalString('serviceAccountToken'), + }; + }), + ); + } + + // As this implementation always returns all clusters serviceId is ignored here + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async getClusterByServiceId(_serviceId: string): Promise { + return this.clusterDetails; + } +} diff --git a/plugins/kubernetes-backend/src/cluster-locator/types.ts b/plugins/kubernetes-backend/src/cluster-locator/types.ts new file mode 100644 index 0000000000..9b793da558 --- /dev/null +++ b/plugins/kubernetes-backend/src/cluster-locator/types.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export type ClusterLocatorMethod = 'configMultiTenant' | 'http'; diff --git a/plugins/kubernetes-backend/src/index.ts b/plugins/kubernetes-backend/src/index.ts index 7612c392a2..96f070ff1e 100644 --- a/plugins/kubernetes-backend/src/index.ts +++ b/plugins/kubernetes-backend/src/index.ts @@ -15,3 +15,4 @@ */ export * from './service/router'; +export * from './types/types'; diff --git a/plugins/kubernetes-backend/src/service/KubernetesClientProvider.test.ts b/plugins/kubernetes-backend/src/service/KubernetesClientProvider.test.ts new file mode 100644 index 0000000000..a3dc67716a --- /dev/null +++ b/plugins/kubernetes-backend/src/service/KubernetesClientProvider.test.ts @@ -0,0 +1,136 @@ +/* + * 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 '@backstage/backend-common'; +import { KubernetesClientProvider } from './KubernetesClientProvider'; + +describe('KubernetesClientProvider', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('can get core client by cluster details', async () => { + const sut = new KubernetesClientProvider(); + + const mockGetKubeConfig = jest.fn(sut.getKubeConfig.bind({})); + + sut.getKubeConfig = mockGetKubeConfig; + + const result = sut.getCoreClientByClusterDetails({ + name: 'cluster-name', + url: 'http://localhost:9999', + serviceAccountToken: 'TOKEN', + }); + + expect(result.basePath).toBe('http://localhost:9999'); + // These fields aren't on the type but are there + const auth = (result as any).authentications.default; + expect(auth.users[0].token).toBe('TOKEN'); + expect(auth.clusters[0].name).toBe('cluster-name'); + + expect(mockGetKubeConfig.mock.calls.length).toBe(1); + }); + + it('can get cached core client by cluster details', async () => { + const sut = new KubernetesClientProvider(); + + const mockGetKubeConfig = jest.fn(sut.getKubeConfig.bind({})); + + sut.getKubeConfig = mockGetKubeConfig; + + const result1 = sut.getCoreClientByClusterDetails({ + name: 'cluster-name', + url: 'http://localhost:9999', + serviceAccountToken: 'TOKEN', + }); + + const result2 = sut.getCoreClientByClusterDetails({ + name: 'cluster-name', + url: 'http://localhost:9999', + serviceAccountToken: 'TOKEN', + }); + + expect(result1.basePath).toBe('http://localhost:9999'); + // These fields aren't on the type but are there + const auth1 = (result1 as any).authentications.default; + expect(auth1.users[0].token).toBe('TOKEN'); + expect(auth1.clusters[0].name).toBe('cluster-name'); + + expect(result2.basePath).toBe('http://localhost:9999'); + // These fields aren't on the type but are there + const auth2 = (result2 as any).authentications.default; + expect(auth2.users[0].token).toBe('TOKEN'); + expect(auth2.clusters[0].name).toBe('cluster-name'); + + expect(mockGetKubeConfig.mock.calls.length).toBe(1); + }); + + it('can get apps client by cluster details', async () => { + const sut = new KubernetesClientProvider(); + + const mockGetKubeConfig = jest.fn(sut.getKubeConfig.bind({})); + + sut.getKubeConfig = mockGetKubeConfig; + + const result = sut.getAppsClientByClusterDetails({ + name: 'cluster-name', + url: 'http://localhost:9999', + serviceAccountToken: 'TOKEN', + }); + + expect(result.basePath).toBe('http://localhost:9999'); + // These fields aren't on the type but are there + const auth = (result as any).authentications.default; + expect(auth.users[0].token).toBe('TOKEN'); + expect(auth.clusters[0].name).toBe('cluster-name'); + + expect(mockGetKubeConfig.mock.calls.length).toBe(1); + }); + + it('can get cached apps client by cluster details', async () => { + const sut = new KubernetesClientProvider(); + + const mockGetKubeConfig = jest.fn(sut.getKubeConfig.bind({})); + + sut.getKubeConfig = mockGetKubeConfig; + + const result1 = sut.getAppsClientByClusterDetails({ + name: 'cluster-name', + url: 'http://localhost:9999', + serviceAccountToken: 'TOKEN', + }); + + const result2 = sut.getAppsClientByClusterDetails({ + name: 'cluster-name', + url: 'http://localhost:9999', + serviceAccountToken: 'TOKEN', + }); + + expect(result1.basePath).toBe('http://localhost:9999'); + // These fields aren't on the type but are there + const auth1 = (result1 as any).authentications.default; + expect(auth1.users[0].token).toBe('TOKEN'); + expect(auth1.clusters[0].name).toBe('cluster-name'); + + expect(result2.basePath).toBe('http://localhost:9999'); + // These fields aren't on the type but are there + const auth2 = (result2 as any).authentications.default; + expect(auth2.users[0].token).toBe('TOKEN'); + expect(auth2.clusters[0].name).toBe('cluster-name'); + + expect(mockGetKubeConfig.mock.calls.length).toBe(1); + }); +}); diff --git a/plugins/kubernetes-backend/src/service/KubernetesClientProvider.ts b/plugins/kubernetes-backend/src/service/KubernetesClientProvider.ts new file mode 100644 index 0000000000..c3986e3ff7 --- /dev/null +++ b/plugins/kubernetes-backend/src/service/KubernetesClientProvider.ts @@ -0,0 +1,96 @@ +/* + * 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 { ClusterDetails } from '..'; +import { AppsV1Api, CoreV1Api, KubeConfig } from '@kubernetes/client-node'; + +export class KubernetesClientProvider { + private readonly coreClientMap: { + [key: string]: CoreV1Api; + }; + + private readonly appsClientMap: { + [key: string]: AppsV1Api; + }; + + constructor() { + this.coreClientMap = {}; + this.appsClientMap = {}; + } + + // visible for testing + getKubeConfig(clusterDetails: ClusterDetails) { + const cluster = { + name: clusterDetails.name, + server: clusterDetails.url, + // TODO configure this + skipTLSVerify: true, + }; + + // TODO configure + const user = { + name: 'service-account', + token: clusterDetails.serviceAccountToken, + }; + + const context = { + name: `${clusterDetails.name}`, + user: user.name, + cluster: cluster.name, + }; + + const kc = new KubeConfig(); + kc.loadFromOptions({ + clusters: [cluster], + users: [user], + contexts: [context], + currentContext: context.name, + }); + return kc; + } + + getCoreClientByClusterDetails(clusterDetails: ClusterDetails) { + const clientMapKey = clusterDetails.name; + + if (this.coreClientMap.hasOwnProperty(clientMapKey)) { + return this.coreClientMap[clientMapKey]; + } + + const kc = this.getKubeConfig(clusterDetails); + + const client = kc.makeApiClient(CoreV1Api); + + this.coreClientMap[clientMapKey] = client; + + return client; + } + + getAppsClientByClusterDetails(clusterDetails: ClusterDetails) { + const clientMapKey = clusterDetails.name; + + if (this.appsClientMap.hasOwnProperty(clientMapKey)) { + return this.appsClientMap[clientMapKey]; + } + + const kc = this.getKubeConfig(clusterDetails); + + const client = kc.makeApiClient(AppsV1Api); + + this.appsClientMap[clientMapKey] = client; + + return client; + } +} diff --git a/plugins/kubernetes-backend/src/service/KubernetesFetcher.test.ts b/plugins/kubernetes-backend/src/service/KubernetesFetcher.test.ts new file mode 100644 index 0000000000..359e7c5806 --- /dev/null +++ b/plugins/kubernetes-backend/src/service/KubernetesFetcher.test.ts @@ -0,0 +1,108 @@ +/* + * 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 { getVoidLogger } from '@backstage/backend-common'; +import { KubernetesClientBasedFetcher } from './KubernetesFetcher'; + +describe('KubernetesClientProvider', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should return pods, services', async () => { + const clientMock: any = { + listPodForAllNamespaces: jest.fn(), + listServiceForAllNamespaces: jest.fn(), + }; + + const kubernetesClientProvider: any = { + getCoreClientByClusterDetails: jest.fn(() => clientMock), + getAppsClientByClusterDetails: jest.fn(() => clientMock), + }; + + const sut = new KubernetesClientBasedFetcher({ + kubernetesClientProvider, + logger: getVoidLogger(), + }); + + clientMock.listPodForAllNamespaces.mockResolvedValueOnce({ + body: { + items: [ + { + metadata: { + name: 'pod-name', + }, + }, + ], + }, + }); + + clientMock.listServiceForAllNamespaces.mockResolvedValueOnce({ + body: { + items: [ + { + metadata: { + name: 'service-name', + }, + }, + ], + }, + }); + + const result = await sut.fetchObjectsByServiceId( + 'some-service', + { + name: 'cluster1', + url: 'http://localhost:9999', + serviceAccountToken: undefined, + }, + new Set(['pods', 'services']), + ); + + expect(result).toStrictEqual([ + { + type: 'pods', + resources: [ + { + metadata: { + name: 'pod-name', + }, + }, + ], + }, + { + type: 'services', + resources: [ + { + metadata: { + name: 'service-name', + }, + }, + ], + }, + ]); + + expect(clientMock.listPodForAllNamespaces.mock.calls.length).toBe(1); + expect(clientMock.listServiceForAllNamespaces.mock.calls.length).toBe(1); + + expect( + kubernetesClientProvider.getAppsClientByClusterDetails.mock.calls.length, + ).toBe(2); + expect( + kubernetesClientProvider.getCoreClientByClusterDetails.mock.calls.length, + ).toBe(2); + }); +}); diff --git a/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts b/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts new file mode 100644 index 0000000000..f6d3edb5e9 --- /dev/null +++ b/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts @@ -0,0 +1,212 @@ +/* + * 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 { + AppsV1Api, + CoreV1Api, + V1ConfigMap, + V1Deployment, + V1Pod, + V1ReplicaSet, + V1Secret, +} from '@kubernetes/client-node'; +import { KubernetesClientProvider } from './KubernetesClientProvider'; +import { V1Service } from '@kubernetes/client-node/dist/gen/model/v1Service'; +import { Logger } from 'winston'; +import { + KubernetesFetcher, + ClusterDetails, + KubernetesObjectTypes, + FetchResponse, +} from '..'; + +export interface Clients { + core: CoreV1Api; + apps: AppsV1Api; +} + +export interface KubernetesClientBasedFetcherOptions { + kubernetesClientProvider: KubernetesClientProvider; + logger: Logger; +} + +export class KubernetesClientBasedFetcher implements KubernetesFetcher { + private readonly kubernetesClientProvider: KubernetesClientProvider; + private readonly logger: Logger; + + constructor({ + kubernetesClientProvider, + logger, + }: KubernetesClientBasedFetcherOptions) { + this.kubernetesClientProvider = kubernetesClientProvider; + this.logger = logger; + } + + fetchObjectsByServiceId( + serviceId: string, + clusterDetails: ClusterDetails, + objectTypesToFetch: Set, + ): Promise { + return Promise.all( + Array.from(objectTypesToFetch).map(type => { + return this.fetchByObjectType(serviceId, clusterDetails, type); + }), + ); + } + + private fetchByObjectType( + serviceId: string, + clusterDetails: ClusterDetails, + type: KubernetesObjectTypes, + ): Promise { + switch (type) { + case 'pods': + return this.fetchPodsByServiceId(serviceId, clusterDetails).then(r => ({ + type: type, + resources: r, + })); + case 'configmaps': + return this.fetchConfigMapsByServiceId( + serviceId, + clusterDetails, + ).then(r => ({ type: type, resources: r })); + case 'deployments': + return this.fetchDeploymentsByServiceId( + serviceId, + clusterDetails, + ).then(r => ({ type: type, resources: r })); + case 'replicasets': + return this.fetchReplicaSetsByServiceId( + serviceId, + clusterDetails, + ).then(r => ({ type: type, resources: r })); + case 'secrets': + return this.fetchSecretsByServiceId( + serviceId, + clusterDetails, + ).then(r => ({ type: type, resources: r })); + case 'services': + return this.fetchServicesByServiceId( + serviceId, + clusterDetails, + ).then(r => ({ type: type, resources: r })); + default: + // unrecognised type + throw new Error(`unrecognised type=${type}`); + } + } + + private singleClusterFetch( + clusterDetails: ClusterDetails, + fn: (client: Clients) => Promise<{ body: { items: Array } }>, + ): Promise> { + const core = this.kubernetesClientProvider.getCoreClientByClusterDetails( + clusterDetails, + ); + const apps = this.kubernetesClientProvider.getAppsClientByClusterDetails( + clusterDetails, + ); + + this.logger.debug(`calling cluster=${clusterDetails.name}`); + return fn({ core, apps }).then(result => { + return result.body.items; + }); + } + + private fetchServicesByServiceId( + serviceId: string, + clusterDetails: ClusterDetails, + ): Promise> { + return this.singleClusterFetch(clusterDetails, ({ core }) => + core.listServiceForAllNamespaces( + false, + '', + '', + `backstage.io/kubernetes-id=${serviceId}`, + ), + ); + } + + private fetchPodsByServiceId( + serviceId: string, + clusterDetails: ClusterDetails, + ): Promise> { + return this.singleClusterFetch(clusterDetails, ({ core }) => + core.listPodForAllNamespaces( + false, + '', + '', + `backstage.io/kubernetes-id=${serviceId}`, + ), + ); + } + + private fetchConfigMapsByServiceId( + serviceId: string, + clusterDetails: ClusterDetails, + ): Promise> { + return this.singleClusterFetch(clusterDetails, ({ core }) => + core.listConfigMapForAllNamespaces( + false, + '', + '', + `backstage.io/kubernetes-id=${serviceId}`, + ), + ); + } + + private fetchSecretsByServiceId( + serviceId: string, + clusterDetails: ClusterDetails, + ): Promise> { + return this.singleClusterFetch(clusterDetails, ({ core }) => + core.listSecretForAllNamespaces( + false, + '', + '', + `backstage.io/kubernetes-id=${serviceId}`, + ), + ); + } + + private fetchDeploymentsByServiceId( + serviceId: string, + clusterDetails: ClusterDetails, + ): Promise> { + return this.singleClusterFetch(clusterDetails, ({ apps }) => + apps.listDeploymentForAllNamespaces( + false, + '', + '', + `backstage.io/kubernetes-id=${serviceId}`, + ), + ); + } + + private fetchReplicaSetsByServiceId( + serviceId: string, + clusterDetails: ClusterDetails, + ): Promise> { + return this.singleClusterFetch(clusterDetails, ({ apps }) => + apps.listReplicaSetForAllNamespaces( + false, + '', + '', + `backstage.io/kubernetes-id=${serviceId}`, + ), + ); + } +} diff --git a/plugins/kubernetes-backend/src/service/getKubernetesObjectsByServiceIdHandler.test.ts b/plugins/kubernetes-backend/src/service/getKubernetesObjectsByServiceIdHandler.test.ts new file mode 100644 index 0000000000..3f8081571a --- /dev/null +++ b/plugins/kubernetes-backend/src/service/getKubernetesObjectsByServiceIdHandler.test.ts @@ -0,0 +1,242 @@ +/* + * 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 { handleGetKubernetesObjectsByServiceId } from './getKubernetesObjectsByServiceIdHandler'; +import { getVoidLogger } from '@backstage/backend-common'; +import { ClusterDetails } from '..'; + +const TEST_SERVICE_ID = 'my-service'; + +const fetchObjectsByServiceId = jest.fn(); + +const getClusterByServiceId = jest.fn(); + +const mockFetch = (mock: jest.Mock) => { + mock.mockImplementation((serviceId: string, clusterDetails: ClusterDetails) => + Promise.resolve([ + { + type: 'pods', + resources: [ + { + metadata: { + name: `my-pods-${serviceId}-${clusterDetails.name}`, + }, + }, + ], + }, + { + type: 'configmaps', + resources: [ + { + metadata: { + name: `my-configmaps-${serviceId}-${clusterDetails.name}`, + }, + }, + ], + }, + { + type: 'services', + resources: [ + { + metadata: { + name: `my-services-${serviceId}-${clusterDetails.name}`, + }, + }, + ], + }, + ]), + ); +}; + +describe('handleGetKubernetesObjectsByServiceId', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('retrieve objects for one cluster', async () => { + getClusterByServiceId.mockImplementation(() => + Promise.resolve([ + { + name: 'test-cluster', + }, + ]), + ); + + mockFetch(fetchObjectsByServiceId); + + const result = await handleGetKubernetesObjectsByServiceId( + TEST_SERVICE_ID, + { + fetchObjectsByServiceId, + }, + { + getClusterByServiceId, + }, + getVoidLogger(), + ); + + expect(getClusterByServiceId.mock.calls.length).toBe(1); + expect(fetchObjectsByServiceId.mock.calls.length).toBe(1); + expect(result).toStrictEqual({ + items: [ + { + cluster: { + name: 'test-cluster', + }, + resources: [ + { + resources: [ + { + metadata: { + name: 'my-pods-my-service-test-cluster', + }, + }, + ], + type: 'pods', + }, + { + resources: [ + { + metadata: { + name: 'my-configmaps-my-service-test-cluster', + }, + }, + ], + type: 'configmaps', + }, + { + resources: [ + { + metadata: { + name: 'my-services-my-service-test-cluster', + }, + }, + ], + type: 'services', + }, + ], + }, + ], + }); + }); + + it('retrieve objects for two clusters', async () => { + getClusterByServiceId.mockImplementation(() => + Promise.resolve([ + { + name: 'test-cluster', + }, + { + name: 'other-cluster', + }, + ]), + ); + + mockFetch(fetchObjectsByServiceId); + + const result = await handleGetKubernetesObjectsByServiceId( + TEST_SERVICE_ID, + { + fetchObjectsByServiceId, + }, + { + getClusterByServiceId, + }, + getVoidLogger(), + ); + + expect(getClusterByServiceId.mock.calls.length).toBe(1); + expect(fetchObjectsByServiceId.mock.calls.length).toBe(2); + expect(result).toStrictEqual({ + items: [ + { + cluster: { + name: 'test-cluster', + }, + resources: [ + { + resources: [ + { + metadata: { + name: 'my-pods-my-service-test-cluster', + }, + }, + ], + type: 'pods', + }, + { + resources: [ + { + metadata: { + name: 'my-configmaps-my-service-test-cluster', + }, + }, + ], + type: 'configmaps', + }, + { + resources: [ + { + metadata: { + name: 'my-services-my-service-test-cluster', + }, + }, + ], + type: 'services', + }, + ], + }, + { + cluster: { + name: 'other-cluster', + }, + resources: [ + { + resources: [ + { + metadata: { + name: 'my-pods-my-service-other-cluster', + }, + }, + ], + type: 'pods', + }, + { + resources: [ + { + metadata: { + name: 'my-configmaps-my-service-other-cluster', + }, + }, + ], + type: 'configmaps', + }, + { + resources: [ + { + metadata: { + name: 'my-services-my-service-other-cluster', + }, + }, + ], + type: 'services', + }, + ], + }, + ], + }); + }); +}); diff --git a/plugins/kubernetes-backend/src/service/getKubernetesObjectsByServiceIdHandler.ts b/plugins/kubernetes-backend/src/service/getKubernetesObjectsByServiceIdHandler.ts new file mode 100644 index 0000000000..52968d4c20 --- /dev/null +++ b/plugins/kubernetes-backend/src/service/getKubernetesObjectsByServiceIdHandler.ts @@ -0,0 +1,69 @@ +/* + * 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 { + KubernetesClusterLocator, + KubernetesFetcher, + KubernetesObjectTypes, + ObjectsByServiceIdResponse, +} from '..'; + +export type GetKubernetesObjectsByServiceIdHandler = ( + serviceId: string, + fetcher: KubernetesFetcher, + clusterLocator: KubernetesClusterLocator, + logger: Logger, + objectsToFetch?: Set, +) => Promise; + +const DEFAULT_OBJECTS = new Set([ + 'pods', + 'services', + 'configmaps', + 'secrets', + 'deployments', + 'replicasets', +]); + +export const handleGetKubernetesObjectsByServiceId: GetKubernetesObjectsByServiceIdHandler = async ( + serviceId, + fetcher, + clusterLocator, + logger, + objectsToFetch = DEFAULT_OBJECTS, +) => { + const clusterDetails = await clusterLocator.getClusterByServiceId(serviceId); + + logger.info( + `serviceId=${serviceId} clusterDetails=${clusterDetails.map(c => c.name)}`, + ); + + return Promise.all( + clusterDetails.map(cd => { + return fetcher + .fetchObjectsByServiceId(serviceId, cd, objectsToFetch) + .then(result => { + return { + cluster: { + name: cd.name, + }, + resources: result, + }; + }); + }), + ).then(r => ({ items: r })); +}; diff --git a/plugins/kubernetes-backend/src/service/router.test.ts b/plugins/kubernetes-backend/src/service/router.test.ts new file mode 100644 index 0000000000..7731fe9abb --- /dev/null +++ b/plugins/kubernetes-backend/src/service/router.test.ts @@ -0,0 +1,87 @@ +/* + * 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 { getVoidLogger } from '@backstage/backend-common'; +import express from 'express'; +import request from 'supertest'; +import { makeRouter } from './router'; +import { + KubernetesClusterLocator, + KubernetesFetcher, + ObjectsByServiceIdResponse, +} from '..'; + +describe('router', () => { + let app: express.Express; + let kubernetesFetcher: jest.Mocked; + let kubernetesClusterLocator: jest.Mocked; + let handleGetByServiceId: jest.Mock>; + + beforeAll(async () => { + kubernetesFetcher = { + fetchObjectsByServiceId: jest.fn(), + }; + + kubernetesClusterLocator = { + getClusterByServiceId: jest.fn(), + }; + + handleGetByServiceId = jest.fn(); + + const router = makeRouter( + getVoidLogger(), + kubernetesFetcher, + kubernetesClusterLocator, + handleGetByServiceId as any, + ); + app = express().use(router); + }); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + describe('GET /services/:serviceId', () => { + it('happy path: lists kubernetes objects', async () => { + const result = { + clusterOne: { + pods: [ + { + metadata: { + name: 'pod1', + }, + }, + ], + }, + } as any; + handleGetByServiceId.mockReturnValueOnce(Promise.resolve(result)); + + const response = await request(app).get('/services/test-service'); + + expect(response.status).toEqual(200); + expect(response.body).toEqual(result); + }); + + it('internal error: lists kubernetes objects', async () => { + handleGetByServiceId.mockRejectedValue(Error('some internal error')); + + const response = await request(app).get('/services/test-service'); + + expect(response.status).toEqual(500); + expect(response.body).toEqual({ error: 'some internal error' }); + }); + }); +}); diff --git a/plugins/kubernetes-backend/src/service/router.ts b/plugins/kubernetes-backend/src/service/router.ts index b200896887..d038c1ed7d 100644 --- a/plugins/kubernetes-backend/src/service/router.ts +++ b/plugins/kubernetes-backend/src/service/router.ts @@ -17,19 +17,65 @@ import express from 'express'; import Router from 'express-promise-router'; import { Logger } from 'winston'; +import { Config } from '@backstage/config'; +import { ClusterLocatorMethod } from '../cluster-locator/types'; +import { MultiTenantConfigClusterLocator } from '../cluster-locator/MultiTenantConfigClusterLocator'; +import { KubernetesClientBasedFetcher } from './KubernetesFetcher'; +import { KubernetesClientProvider } from './KubernetesClientProvider'; +import { + GetKubernetesObjectsByServiceIdHandler, + handleGetKubernetesObjectsByServiceId, +} from './getKubernetesObjectsByServiceIdHandler'; +import { KubernetesClusterLocator, KubernetesFetcher } from '..'; export interface RouterOptions { logger: Logger; + config: Config; } -const makeRouter = (logger: Logger): express.Router => { +const getClusterLocator = (config: Config): KubernetesClusterLocator => { + const clusterLocatorMethod = config.getString( + 'kubernetes.clusterLocatorMethod', + ) as ClusterLocatorMethod; + + switch (clusterLocatorMethod) { + case 'configMultiTenant': + return MultiTenantConfigClusterLocator.fromConfig( + config.getConfigArray('kubernetes.clusters'), + ); + case 'http': + throw new Error('not implemented'); + default: + throw new Error( + `Unsupported kubernetes.clusterLocatorMethod "${clusterLocatorMethod}"`, + ); + } +}; + +export const makeRouter = ( + logger: Logger, + fetcher: KubernetesFetcher, + clusterLocator: KubernetesClusterLocator, + handleGetByServiceId: GetKubernetesObjectsByServiceIdHandler, +): express.Router => { const router = Router(); router.use(express.json()); + // TODO error handling router.get('/services/:serviceId', async (req, res) => { const serviceId = req.params.serviceId; - logger.info(`HERE ${serviceId}`); - res.send({ serviceId }); + + try { + const response = await handleGetByServiceId( + serviceId, + fetcher, + clusterLocator, + logger, + ); + res.send(response); + } catch (e) { + res.status(500).send({ error: e.message }); + } }); return router; @@ -41,5 +87,18 @@ export async function createRouter( const logger = options.logger; logger.info('Initializing Kubernetes backend'); - return makeRouter(logger); + + const clusterLocator = getClusterLocator(options.config); + + const fetcher = new KubernetesClientBasedFetcher({ + kubernetesClientProvider: new KubernetesClientProvider(), + logger, + }); + + return makeRouter( + logger, + fetcher, + clusterLocator, + handleGetKubernetesObjectsByServiceId, + ); } diff --git a/plugins/kubernetes-backend/src/service/standaloneApplication.ts b/plugins/kubernetes-backend/src/service/standaloneApplication.ts index 5eea4fb8f5..e23e87a7a2 100644 --- a/plugins/kubernetes-backend/src/service/standaloneApplication.ts +++ b/plugins/kubernetes-backend/src/service/standaloneApplication.ts @@ -25,6 +25,7 @@ import express from 'express'; import helmet from 'helmet'; import { Logger } from 'winston'; import { createRouter } from './router'; +import { ConfigReader } from '@backstage/config'; export interface ApplicationOptions { enableCors: boolean; @@ -35,6 +36,7 @@ export async function createStandaloneApplication( options: ApplicationOptions, ): Promise { const { enableCors, logger } = options; + const config = ConfigReader.fromConfigs([]); const app = express(); app.use(helmet()); @@ -44,7 +46,7 @@ export async function createStandaloneApplication( app.use(compression()); app.use(express.json()); app.use(requestLoggingHandler()); - app.use('/', await createRouter({ logger })); + app.use('/', await createRouter({ logger, config })); app.use(notFoundHandler()); app.use(errorHandler()); diff --git a/plugins/kubernetes-backend/src/types/types.ts b/plugins/kubernetes-backend/src/types/types.ts new file mode 100644 index 0000000000..2c69fd67a0 --- /dev/null +++ b/plugins/kubernetes-backend/src/types/types.ts @@ -0,0 +1,103 @@ +/* + * 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 { + V1ConfigMap, + V1Deployment, + V1Pod, + V1ReplicaSet, + V1Secret, + V1Service, +} from '@kubernetes/client-node'; + +export interface ClusterDetails { + name: string; + url: string; + // TODO this will eventually be configured by the auth translation work + serviceAccountToken: string | undefined; +} + +export interface ClusterObjects { + cluster: { name: string }; + resources: FetchResponse[]; +} + +export interface ObjectsByServiceIdResponse { + items: ClusterObjects[]; +} + +export type FetchResponse = + | PodFetchResponse + | ServiceFetchResponse + | ConfigMapFetchResponse + | SecretFetchResponse + | DeploymentFetchResponse + | ReplicaSetsFetchResponse; + +// TODO fairly sure there's a easier way to do this + +export type KubernetesObjectTypes = + | 'pods' + | 'services' + | 'configmaps' + | 'secrets' + | 'deployments' + | 'replicasets'; + +export interface PodFetchResponse { + type: 'pods'; + resources: Array; +} + +export interface ServiceFetchResponse { + type: 'services'; + resources: Array; +} + +export interface ConfigMapFetchResponse { + type: 'configmaps'; + resources: Array; +} + +export interface SecretFetchResponse { + type: 'secrets'; + resources: Array; +} + +export interface DeploymentFetchResponse { + type: 'deployments'; + resources: Array; +} + +export interface ReplicaSetsFetchResponse { + type: 'replicasets'; + resources: Array; +} + +// Fetches information from a kubernetes cluster using the cluster details object +// to target a specific cluster +export interface KubernetesFetcher { + fetchObjectsByServiceId( + serviceId: string, + clusterDetails: ClusterDetails, + objectTypesToFetch: Set, + ): Promise; +} + +// Used to locate which cluster(s) a service is running on +export interface KubernetesClusterLocator { + getClusterByServiceId(serviceId: string): Promise; +} diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index ffe6207680..d1f3d5e7ba 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -22,6 +22,7 @@ "dependencies": { "@backstage/catalog-model": "^0.1.1-alpha.23", "@backstage/core": "^0.1.1-alpha.23", + "@backstage/plugin-kubernetes-backend": "^0.1.1-alpha.23", "@backstage/theme": "^0.1.1-alpha.23", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", diff --git a/plugins/kubernetes/src/Router.tsx b/plugins/kubernetes/src/Router.tsx index 994788bf72..41f534fbef 100644 --- a/plugins/kubernetes/src/Router.tsx +++ b/plugins/kubernetes/src/Router.tsx @@ -22,7 +22,7 @@ import { rootCatalogKubernetesRouteRef } from './plugin'; import { KubernetesContent } from './components/KubernetesContent'; import { WarningPanel } from '@backstage/core'; -const KUBERNETES_ANNOTATION = 'backstage.io/kubernetes'; +const KUBERNETES_ANNOTATION = 'backstage.io/kubernetes-id'; export const Router = ({ entity }: { entity: Entity }) => { const kubernetesAnnotationValue = diff --git a/plugins/kubernetes/src/api/KubernetesBackendClient.ts b/plugins/kubernetes/src/api/KubernetesBackendClient.ts index fed3cd2ed0..cbb3b03a38 100644 --- a/plugins/kubernetes/src/api/KubernetesBackendClient.ts +++ b/plugins/kubernetes/src/api/KubernetesBackendClient.ts @@ -16,6 +16,7 @@ import { DiscoveryApi } from '@backstage/core'; import { KubernetesApi } from './types'; +import { ObjectsByServiceIdResponse } from '@backstage/plugin-kubernetes-backend'; export class KubernetesBackendClient implements KubernetesApi { private readonly discoveryApi: DiscoveryApi; @@ -37,7 +38,9 @@ export class KubernetesBackendClient implements KubernetesApi { return await response.json(); } - async getObjectsByServiceId(serviceId: String): Promise<{}> { + async getObjectsByServiceId( + serviceId: String, + ): Promise { return await this.getRequired(`/services/${serviceId}`); } } diff --git a/plugins/kubernetes/src/api/types.ts b/plugins/kubernetes/src/api/types.ts index 2ad6e567f9..5ec3cda6f8 100644 --- a/plugins/kubernetes/src/api/types.ts +++ b/plugins/kubernetes/src/api/types.ts @@ -15,6 +15,7 @@ */ import { createApiRef } from '@backstage/core'; +import { ObjectsByServiceIdResponse } from '@backstage/plugin-kubernetes-backend'; export const kubernetesApiRef = createApiRef({ id: 'plugin.kubernetes.service', @@ -23,5 +24,5 @@ export const kubernetesApiRef = createApiRef({ }); export interface KubernetesApi { - getObjectsByServiceId(serviceId: String): Promise<{}>; + getObjectsByServiceId(serviceId: String): Promise; } diff --git a/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx b/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx index d79454a5d1..bbcef6a0d0 100644 --- a/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx +++ b/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx @@ -15,25 +15,19 @@ */ import React, { FC, useEffect, useState } from 'react'; -import { Typography, Grid } from '@material-ui/core'; -import { - InfoCard, - Page, - pageTheme, - Content, - ContentHeader, - useApi, -} from '@backstage/core'; +import { Grid } from '@material-ui/core'; +import { InfoCard, Page, pageTheme, Content, useApi } from '@backstage/core'; import { Entity } from '@backstage/catalog-model'; import { kubernetesApiRef } from '../../api/types'; +import { ObjectsByServiceIdResponse } from '@backstage/plugin-kubernetes-backend'; // TODO this is a temporary component used to construct the Kubernetes plugin boilerplate export const KubernetesContent: FC<{ entity: Entity }> = ({ entity }) => { const kubernetesApi = useApi(kubernetesApiRef); - const [kubernetesObjects, setKubernetesObjects] = useState<{} | undefined>( - undefined, - ); + const [kubernetesObjects, setKubernetesObjects] = useState< + ObjectsByServiceIdResponse | undefined + >(undefined); const [error, setError] = useState(undefined); useEffect(() => { @@ -50,21 +44,28 @@ export const KubernetesContent: FC<{ entity: Entity }> = ({ entity }) => { return ( - - - - - {kubernetesObjects === undefined &&
loading....
} - {error !== undefined &&
{error}
} - {kubernetesObjects !== undefined && ( -
- backend response: {JSON.stringify(kubernetesObjects)} -
- )} -
-
-
+ {kubernetesObjects === undefined &&
loading....
} + {error !== undefined &&
{error}
} + {kubernetesObjects !== undefined && ( +
+ {kubernetesObjects.items.map((item, i) => ( + + + {item.resources.map((fr, j) => ( +
+
+ {fr.type}:{' '} + {(fr.resources as any) + .map((v: any) => v.metadata.name) + .join(' ')} +
+ ))} +
+
+ ))} +
+ )}
diff --git a/yarn.lock b/yarn.lock index 0b6a8c83f4..46402a9815 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2441,6 +2441,34 @@ resolved "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== +"@kubernetes/client-node@^0.12.1": + version "0.12.2" + resolved "https://registry.npmjs.org/@kubernetes/client-node/-/client-node-0.12.2.tgz#8728684bd57d1cbcbe14fe742e79be7021403eea" + integrity sha512-J0UwyFl1Iv/IZ6WMP7LaizBEoKPnqwtc8tIO2q/X+EuDT7eGpPPAMHXSEOC/EI9JGIf0FaJEcDHhB/Dio/mKhw== + dependencies: + "@types/js-yaml" "^3.12.1" + "@types/node" "^10.12.0" + "@types/request" "^2.47.1" + "@types/stream-buffers" "^3.0.3" + "@types/tar" "^4.0.3" + "@types/underscore" "^1.8.9" + "@types/ws" "^6.0.1" + byline "^5.0.0" + execa "1.0.0" + isomorphic-ws "^4.0.1" + js-yaml "^3.13.1" + jsonpath-plus "^0.19.0" + openid-client "^4.1.1" + request "^2.88.0" + rfc4648 "^1.3.0" + shelljs "^0.8.2" + stream-buffers "^3.0.2" + tar "^6.0.2" + tmp-promise "^3.0.2" + tslib "^1.9.3" + underscore "^1.9.1" + ws "^7.3.1" + "@kyleshockey/object-assign-deep@^0.4.2": version "0.4.2" resolved "https://registry.npmjs.org/@kyleshockey/object-assign-deep/-/object-assign-deep-0.4.2.tgz#84900f0eefc372798f4751b5262830b8208922ec" @@ -4638,6 +4666,11 @@ "@types/node" "*" "@types/responselike" "*" +"@types/caseless@*": + version "0.12.2" + resolved "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8" + integrity sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w== + "@types/classnames@^2.2.9": version "2.2.10" resolved "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.10.tgz#cc658ca319b6355399efc1f5b9e818f1a24bf999" @@ -4995,7 +5028,7 @@ resolved "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.6.tgz#f1a1cb35aff47bc5cfb05cb0c441ca91e914c26f" integrity sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw== -"@types/js-yaml@^3.12.5": +"@types/js-yaml@^3.12.1", "@types/js-yaml@^3.12.5": version "3.12.5" resolved "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.5.tgz#136d5e6a57a931e1cce6f9d8126aa98a9c92a6bb" integrity sha512-JCcp6J0GV66Y4ZMDAQCXot4xprYB+Zfd3meK9+INSJeVZwJmHAW30BBEEkPzXswMXuiyReUGOP3GxrADc9wPww== @@ -5139,6 +5172,11 @@ resolved "https://registry.npmjs.org/@types/node/-/node-10.17.28.tgz#0e36d718a29355ee51cec83b42d921299200f6d9" integrity sha512-dzjES1Egb4c1a89C7lKwQh8pwjYmlOAG9dW1pBgxEk57tMrLnssOfEthz8kdkNaBd7lIqQx7APm5+mZ619IiCQ== +"@types/node@^10.12.0": + version "10.17.35" + resolved "https://registry.npmjs.org/@types/node/-/node-10.17.35.tgz#58058f29b870e6ae57b20e4f6e928f02b7129f56" + integrity sha512-gXx7jAWpMddu0f7a+L+txMplp3FnHl53OhQIF9puXKq3hDGY/GjH+MF04oWnV/adPSCrbtHumDCFwzq2VhltWA== + "@types/node@^12.0.0": version "12.12.53" resolved "https://registry.npmjs.org/@types/node/-/node-12.12.53.tgz#be0d375933c3d15ef2380dafb3b0350ea7021129" @@ -5387,6 +5425,16 @@ resolved "https://registry.npmjs.org/@types/relateurl/-/relateurl-0.2.28.tgz#6bda7db8653fa62643f5ee69e9f69c11a392e3a6" integrity sha1-a9p9uGU/piZD9e5p6facEaOS46Y= +"@types/request@^2.47.1": + version "2.48.5" + resolved "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz#019b8536b402069f6d11bee1b2c03e7f232937a0" + integrity sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ== + dependencies: + "@types/caseless" "*" + "@types/node" "*" + "@types/tough-cookie" "*" + form-data "^2.5.0" + "@types/resolve@1.17.1": version "1.17.1" resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" @@ -5472,6 +5520,13 @@ dependencies: "@types/node" "*" +"@types/stream-buffers@^3.0.3": + version "3.0.3" + resolved "https://registry.npmjs.org/@types/stream-buffers/-/stream-buffers-3.0.3.tgz#34e565bf64e3e4bdeee23fd4aa58d4636014a02b" + integrity sha512-NeFeX7YfFZDYsCfbuaOmFQ0OjSmHreKBpp7MQ4alWQBHeh2USLsj7qyMyn9t82kjqIX516CR/5SRHnARduRtbQ== + dependencies: + "@types/node" "*" + "@types/styled-jsx@^2.2.8": version "2.2.8" resolved "https://registry.npmjs.org/@types/styled-jsx/-/styled-jsx-2.2.8.tgz#b50d13d8a3c34036282d65194554cf186bab7234" @@ -5550,6 +5605,11 @@ dependencies: "@types/node" "*" +"@types/tough-cookie@*": + version "4.0.0" + resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d" + integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A== + "@types/uglify-js@*": version "3.0.4" resolved "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.0.4.tgz#96beae23df6f561862a830b4288a49e86baac082" @@ -5557,6 +5617,11 @@ dependencies: source-map "^0.6.1" +"@types/underscore@^1.8.9": + version "1.10.23" + resolved "https://registry.npmjs.org/@types/underscore/-/underscore-1.10.23.tgz#cc672e8864000d288e1e39c609fd9cab84391ff3" + integrity sha512-vX1NPekXhrLquFWskH2thcvFAha187F/lM6xYOoEMZWwJ/6alSk0/ttmGP/YRqcqtCv0TMbZjYAdZyHAEcuU4g== + "@types/uuid@^8.0.0": version "8.0.0" resolved "https://registry.npmjs.org/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0" @@ -5618,6 +5683,13 @@ resolved "https://registry.npmjs.org/@types/whatwg-streams/-/whatwg-streams-0.0.7.tgz#28bfe73dc850562296367249c4b32a50db81e9d3" integrity sha512-6sDiSEP6DWcY2ZolsJ2s39ZmsoGQ7KVwBDI3sESQsEm9P2dHTcqnDIHRZFRNtLCzWp7hCFGqYbw5GyfpQnJ01A== +"@types/ws@^6.0.1": + version "6.0.4" + resolved "https://registry.npmjs.org/@types/ws/-/ws-6.0.4.tgz#7797707c8acce8f76d8c34b370d4645b70421ff1" + integrity sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg== + dependencies: + "@types/node" "*" + "@types/ws@^7.0.0": version "7.2.6" resolved "https://registry.npmjs.org/@types/ws/-/ws-7.2.6.tgz#516cbfb818310f87b43940460e065eb912a4178d" @@ -7156,7 +7228,7 @@ base64-js@^1.0.2, base64-js@^1.2.0: resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== -base64url@3.x.x: +base64url@3.x.x, base64url@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== @@ -10630,6 +10702,19 @@ exec-sh@^0.3.2: resolved "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5" integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A== +execa@1.0.0, execa@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + execa@3.4.0: version "3.4.0" resolved "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" @@ -10659,19 +10744,6 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^4.0.0, execa@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/execa/-/execa-4.0.2.tgz#ad87fb7b2d9d564f70d2b62d511bee41d5cbb240" @@ -11337,7 +11409,7 @@ fork-ts-checker-webpack-plugin@^4.0.5, fork-ts-checker-webpack-plugin@^4.1.4: tapable "^1.0.0" worker-rpc "^0.1.0" -form-data@^2.3.1, form-data@^2.3.2: +form-data@^2.3.1, form-data@^2.3.2, form-data@^2.5.0: version "2.5.1" resolved "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== @@ -11982,6 +12054,23 @@ got@^11.5.2: p-cancelable "^2.0.0" responselike "^2.0.0" +got@^11.6.2: + version "11.7.0" + resolved "https://registry.npmjs.org/got/-/got-11.7.0.tgz#a386360305571a74548872e674932b4ef70d3b24" + integrity sha512-7en2XwH2MEqOsrK0xaKhbWibBoZqy+f1RSUoIeF1BLcnf+pyQdDsljWMfmOh+QKJwuvDIiKx38GtPh5wFdGGjg== + dependencies: + "@sindresorhus/is" "^3.1.1" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.1" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + got@^9.6.0: version "9.6.0" resolved "https://registry.npmjs.org/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -13604,6 +13693,11 @@ isomorphic-form-data@~2.0.0: dependencies: form-data "^2.3.2" +isomorphic-ws@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" + integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== + isstream@~0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -14110,6 +14204,13 @@ jose@^1.27.1: dependencies: "@panva/asn1.js" "^1.0.0" +jose@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/jose/-/jose-2.0.2.tgz#fb22385b80c658cc7a0cae05b7086c04c6be49f4" + integrity sha512-yD93lsiMA1go/qxSY/vXWBodmIZJIxeB7QhFi8z1yQ3KUwKENqI9UA8VCHlQ5h3x1zWuWZjoY87ByQzkQbIrQg== + dependencies: + "@panva/asn1.js" "^1.0.0" + js-cookie@^2.2.1: version "2.2.1" resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" @@ -14402,6 +14503,11 @@ jsonparse@^1.2.0: resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= +jsonpath-plus@^0.19.0: + version "0.19.0" + resolved "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-0.19.0.tgz#b901e57607055933dc9a8bef0cc25160ee9dd64c" + integrity sha512-GSVwsrzW9LsA5lzsqe4CkuZ9wp+kxBb2GwNniaWzI2YFn5Ig42rSW8ZxVpWXaAfakXNrx5pgY5AbQq7kzX29kg== + jsonpointer@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" @@ -15297,7 +15403,7 @@ make-dir@^3.0.0, make-dir@^3.0.2: dependencies: semver "^6.0.0" -make-error@1.x, make-error@^1.1.1: +make-error@1.x, make-error@^1.1.1, make-error@^1.3.6: version "1.3.6" resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -16500,6 +16606,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-hash@^2.0.1: + version "2.0.3" + resolved "https://registry.npmjs.org/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" + integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== + object-inspect@^1.7.0: version "1.7.0" resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" @@ -16615,6 +16726,11 @@ octokit-pagination-methods@^1.1.0: resolved "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz#cf472edc9d551055f9ef73f6e42b4dbb4c80bea4" integrity sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ== +oidc-token-hash@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.0.tgz#acdfb1f4310f58e64d5d74a4e8671a426986e888" + integrity sha512-8Yr4CZSv+Tn8ZkN3iN2i2w2G92mUKClp4z7EGUfdsERiYSbj7P4i/NHm72ft+aUdsiFx9UdIPSTwbyzQ6C4URg== + omggif@1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/omggif/-/omggif-1.0.7.tgz#59d2eecb0263de84635b3feb887c0c9973f1e49d" @@ -16683,6 +16799,20 @@ opencollective-postinstall@^2.0.2: resolved "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz#5657f1bede69b6e33a45939b061eb53d3c6c3a89" integrity sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw== +openid-client@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/openid-client/-/openid-client-4.1.1.tgz#3e8a25584c4292e9b9b03e60358f5549fb85197a" + integrity sha512-/qch3I3v8UtO0A7wVgyXJJjGX/knR8bv06DQpLuKQqLG5u4AHcgusGuVKPKAcneLZvHKbKovF2+3e2ngXyuudA== + dependencies: + base64url "^3.0.1" + got "^11.6.2" + jose "^2.0.2" + lru-cache "^6.0.0" + make-error "^1.3.6" + object-hash "^2.0.1" + oidc-token-hash "^5.0.0" + p-any "^3.0.0" + opn@^5.5.0: version "5.5.0" resolved "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" @@ -16788,6 +16918,14 @@ overlayscrollbars@^1.10.2: resolved "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.13.0.tgz#1edb436328133b94877b558f77966d5497ca36a7" integrity sha512-p8oHrMeRAKxXDMPI/EBNITj/zTVHKNnAnM59Im+xnoZUlV07FyTg46wom2286jJlXGGfcPFG/ba5NUiCwWNd4w== +p-any@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/p-any/-/p-any-3.0.0.tgz#79847aeed70b5d3a10ea625296c0c3d2e90a87b9" + integrity sha512-5rqbqfsRWNb0sukt0awwgJMlaep+8jV45S15SKKB34z4UuzjcofIfnriCBhWjZP2jbVtjt9yRl7buB6RlKsu9w== + dependencies: + p-cancelable "^2.0.0" + p-some "^5.0.0" + p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -16934,6 +17072,14 @@ p-retry@^3.0.1: dependencies: retry "^0.12.0" +p-some@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/p-some/-/p-some-5.0.0.tgz#8b730c74b4fe5169d7264a240ad010b6ebc686a4" + integrity sha512-Js5XZxo6vHjB9NOYAzWDYAIyyiPvva0DWESAIWIK7uhSpGsyg5FwUPxipU/SOQx5x9EqhOh545d1jo6cVkitig== + dependencies: + aggregate-error "^3.0.0" + p-cancelable "^2.0.0" + p-timeout@^3.1.0: version "3.2.0" resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" @@ -19764,6 +19910,11 @@ reusify@^1.0.4: resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rfc4648@^1.3.0: + version "1.4.0" + resolved "https://registry.npmjs.org/rfc4648/-/rfc4648-1.4.0.tgz#c75b2856ad2e2d588b6ddb985d556f1f7f2a2abd" + integrity sha512-3qIzGhHlMHA6PoT6+cdPKZ+ZqtxkIvg8DZGKA5z6PQ33/uuhoJ+Ws/D/J9rXW6gXodgH8QYlz2UCl+sdUDmNIg== + rgb-regex@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" @@ -20303,7 +20454,7 @@ shell-quote@1.7.2: resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== -shelljs@^0.8.3: +shelljs@^0.8.2, shelljs@^0.8.3: version "0.8.4" resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== @@ -20867,6 +21018,11 @@ stream-browserify@^2.0.1: inherits "~2.0.1" readable-stream "^2.0.2" +stream-buffers@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz#5249005a8d5c2d00b3a32e6e0a6ea209dc4f3521" + integrity sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ== + stream-combiner@~0.0.4: version "0.0.4" resolved "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" @@ -21731,6 +21887,13 @@ tinycolor2@^1.4.1: resolved "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8" integrity sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g= +tmp-promise@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.2.tgz#6e933782abff8b00c3119d63589ca1fb9caaa62a" + integrity sha512-OyCLAKU1HzBjL6Ev3gxUeraJNlbNingmi8IrHHEsYH8LTmEuhvYfqvhn2F/je+mjf4N58UmZ96OMEy1JanSCpA== + dependencies: + tmp "^0.2.0" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -21738,6 +21901,13 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +tmp@^0.2.0: + version "0.2.1" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + tmp@~0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" @@ -22190,6 +22360,11 @@ undefsafe@^2.0.2: dependencies: debug "^2.2.0" +underscore@^1.9.1: + version "1.11.0" + resolved "https://registry.npmjs.org/underscore/-/underscore-1.11.0.tgz#dd7c23a195db34267186044649870ff1bab5929e" + integrity sha512-xY96SsN3NA461qIRKZ/+qox37YXPtSBswMGfiNptr+wrt6ds4HaMw23TP612fEyGekRE6LNRiLYr/aqbHXNedw== + unfetch@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/unfetch/-/unfetch-4.1.0.tgz#6ec2dd0de887e58a4dee83a050ded80ffc4137db" @@ -23243,6 +23418,11 @@ ws@^7.2.3: resolved "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz#4b2f7f219b3d3737bc1a2fbf145d825b94d38ffd" integrity sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w== +ws@^7.3.1: + version "7.3.1" + resolved "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" + integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== + x-is-string@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82" From 5e60184ea4dffa2a07a659feada0a62949ecf1e9 Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 21 Sep 2020 10:22:33 -0600 Subject: [PATCH 36/38] Tweak dark mode colors --- packages/theme/src/themes.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/theme/src/themes.ts b/packages/theme/src/themes.ts index 73d6c13859..e92f36d22a 100644 --- a/packages/theme/src/themes.ts +++ b/packages/theme/src/themes.ts @@ -80,12 +80,12 @@ export const darkTheme = createTheme({ default: '#333333', }, status: { - ok: '#1DB954', - warning: '#FF9800', - error: '#E22134', - running: '#2E77D0', - pending: '#FFED51', - aborted: '#757575', + ok: '#9cdcaa', + warning: '#ffcd80', + error: '#f07278', + running: '#6cb6f5', + pending: '#fff9c2', + aborted: '#eeeeee', }, bursts: { fontColor: '#FEFEFE', @@ -103,7 +103,7 @@ export const darkTheme = createTheme({ }, border: '#E6E6E6', textContrast: '#FFFFFF', - textVerySubtle: '#DDD', + textVerySubtle: '#727272', textSubtle: '#EEEEEE', highlight: '#FFFBCC', errorBackground: '#FFEBEE', From 3947c92c2c35d87beb355c13bf544a1220fe75bc Mon Sep 17 00:00:00 2001 From: Ryan Vazquez Date: Thu, 24 Sep 2020 12:54:56 -0400 Subject: [PATCH 37/38] tweak contrast --- packages/theme/src/themes.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/theme/src/themes.ts b/packages/theme/src/themes.ts index e92f36d22a..72a1df716f 100644 --- a/packages/theme/src/themes.ts +++ b/packages/theme/src/themes.ts @@ -80,12 +80,12 @@ export const darkTheme = createTheme({ default: '#333333', }, status: { - ok: '#9cdcaa', - warning: '#ffcd80', - error: '#f07278', - running: '#6cb6f5', - pending: '#fff9c2', - aborted: '#eeeeee', + ok: '#71CF88', + warning: '#FFB84D', + error: '#F84C55', + running: '#3488E3', + pending: '#FEF071', + aborted: '#9E9E9E', }, bursts: { fontColor: '#FEFEFE', @@ -103,7 +103,7 @@ export const darkTheme = createTheme({ }, border: '#E6E6E6', textContrast: '#FFFFFF', - textVerySubtle: '#727272', + textVerySubtle: '#ececec', textSubtle: '#EEEEEE', highlight: '#FFFBCC', errorBackground: '#FFEBEE', From b097af87a4477115d65f6933d863d1c8d3548ba5 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Thu, 24 Sep 2020 23:01:28 +0200 Subject: [PATCH 38/38] Enable carryforward for codecov flags Our builds for Pull Requests do not upload coverage report for flags. This overrides the coverage data for flags. Setting carryforward flag to true prevents the data for flags to be overridden. --- .codecov.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.codecov.yml b/.codecov.yml index d03ad99836..1b8e770b59 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -13,6 +13,8 @@ flags: core: paths: - packages/core/ + carryforward: true core-api: paths: - packages/core-api/ + carryforward: true