From a63d3af1d78698efe45de3d891db9b45ffd0780e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 5 Feb 2020 15:57:46 +0100 Subject: [PATCH 01/25] Add a login barrier --- frontend/packages/app/package.json | 5 +- frontend/packages/app/src/App.test.tsx | 10 - frontend/packages/app/src/App.tsx | 42 ++-- .../app/src/login/LoginBarrier.test.tsx | 57 +++++ .../packages/app/src/login/LoginBarrier.tsx | 22 ++ .../app/src/login/MockCurrentUser.test.ts | 38 ++++ .../packages/app/src/login/MockCurrentUser.ts | 38 ++++ frontend/packages/app/src/login/types.ts | 11 + .../LoginComponent/LoginComponent.test.tsx | 3 +- .../LoginComponent/LoginComponent.tsx | 40 +++- frontend/yarn.lock | 205 +++++++++++++++++- 11 files changed, 428 insertions(+), 43 deletions(-) delete mode 100644 frontend/packages/app/src/App.test.tsx create mode 100644 frontend/packages/app/src/login/LoginBarrier.test.tsx create mode 100644 frontend/packages/app/src/login/LoginBarrier.tsx create mode 100644 frontend/packages/app/src/login/MockCurrentUser.test.ts create mode 100644 frontend/packages/app/src/login/MockCurrentUser.ts create mode 100644 frontend/packages/app/src/login/types.ts diff --git a/frontend/packages/app/package.json b/frontend/packages/app/package.json index 130e7972ad..43855d02b6 100644 --- a/frontend/packages/app/package.json +++ b/frontend/packages/app/package.json @@ -16,10 +16,13 @@ "@types/react": "^16.9.0", "@types/react-dom": "^16.9.0", "@types/react-router-dom": "^5.1.3", + "@types/zen-observable": "^0.8.0", "cross-env": "^7.0.0", "react": "^16.12.0", "react-dom": "^16.12.0", - "react-router-dom": "^5.1.2" + "react-router-dom": "^5.1.2", + "react-use": "^13.24.0", + "zen-observable": "^0.8.15" }, "workspaces": { "nohoist": [ diff --git a/frontend/packages/app/src/App.test.tsx b/frontend/packages/app/src/App.test.tsx deleted file mode 100644 index 3dc39e48b7..0000000000 --- a/frontend/packages/app/src/App.test.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import App from './App'; - -describe('App', () => { - it('renders learn react link', () => { - const rendered = render(); - rendered.getByText('This is Backstage!'); - }); -}); diff --git a/frontend/packages/app/src/App.tsx b/frontend/packages/app/src/App.tsx index c172c2bd38..3e52d6ca2b 100644 --- a/frontend/packages/app/src/App.tsx +++ b/frontend/packages/app/src/App.tsx @@ -11,15 +11,12 @@ import { LoginComponent } from '@backstage/plugin-login'; import HomePagePlugin from '@backstage/plugin-home-page'; import { CssBaseline, makeStyles, ThemeProvider } from '@material-ui/core'; import React, { FC } from 'react'; -import { - BrowserRouter as Router, - Link as RouterLink, - Route, - Switch, -} from 'react-router-dom'; +import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import HomePageTimer from './components/HomepageTimer'; import SideBar from './components/SideBar'; import entities from './entities'; +import { LoginBarrier } from './login/LoginBarrier'; +import { MockCurrentUser } from './login/MockCurrentUser'; const useStyles = makeStyles(theme => ({ '@global': { @@ -56,13 +53,12 @@ const useStyles = makeStyles(theme => ({ }, })); +const currentUser = new MockCurrentUser(); + const Login: FC<{}> = () => { return ( - -
- Go to Home -
+ currentUser.login(username)} />
); }; @@ -96,18 +92,20 @@ const App: FC<{}> = () => { return ( - - - - - - - - - - - - + + + + + + + + + + + + + + ); diff --git a/frontend/packages/app/src/login/LoginBarrier.test.tsx b/frontend/packages/app/src/login/LoginBarrier.test.tsx new file mode 100644 index 0000000000..14389cea43 --- /dev/null +++ b/frontend/packages/app/src/login/LoginBarrier.test.tsx @@ -0,0 +1,57 @@ +import { render, wait, act } from '@testing-library/react'; +import React from 'react'; +import Observable from 'zen-observable'; +import { LoginBarrier } from './LoginBarrier'; +import { LoginState } from './types'; + +describe('LoginBarrier', () => { + it('passes through when logged in', async () => { + const state$ = Observable.of({ + type: 'LOGGED_IN', + user: 'apa', + }); + const rendered = render( +
{state.type}
} + > + Logged in! +
, + ); + await wait(() => rendered.getByText('Logged in!')); + }); + + it('goes to fallback when not logged in', async () => { + const state$ = Observable.of({ type: 'LOGGED_OUT' }); + const rendered = render( +
{state.type}
} + > + Logged in! +
, + ); + await wait(() => rendered.getByText('LOGGED_OUT')); + }); + + it('transitions between states', async () => { + let subscriber: ZenObservable.SubscriptionObserver | undefined; + const state$ = new Observable(s => { + subscriber = s; + }); + + const rendered = render( +
{state.type}
} + > + Logged in! +
, + ); + + await wait(() => rendered.getByText('LOGGED_OUT')); + // @ts-ignore: Object is possibly 'undefined' + act(() => subscriber.next({ type: 'LOGGED_IN', user: 'apa' })); + await wait(() => rendered.getByText('Logged in!')); + }); +}); diff --git a/frontend/packages/app/src/login/LoginBarrier.tsx b/frontend/packages/app/src/login/LoginBarrier.tsx new file mode 100644 index 0000000000..56caf04bb8 --- /dev/null +++ b/frontend/packages/app/src/login/LoginBarrier.tsx @@ -0,0 +1,22 @@ +import React, { FC } from 'react'; +import Observable from 'zen-observable'; +import { useObservable } from 'react-use'; +import { LoginState } from './types'; + +type LoginBarrierProps = { + fallback: React.ComponentType<{ state: LoginState }>; + state$: Observable; +}; + +export const LoginBarrier: FC = ({ + fallback: Fallback, + state$, + children, +}) => { + const state = useObservable(state$, { type: 'LOGGED_OUT' }); + if (state.type === 'LOGGED_IN') { + return <>{children}; + } else { + return ; + } +}; diff --git a/frontend/packages/app/src/login/MockCurrentUser.test.ts b/frontend/packages/app/src/login/MockCurrentUser.test.ts new file mode 100644 index 0000000000..598c5e53ba --- /dev/null +++ b/frontend/packages/app/src/login/MockCurrentUser.test.ts @@ -0,0 +1,38 @@ +import { MockCurrentUser } from './MockCurrentUser'; +import { wait } from '@testing-library/react'; + +describe('MockCurrentUser', () => { + it('notifies immediately on subscribe', async () => { + const next = jest.fn(); + const current = new MockCurrentUser(); + current.state.subscribe(next); + await wait(); + expect(next).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ type: 'LOGGED_OUT' }), + ); + }); + + it('logs in and out', async () => { + const next = jest.fn(); + const current = new MockCurrentUser(); + current.state.subscribe(next); + await wait(); + expect(next).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ type: 'LOGGED_OUT' }), + ); + current.login('apa'); + await wait(); + expect(next).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ type: 'LOGGED_IN', user: 'apa' }), + ); + current.logout(); + await wait(); + expect(next).toHaveBeenNthCalledWith( + 3, + expect.objectContaining({ type: 'LOGGED_OUT' }), + ); + }); +}); diff --git a/frontend/packages/app/src/login/MockCurrentUser.ts b/frontend/packages/app/src/login/MockCurrentUser.ts new file mode 100644 index 0000000000..76dd04443e --- /dev/null +++ b/frontend/packages/app/src/login/MockCurrentUser.ts @@ -0,0 +1,38 @@ +import Observable from 'zen-observable'; +import { LoginState } from './types'; + +export class MockCurrentUser { + private listeners: ZenObservable.SubscriptionObserver[] = []; + private currentUser: string | undefined = undefined; + + login(user: string) { + this.currentUser = user; + this.notify(); + } + + logout() { + this.currentUser = undefined; + this.notify(); + } + + get state(): Observable { + return new Observable(subscriber => { + this.listeners.push(subscriber); + subscriber.next(this.getState()); + return () => { + this.listeners = this.listeners.filter(x => x !== subscriber); + }; + }); + } + + private notify() { + const state = this.getState(); + this.listeners.forEach(listener => listener.next(state)); + } + + private getState(): LoginState { + return this.currentUser + ? { type: 'LOGGED_IN', user: this.currentUser } + : { type: 'LOGGED_OUT' }; + } +} diff --git a/frontend/packages/app/src/login/types.ts b/frontend/packages/app/src/login/types.ts new file mode 100644 index 0000000000..9a341917ad --- /dev/null +++ b/frontend/packages/app/src/login/types.ts @@ -0,0 +1,11 @@ +export type LoggedOutState = { type: 'LOGGED_OUT' }; +export type LoggingInState = { type: 'LOGGING_IN' }; +export type LoggedInState = { type: 'LOGGED_IN'; user: string }; +export type LoggingOutState = { type: 'LOGGING_OUT' }; +export type LoginFailedState = { type: 'LOGIN_FAILED'; error: Error }; +export type LoginState = + | LoggedOutState + | LoggingInState + | LoggedInState + | LoggingOutState + | LoginFailedState; diff --git a/frontend/packages/plugins/login/src/components/LoginComponent/LoginComponent.test.tsx b/frontend/packages/plugins/login/src/components/LoginComponent/LoginComponent.test.tsx index 5262db4a5b..52e3341d6c 100644 --- a/frontend/packages/plugins/login/src/components/LoginComponent/LoginComponent.test.tsx +++ b/frontend/packages/plugins/login/src/components/LoginComponent/LoginComponent.test.tsx @@ -4,7 +4,8 @@ import LoginComponent from './LoginComponent'; describe('LoginComponent', () => { it('should render', () => { - const rendered = render(); + const onLogin = jest.fn(); + const rendered = render(); expect(rendered.getByText('Login')).toBeInTheDocument(); }); }); diff --git a/frontend/packages/plugins/login/src/components/LoginComponent/LoginComponent.tsx b/frontend/packages/plugins/login/src/components/LoginComponent/LoginComponent.tsx index 8826eceda7..f0915bed2e 100644 --- a/frontend/packages/plugins/login/src/components/LoginComponent/LoginComponent.tsx +++ b/frontend/packages/plugins/login/src/components/LoginComponent/LoginComponent.tsx @@ -1,11 +1,36 @@ -import React, { FC, Fragment } from 'react'; +import React, { FC, useState } from 'react'; import Button from '@material-ui/core/Button'; import TextField from '@material-ui/core/TextField'; import Grid from '@material-ui/core/Grid'; +import { Typography } from '@material-ui/core'; + +type Props = { + onLogin: (username: string) => Promise | void; +}; + +const LoginComponent: FC = ({ onLogin }) => { + const [error, setError] = useState(''); + const [username, setUsername] = useState(''); + + const onUsernameChanged = async (e: React.ChangeEvent) => { + setUsername(e.target.value); + }; + + const onSubmit = async () => { + if (!username) { + setError('You have to supply a username'); + } + + setError(''); + try { + await onLogin(username); + } catch (e) { + setError(`Login failed: ${e.message}`); + } + }; -const LoginComponent: FC<{}> = () => { return ( - +
= () => { type="email" autoFocus required + value={username} + onChange={onUsernameChanged} /> @@ -23,14 +50,17 @@ const LoginComponent: FC<{}> = () => { style={{ marginTop: '24px', marginBottom: '24px' }} > - + {error || 'Just enter any fake username'} +
); }; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 31d4e195f3..d52677c0d4 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2890,6 +2890,11 @@ jest-diff "^25.1.0" pretty-format "^25.1.0" +"@types/js-cookie@2.2.4": + version "2.2.4" + resolved "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.4.tgz#f79720b4755aa197c2e15e982e2f438f5748e348" + integrity sha512-WTfSE1Eauak/Nrg6cA9FgPTFvVawejsai6zXoq0QYTQ3mxONeRtGhKxa7wMlUzWWmzrmTeV+rwLjHgsCntdrsA== + "@types/json-schema@^7.0.3": version "7.0.4" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" @@ -3013,6 +3018,11 @@ dependencies: "@types/yargs-parser" "*" +"@types/zen-observable@^0.8.0": + version "0.8.0" + resolved "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d" + integrity sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg== + "@typescript-eslint/eslint-plugin@^2.14.0", "@typescript-eslint/eslint-plugin@^2.8.0": version "2.18.0" resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.18.0.tgz#f8cf272dfb057ecf1ea000fea1e0b3f06a32f9cb" @@ -3202,6 +3212,11 @@ "@webassemblyjs/wast-parser" "1.8.5" "@xtuc/long" "4.2.2" +"@xobotyi/scrollbar-width@1.8.2": + version "1.8.2" + resolved "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.8.2.tgz#056946ac41ade4885c576619c8d70c46c77e9683" + integrity sha512-RV6+4hR29oMaPCvSYFUvzOvlsrg2s2k5NE9tNERs+4nFIC9dRXxs+lL2CcaRTbl3yQxKwAZ8Cd+qMI8aUu9TFw== + "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -4033,6 +4048,11 @@ bottleneck@^2.18.1: resolved "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91" integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw== +bowser@^1.7.3: + version "1.9.4" + resolved "https://registry.npmjs.org/bowser/-/bowser-1.9.4.tgz#890c58a2813a9d3243704334fa81b96a5c150c9a" + integrity sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ== + boxen@^1.2.1: version "1.3.0" resolved "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" @@ -5115,6 +5135,13 @@ copy-descriptor@^0.1.0: resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= +copy-to-clipboard@^3.2.0: + version "3.2.1" + resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.2.1.tgz#b1a1137100e5665d5a96015cb579e30e90e07c44" + integrity sha512-btru1Q6RD9wbonIvEU5EfnhIRGHLo//BGXQ1hNAD2avIs/nBZlpbOeKtv3mhoUByN4DB9Cb6/vXBymj1S43KmA== + dependencies: + toggle-selection "^1.0.6" + core-js-compat@^3.6.2: version "3.6.4" resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.4.tgz#938476569ebb6cda80d339bcf199fae4f16fff17" @@ -5300,6 +5327,14 @@ css-has-pseudo@^0.10.0: postcss "^7.0.6" postcss-selector-parser "^5.0.0-rc.4" +css-in-js-utils@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz#3b472b398787291b47cfe3e44fecfdd9e914ba99" + integrity sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA== + dependencies: + hyphenate-style-name "^1.0.2" + isobject "^3.0.1" + css-loader@3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/css-loader/-/css-loader-3.2.0.tgz#bb570d89c194f763627fcf1f80059c6832d009b2" @@ -5358,6 +5393,14 @@ css-tree@1.0.0-alpha.37: mdn-data "2.0.4" source-map "^0.6.1" +css-tree@^1.0.0-alpha.28: + version "1.0.0-alpha.39" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb" + integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA== + dependencies: + mdn-data "2.0.6" + source-map "^0.6.1" + css-unit-converter@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996" @@ -5510,7 +5553,7 @@ cssstyle@^2.0.0: dependencies: cssom "~0.3.6" -csstype@^2.2.0, csstype@^2.5.2, csstype@^2.6.5, csstype@^2.6.7: +csstype@^2.2.0, csstype@^2.5.2, csstype@^2.5.5, csstype@^2.6.5, csstype@^2.6.7: version "2.6.8" resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.8.tgz#0fb6fc2417ffd2816a418c9336da74d7f07db431" integrity sha512-msVS9qTuMT5zwAGCVm4mxfrZ18BNc6Csd0oJAtiFMZ1FAx1CCvy2+5MDmYoix63LM/6NDbNtodCiGYGmFgO0dA== @@ -6154,6 +6197,13 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +error-stack-parser@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8" + integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ== + dependencies: + stackframe "^1.1.1" + es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2: version "1.17.4" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" @@ -6758,6 +6808,16 @@ fast-levenshtein@~2.0.6: resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-shallow-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b" + integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== + +fastest-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-1.0.1.tgz#9122d406d4c9d98bea644a6b6853d5874b87b028" + integrity sha1-kSLUBtTJ2YvqZEpraFPVh0uHsCg= + fastq@^1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz#4ec8a38f4ac25f21492673adb7eae9cfef47d1c2" @@ -7947,7 +8007,7 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -hyphenate-style-name@^1.0.3: +hyphenate-style-name@^1.0.2, hyphenate-style-name@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48" integrity sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ== @@ -8147,6 +8207,14 @@ init-package-json@^1.10.3: validate-npm-package-license "^3.0.1" validate-npm-package-name "^3.0.0" +inline-style-prefixer@^4.0.0: + version "4.0.2" + resolved "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-4.0.2.tgz#d390957d26f281255fe101da863158ac6eb60911" + integrity sha512-N8nVhwfYga9MiV9jWlwfdj1UDIaZlBFu4cJSJkIr7tZX7sHpHhGR5su1qdpW+7KPL8ISTvCIkcaFi/JdBknvPg== + dependencies: + bowser "^1.7.3" + css-in-js-utils "^2.0.0" + inquirer@6.5.0: version "6.5.0" resolved "https://registry.npmjs.org/inquirer/-/inquirer-6.5.0.tgz#2303317efc9a4ea7ec2e2df6f86569b734accf42" @@ -9541,6 +9609,11 @@ jest@^25.1.0: import-local "^3.0.2" jest-cli "^25.1.0" +js-cookie@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -10588,6 +10661,11 @@ mdn-data@2.0.4: resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== +mdn-data@2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978" + integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA== + meant@~1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/meant/-/meant-1.0.1.tgz#66044fea2f23230ec806fb515efea29c44d2115d" @@ -11019,6 +11097,20 @@ nan@^2.12.1: resolved "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== +nano-css@^5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/nano-css/-/nano-css-5.2.1.tgz#73b8470fa40b028a134d3393ae36bbb34b9fa332" + integrity sha512-T54okxMAha0+de+W8o3qFtuWhTxYvqQh2ku1cYEqTTP9mR62nWV2lLK9qRuAGWmoaYWhU7K4evT9Lc1iF65wuw== + dependencies: + css-tree "^1.0.0-alpha.28" + csstype "^2.5.5" + fastest-stable-stringify "^1.0.1" + inline-style-prefixer "^4.0.0" + rtl-css-js "^1.9.0" + sourcemap-codec "^1.4.1" + stacktrace-js "^2.0.0" + stylis "3.5.0" + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -13360,7 +13452,7 @@ react-error-overlay@^6.0.5: resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.5.tgz#55d59c2a3810e8b41922e0b4e5f85dcf239bd533" integrity sha512-+DMR2k5c6BqMDSMF8hLH0vYKtKTeikiFW+fj0LClN+XZg4N9b8QUAdHC62CGWNLTi/gnuuemNcNcTFrCvK1f+A== -react-fast-compare@^2.0.2: +react-fast-compare@^2.0.2, react-fast-compare@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== @@ -13431,6 +13523,25 @@ react-transition-group@^4.3.0: loose-envify "^1.4.0" prop-types "^15.6.2" +react-use@^13.24.0: + version "13.24.0" + resolved "https://registry.npmjs.org/react-use/-/react-use-13.24.0.tgz#f4574e26cfaaad65e3f04c0d5ff80c1836546236" + integrity sha512-p8GsZuMdz8OeIGzuYLm6pzJysKOhNyQjCUG6SHrQGk6o6ghy/RVGSqnmxVacNbN9166S0+9FsM1N1yH9GzWlgg== + dependencies: + "@types/js-cookie" "2.2.4" + "@xobotyi/scrollbar-width" "1.8.2" + copy-to-clipboard "^3.2.0" + fast-shallow-equal "^1.0.0" + js-cookie "^2.2.1" + nano-css "^5.2.1" + react-fast-compare "^2.0.4" + resize-observer-polyfill "^1.5.1" + screenfull "^5.0.0" + set-harmonic-interval "^1.0.1" + throttle-debounce "^2.1.0" + ts-easing "^0.2.0" + tslib "^1.10.0" + react@^16.12.0: version "16.12.0" resolved "https://registry.npmjs.org/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83" @@ -13900,6 +14011,11 @@ requires-port@^1.0.0: resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -14087,6 +14203,13 @@ rsvp@^4.8.4: resolved "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== +rtl-css-js@^1.9.0: + version "1.14.0" + resolved "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.14.0.tgz#daa4f192a92509e292a0519f4b255e6e3c076b7d" + integrity sha512-Dl5xDTeN3e7scU1cWX8c9b6/Nqz3u/HgR4gePc1kWXYiQWVQbKCEyK6+Hxve9LbcJ5EieHy1J9nJCN3grTtGwg== + dependencies: + "@babel/runtime" "^7.1.2" + run-async@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" @@ -14203,6 +14326,11 @@ schema-utils@^2.0.0, schema-utils@^2.0.1, schema-utils@^2.1.0, schema-utils@^2.2 ajv "^6.10.2" ajv-keywords "^3.4.1" +screenfull@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/screenfull/-/screenfull-5.0.1.tgz#873052411eb9096bc1c8e615f5badad119e6e42c" + integrity sha512-NgQH4KKh2V3zlj2u90l7TUcSFxr9qL/64QEvhAvCN/fu1YS39YLTBKIqZqiS3STj3QD8sN6XnsK/8jk3hRq4WA== + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -14345,6 +14473,11 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-harmonic-interval@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" + integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g== + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -14645,6 +14778,11 @@ source-map-url@^0.4.0: resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= +source-map@0.5.6: + version "0.5.6" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= + source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -14660,6 +14798,11 @@ source-map@^0.7.3: resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== +sourcemap-codec@^1.4.1: + version "1.4.8" + resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + spawn-error-forwarder@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz#1afd94738e999b0346d7b9fc373be55e07577029" @@ -14787,11 +14930,40 @@ stable@^0.1.8: resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== +stack-generator@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.5.tgz#fb00e5b4ee97de603e0773ea78ce944d81596c36" + integrity sha512-/t1ebrbHkrLrDuNMdeAcsvynWgoH/i4o8EGGfX7dEYDoTXOYVAkEpFdtshlvabzc6JlJ8Kf9YdFEoz7JkzGN9Q== + dependencies: + stackframe "^1.1.1" + stack-utils@^1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== +stackframe@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/stackframe/-/stackframe-1.1.1.tgz#ffef0a3318b1b60c3b58564989aca5660729ec71" + integrity sha512-0PlYhdKh6AfFxRyK/v+6/k+/mMfyiEBbTM5L94D0ZytQnJ166wuwoTYLHFWGbs2dpA8Rgq763KGWmN1EQEYHRQ== + +stacktrace-gps@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.0.4.tgz#7688dc2fc09ffb3a13165ebe0dbcaf41bcf0c69a" + integrity sha512-qIr8x41yZVSldqdqe6jciXEaSCKw1U8XTXpjDuy0ki/apyTn/r3w9hDAAQOhZdxvsC93H+WwwEu5cq5VemzYeg== + dependencies: + source-map "0.5.6" + stackframe "^1.1.1" + +stacktrace-js@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b" + integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg== + dependencies: + error-stack-parser "^2.0.6" + stack-generator "^2.0.5" + stacktrace-gps "^3.0.4" + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -15103,6 +15275,11 @@ stylehacks@^4.0.0: postcss "^7.0.0" postcss-selector-parser "^3.0.0" +stylis@3.5.0: + version "3.5.0" + resolved "https://registry.npmjs.org/stylis/-/stylis-3.5.0.tgz#016fa239663d77f868fef5b67cf201c4b7c701e1" + integrity sha512-pP7yXN6dwMzAR29Q0mBrabPCe0/mNO1MSr93bhay+hcZondvMMTpeGyd8nbhYJdyperNT2DRxONQuUGcJr5iPw== + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -15331,6 +15508,11 @@ throat@^5.0.0: resolved "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== +throttle-debounce@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.1.0.tgz#257e648f0a56bd9e54fe0f132c4ab8611df4e1d5" + integrity sha512-AOvyNahXQuU7NN+VVvOOX+uW6FPaWdAOdRP5HfwYxAfCzXTFKRMoIMk+n+po318+ktcChx+F1Dd91G3YHeMKyg== + through2@^2.0.0, through2@^2.0.2, through2@~2.0.0: version "2.0.5" resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -15442,6 +15624,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI= + toidentifier@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" @@ -15499,6 +15686,11 @@ trim-off-newlines@^1.0.0: resolved "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= +ts-easing@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" + integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== + ts-jest@^25.0.0: version "25.1.0" resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-25.1.0.tgz#06e776c4cce8a4da8eec4945f36a5823d0c0f9ba" @@ -15520,7 +15712,7 @@ ts-pnp@1.1.5, ts-pnp@^1.1.2: resolved "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.5.tgz#840e0739c89fce5f3abd9037bb091dbff16d9dec" integrity sha512-ti7OGMOUOzo66wLF3liskw6YQIaSsBgc4GOAlWRnIEj8htCxJUxskanMUoJOD6MDCRAXo36goXJZch+nOS0VMA== -tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: version "1.10.0" resolved "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== @@ -16691,3 +16883,8 @@ yargs@^8.0.2: which-module "^2.0.0" y18n "^3.2.1" yargs-parser "^7.0.0" + +zen-observable@^0.8.15: + version "0.8.15" + resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" + integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== From b894a0be0c747edbbf4c11e5901bf788abce9cfe Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 6 Feb 2020 09:01:55 +0100 Subject: [PATCH 02/25] frontend: added github-actions plugin skeleton + plugin output experiments --- frontend/packages/app/package.json | 1 + frontend/packages/app/src/entities/index.ts | 4 +- .../api/entityView/EntityViewPageBuilder.tsx | 23 +++++++++- .../packages/core/src/api/plugin/Plugin.tsx | 15 +++++- .../core/src/api/plugin/PluginOutputHook.ts | 11 +++++ .../packages/core/src/api/plugin/outputs.ts | 8 ++++ .../src/api/widgetView/WidgetViewBuilder.tsx | 4 +- .../src/components/EntityLink/EntityLink.tsx | 2 +- .../EntityLink/RelativeEntityLink.tsx | 16 +++++++ .../core/src/components/EntityLink/index.ts | 1 + frontend/packages/core/src/index.ts | 5 +- .../packages/plugins/github-actions/README.md | 1 + .../plugins/github-actions/jest.config.js | 4 ++ .../plugins/github-actions/jest.setup.ts | 1 + .../plugins/github-actions/package.json | 28 +++++++++++ .../BuildDetailsPage/BuildDetailsPage.tsx | 20 ++++++++ .../src/components/BuildDetailsPage/index.ts | 1 + .../BuildInfoCard/BuildInfoCard.tsx | 8 ++++ .../src/components/BuildInfoCard/index.ts | 1 + .../BuildListPage/BuildListPage.tsx | 46 +++++++++++++++++++ .../src/components/BuildListPage/index.ts | 1 + .../src/components/BuildPage/BuildPage.tsx | 22 +++++++++ .../src/components/BuildPage/index.ts | 1 + .../plugins/github-actions/src/index.ts | 1 + .../plugins/github-actions/src/plugin.test.ts | 7 +++ .../plugins/github-actions/src/plugin.ts | 18 ++++++++ 26 files changed, 243 insertions(+), 7 deletions(-) create mode 100644 frontend/packages/core/src/api/plugin/PluginOutputHook.ts create mode 100644 frontend/packages/core/src/api/plugin/outputs.ts create mode 100644 frontend/packages/core/src/components/EntityLink/RelativeEntityLink.tsx create mode 100644 frontend/packages/plugins/github-actions/README.md create mode 100644 frontend/packages/plugins/github-actions/jest.config.js create mode 100644 frontend/packages/plugins/github-actions/jest.setup.ts create mode 100644 frontend/packages/plugins/github-actions/package.json create mode 100644 frontend/packages/plugins/github-actions/src/components/BuildDetailsPage/BuildDetailsPage.tsx create mode 100644 frontend/packages/plugins/github-actions/src/components/BuildDetailsPage/index.ts create mode 100644 frontend/packages/plugins/github-actions/src/components/BuildInfoCard/BuildInfoCard.tsx create mode 100644 frontend/packages/plugins/github-actions/src/components/BuildInfoCard/index.ts create mode 100644 frontend/packages/plugins/github-actions/src/components/BuildListPage/BuildListPage.tsx create mode 100644 frontend/packages/plugins/github-actions/src/components/BuildListPage/index.ts create mode 100644 frontend/packages/plugins/github-actions/src/components/BuildPage/BuildPage.tsx create mode 100644 frontend/packages/plugins/github-actions/src/components/BuildPage/index.ts create mode 100644 frontend/packages/plugins/github-actions/src/index.ts create mode 100644 frontend/packages/plugins/github-actions/src/plugin.test.ts create mode 100644 frontend/packages/plugins/github-actions/src/plugin.ts diff --git a/frontend/packages/app/package.json b/frontend/packages/app/package.json index 130e7972ad..6738d591d7 100644 --- a/frontend/packages/app/package.json +++ b/frontend/packages/app/package.json @@ -4,6 +4,7 @@ "private": true, "dependencies": { "@backstage/core": "0.0.0", + "@backstage/plugin-github-actions": "0.0.0", "@backstage/plugin-hello-world": "0.0.0", "@backstage/plugin-home-page": "0.0.0", "@backstage/plugin-login": "0.0.0", diff --git a/frontend/packages/app/src/entities/index.ts b/frontend/packages/app/src/entities/index.ts index f32f99ab06..8b42342e5d 100644 --- a/frontend/packages/app/src/entities/index.ts +++ b/frontend/packages/app/src/entities/index.ts @@ -6,6 +6,7 @@ import { import ComputerIcon from '@material-ui/icons/Computer'; import MockEntityPage from './MockEntityPage'; import MockEntityCard from './MockEntityCard'; +import GithubActionsPlugin from '@backstage/plugin-github-actions'; /* SERVICE */ const serviceOverviewPage = createWidgetView() @@ -14,7 +15,8 @@ const serviceOverviewPage = createWidgetView() const serviceView = createEntityView() .addPage('Overview', 'overview', serviceOverviewPage) - .addComponent('CI/CD', 'ci-cd', MockEntityPage); + .register(GithubActionsPlugin) + .addComponent('Deployment', 'deployment', MockEntityPage); const serviceEntity = createEntityKind({ kind: 'service', diff --git a/frontend/packages/core/src/api/entityView/EntityViewPageBuilder.tsx b/frontend/packages/core/src/api/entityView/EntityViewPageBuilder.tsx index 10985325fc..e4089e16d7 100644 --- a/frontend/packages/core/src/api/entityView/EntityViewPageBuilder.tsx +++ b/frontend/packages/core/src/api/entityView/EntityViewPageBuilder.tsx @@ -5,6 +5,8 @@ import ListItem from '@material-ui/core/ListItem'; import { AppComponentBuilder, App } from '../app/types'; import { useEntity, useEntityUri, useEntityConfig } from './EntityContext'; import EntityLink from '../../components/EntityLink/EntityLink'; +import BackstagePlugin, { outputSymbol } from '../plugin/Plugin'; +import { entityViewPage } from '../plugin/outputs'; const EntityLayout: FC<{}> = ({ children }) => { const config = useEntityConfig(); @@ -57,7 +59,7 @@ const EntityViewComponent: FC = ({ pages }) => { {pages.map(({ path, component }) => ( @@ -75,6 +77,10 @@ type EntityViewRegistration = path: string; page: AppComponentBuilder; } + | { + type: 'plugin'; + plugin: BackstagePlugin; + } | { type: 'component'; title: string; @@ -103,6 +109,11 @@ export default class EntityViewBuilder extends AppComponentBuilder { return this; } + register(plugin: BackstagePlugin): EntityViewBuilder { + this.registrations.push({ type: 'plugin', plugin }); + return this; + } + build(app: App): ComponentType { const pages = this.registrations.map(registration => { switch (registration.type) { @@ -114,6 +125,16 @@ export default class EntityViewBuilder extends AppComponentBuilder { const { title, path, component } = registration; return { title, path, component }; } + case 'plugin': { + const { plugin } = registration; + const output = plugin[outputSymbol](entityViewPage); + if (!output) { + throw new Error( + `Plugin ${plugin} was registered as entity view, but did not have any output`, + ); + } + return output; + } default: throw new Error(`Unknown EntityViewBuilder registration`); } diff --git a/frontend/packages/core/src/api/plugin/Plugin.tsx b/frontend/packages/core/src/api/plugin/Plugin.tsx index 13caa40475..a5714fb009 100644 --- a/frontend/packages/core/src/api/plugin/Plugin.tsx +++ b/frontend/packages/core/src/api/plugin/Plugin.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { Route, Redirect } from 'react-router-dom'; +import PluginOutputHook from './PluginOutputHook'; export type PluginConfig = { id: string; @@ -8,6 +9,7 @@ export type PluginConfig = { export type PluginHooks = { router: Router; + provide(ref: PluginOutputHook, value: T): void; }; export type RouteOptions = { @@ -35,9 +37,11 @@ export type Router = { export type PluginRegistrationResult = { routes?: JSX.Element[]; + outputs?: Map, any>; }; export const registerSymbol = Symbol('plugin-register'); +export const outputSymbol = Symbol('plugin-output'); export default class Plugin { private result?: PluginRegistrationResult; @@ -55,6 +59,7 @@ export default class Plugin { const { id } = this.config; const routes = new Array(); + const outputs = new Map, any>(); this.config.register({ router: { @@ -86,12 +91,20 @@ export default class Plugin { ); }, }, + provide(hook, value) { + outputs.set(hook, value); + }, }); - this.result = { routes }; + this.result = { routes, outputs }; return this.result; } + [outputSymbol](outputHook: PluginOutputHook): T | undefined { + const { outputs } = this[registerSymbol](); + return outputs?.get(outputHook) as T; + } + toString() { return `plugin{${this.config.id}}`; } diff --git a/frontend/packages/core/src/api/plugin/PluginOutputHook.ts b/frontend/packages/core/src/api/plugin/PluginOutputHook.ts new file mode 100644 index 0000000000..2780117d24 --- /dev/null +++ b/frontend/packages/core/src/api/plugin/PluginOutputHook.ts @@ -0,0 +1,11 @@ +export default class PluginOutputHook { + constructor(private readonly name: string) {} + + get T(): T { + throw new Error('use typeof instead'); + } + + toString() { + return `pluginOutput{${this.name}}`; + } +} diff --git a/frontend/packages/core/src/api/plugin/outputs.ts b/frontend/packages/core/src/api/plugin/outputs.ts new file mode 100644 index 0000000000..59c4555cbf --- /dev/null +++ b/frontend/packages/core/src/api/plugin/outputs.ts @@ -0,0 +1,8 @@ +import PluginOutputHook from './PluginOutputHook'; +import { ComponentType } from 'react'; + +export const entityViewPage = new PluginOutputHook<{ + title: string; + path: string; + component: ComponentType; +}>('entity-view-page'); diff --git a/frontend/packages/core/src/api/widgetView/WidgetViewBuilder.tsx b/frontend/packages/core/src/api/widgetView/WidgetViewBuilder.tsx index eb9f5d1ed0..3ab13c2f8c 100644 --- a/frontend/packages/core/src/api/widgetView/WidgetViewBuilder.tsx +++ b/frontend/packages/core/src/api/widgetView/WidgetViewBuilder.tsx @@ -9,8 +9,8 @@ type Props = { const WidgetViewComponent: FC = ({ cards }) => { return (
- {cards.map(CardComponent => ( - + {cards.map((CardComponent, index) => ( + ))}
); diff --git a/frontend/packages/core/src/components/EntityLink/EntityLink.tsx b/frontend/packages/core/src/components/EntityLink/EntityLink.tsx index c26972c467..a060cb1719 100644 --- a/frontend/packages/core/src/components/EntityLink/EntityLink.tsx +++ b/frontend/packages/core/src/components/EntityLink/EntityLink.tsx @@ -13,7 +13,7 @@ type Props = { } ); -function buildPath(kind: string, id?: string, subPath?: string) { +export function buildPath(kind: string, id?: string, subPath?: string) { if (id) { if (subPath) { return `/entity/${kind}/${id}/${subPath}`; diff --git a/frontend/packages/core/src/components/EntityLink/RelativeEntityLink.tsx b/frontend/packages/core/src/components/EntityLink/RelativeEntityLink.tsx new file mode 100644 index 0000000000..aa61abcc01 --- /dev/null +++ b/frontend/packages/core/src/components/EntityLink/RelativeEntityLink.tsx @@ -0,0 +1,16 @@ +import React, { FC } from 'react'; +import { Link } from 'react-router-dom'; +import { useEntity } from '../../api'; +import { buildPath } from './EntityLink'; + +type Props = { + view: string; +}; + +const RelativeEntityLink: FC = ({ view, children }) => { + const entity = useEntity(); + + return {children}; +}; + +export default RelativeEntityLink; diff --git a/frontend/packages/core/src/components/EntityLink/index.ts b/frontend/packages/core/src/components/EntityLink/index.ts index d35b5bee26..f067ebf3e2 100644 --- a/frontend/packages/core/src/components/EntityLink/index.ts +++ b/frontend/packages/core/src/components/EntityLink/index.ts @@ -1 +1,2 @@ export { default } from './EntityLink'; +export { default as RelativeEntityLink } from './RelativeEntityLink'; diff --git a/frontend/packages/core/src/index.ts b/frontend/packages/core/src/index.ts index 3dc9c80d41..ccca30231a 100644 --- a/frontend/packages/core/src/index.ts +++ b/frontend/packages/core/src/index.ts @@ -1,5 +1,8 @@ export * from './api'; -export { default as EntityLink } from './components/EntityLink'; +export { + default as EntityLink, + RelativeEntityLink, +} from './components/EntityLink'; export { default as Page } from '../src/layout/Page'; export { gradients, theme } from '../src/layout/Page'; export { default as Header } from '../src/layout/Header/Header'; diff --git a/frontend/packages/plugins/github-actions/README.md b/frontend/packages/plugins/github-actions/README.md new file mode 100644 index 0000000000..d55e47c5b1 --- /dev/null +++ b/frontend/packages/plugins/github-actions/README.md @@ -0,0 +1 @@ +Welcome to your github-actions plugin! diff --git a/frontend/packages/plugins/github-actions/jest.config.js b/frontend/packages/plugins/github-actions/jest.config.js new file mode 100644 index 0000000000..6b28dacb3d --- /dev/null +++ b/frontend/packages/plugins/github-actions/jest.config.js @@ -0,0 +1,4 @@ +module.exports = { + ...require('@spotify/web-scripts/config/jest.config.js'), + setupFilesAfterEnv: ['../jest.setup.ts'], +}; diff --git a/frontend/packages/plugins/github-actions/jest.setup.ts b/frontend/packages/plugins/github-actions/jest.setup.ts new file mode 100644 index 0000000000..666127af39 --- /dev/null +++ b/frontend/packages/plugins/github-actions/jest.setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom/extend-expect'; diff --git a/frontend/packages/plugins/github-actions/package.json b/frontend/packages/plugins/github-actions/package.json new file mode 100644 index 0000000000..cfe203d986 --- /dev/null +++ b/frontend/packages/plugins/github-actions/package.json @@ -0,0 +1,28 @@ +{ + "name": "@backstage/plugin-github-actions", + "version": "0.0.0", + "main": "src/index.ts", + "main:src": "src/index.ts", + "devDependencies": { + "@backstage/core": "0.0.0", + "@spotify/web-scripts": "^6.0.0", + "@testing-library/jest-dom": "^4.2.4", + "@testing-library/react": "^9.3.2", + "@testing-library/user-event": "^7.1.2", + "@types/jest": "^24.0.0", + "@types/node": "^12.0.0", + "@types/react": "^16.9.0", + "@types/react-dom": "^16.9.0", + "@types/react-router-dom": "^5.1.3", + "react": "^16.12.0", + "react-dom": "^16.12.0", + "react-router-dom": "^5.1.2", + "@material-ui/core": "^4.9.1", + "@material-ui/icons": "^4.9.1" + }, + "scripts": { + "lint": "web-scripts lint", + "test": "web-scripts test" + }, + "license": "Apache-2.0" +} diff --git a/frontend/packages/plugins/github-actions/src/components/BuildDetailsPage/BuildDetailsPage.tsx b/frontend/packages/plugins/github-actions/src/components/BuildDetailsPage/BuildDetailsPage.tsx new file mode 100644 index 0000000000..5cd5e21bf7 --- /dev/null +++ b/frontend/packages/plugins/github-actions/src/components/BuildDetailsPage/BuildDetailsPage.tsx @@ -0,0 +1,20 @@ +import React, { FC } from 'react'; +import Button from '@material-ui/core/Button'; + +type Props = { + buildId: string; +}; + +const BuildDetailsPage: FC = ({ buildId }) => { + return ( + + ); +}; + +export default BuildDetailsPage; diff --git a/frontend/packages/plugins/github-actions/src/components/BuildDetailsPage/index.ts b/frontend/packages/plugins/github-actions/src/components/BuildDetailsPage/index.ts new file mode 100644 index 0000000000..a2b6def3b0 --- /dev/null +++ b/frontend/packages/plugins/github-actions/src/components/BuildDetailsPage/index.ts @@ -0,0 +1 @@ +export { default } from './BuildDetailsPage'; diff --git a/frontend/packages/plugins/github-actions/src/components/BuildInfoCard/BuildInfoCard.tsx b/frontend/packages/plugins/github-actions/src/components/BuildInfoCard/BuildInfoCard.tsx new file mode 100644 index 0000000000..38fc2266db --- /dev/null +++ b/frontend/packages/plugins/github-actions/src/components/BuildInfoCard/BuildInfoCard.tsx @@ -0,0 +1,8 @@ +import React, { FC } from 'react'; +import { InfoCard } from '@backstage/core'; + +const BuildInfoCard: FC<{}> = () => { + return Last build was acb67fa3b5472w; +}; + +export default BuildInfoCard; diff --git a/frontend/packages/plugins/github-actions/src/components/BuildInfoCard/index.ts b/frontend/packages/plugins/github-actions/src/components/BuildInfoCard/index.ts new file mode 100644 index 0000000000..21c0b99099 --- /dev/null +++ b/frontend/packages/plugins/github-actions/src/components/BuildInfoCard/index.ts @@ -0,0 +1 @@ +export { default } from './BuildInfoCard'; diff --git a/frontend/packages/plugins/github-actions/src/components/BuildListPage/BuildListPage.tsx b/frontend/packages/plugins/github-actions/src/components/BuildListPage/BuildListPage.tsx new file mode 100644 index 0000000000..697d9edfa6 --- /dev/null +++ b/frontend/packages/plugins/github-actions/src/components/BuildListPage/BuildListPage.tsx @@ -0,0 +1,46 @@ +import React, { FC } from 'react'; +import { + TableContainer, + Table, + TableHead, + TableRow, + TableCell, + Paper, + TableBody, +} from '@material-ui/core'; +import { RelativeEntityLink } from '@backstage/core'; + +const BuildListPage: FC<{}> = () => { + const rows = [ + { message: 'Fixed a Bar', commit: 'fb46ca3dfbd7af5bc43da', id: 165 }, + { message: 'Fixed a Foo', commit: 'd7af5bc43dafb46ca3dfb', id: 164 }, + ]; + return ( + + + + + Message + Commit + Build ID + + + + {rows.map(row => ( + + {row.message} + {row.commit} + + + {row.commit} + + + + ))} + +
+
+ ); +}; + +export default BuildListPage; diff --git a/frontend/packages/plugins/github-actions/src/components/BuildListPage/index.ts b/frontend/packages/plugins/github-actions/src/components/BuildListPage/index.ts new file mode 100644 index 0000000000..497fb05ab5 --- /dev/null +++ b/frontend/packages/plugins/github-actions/src/components/BuildListPage/index.ts @@ -0,0 +1 @@ +export { default } from './BuildListPage'; diff --git a/frontend/packages/plugins/github-actions/src/components/BuildPage/BuildPage.tsx b/frontend/packages/plugins/github-actions/src/components/BuildPage/BuildPage.tsx new file mode 100644 index 0000000000..6fff20640a --- /dev/null +++ b/frontend/packages/plugins/github-actions/src/components/BuildPage/BuildPage.tsx @@ -0,0 +1,22 @@ +import React, { FC } from 'react'; +import { Switch, Route } from 'react-router-dom'; +import BuildListPage from '../BuildListPage'; +import BuildDetailsPage from '../BuildDetailsPage'; +import { useRouteMatch } from 'react-router-dom'; + +const BuildPage: FC<{}> = () => { + const match = useRouteMatch(); + return ( + + ( + + )} + /> + + + ); +}; + +export default BuildPage; diff --git a/frontend/packages/plugins/github-actions/src/components/BuildPage/index.ts b/frontend/packages/plugins/github-actions/src/components/BuildPage/index.ts new file mode 100644 index 0000000000..092d2e639d --- /dev/null +++ b/frontend/packages/plugins/github-actions/src/components/BuildPage/index.ts @@ -0,0 +1 @@ +export { default } from './BuildPage'; diff --git a/frontend/packages/plugins/github-actions/src/index.ts b/frontend/packages/plugins/github-actions/src/index.ts new file mode 100644 index 0000000000..b68aea57f9 --- /dev/null +++ b/frontend/packages/plugins/github-actions/src/index.ts @@ -0,0 +1 @@ +export { default } from './plugin'; diff --git a/frontend/packages/plugins/github-actions/src/plugin.test.ts b/frontend/packages/plugins/github-actions/src/plugin.test.ts new file mode 100644 index 0000000000..c202f56231 --- /dev/null +++ b/frontend/packages/plugins/github-actions/src/plugin.test.ts @@ -0,0 +1,7 @@ +import plugin from './plugin'; + +describe('github-actions', () => { + it('should export plugin', () => { + expect(plugin).toBeDefined(); + }); +}); diff --git a/frontend/packages/plugins/github-actions/src/plugin.ts b/frontend/packages/plugins/github-actions/src/plugin.ts new file mode 100644 index 0000000000..359ba2e7f9 --- /dev/null +++ b/frontend/packages/plugins/github-actions/src/plugin.ts @@ -0,0 +1,18 @@ +import { createPlugin } from '@backstage/core'; +import { entityViewPage } from '@backstage/core/src/api/plugin/outputs'; +import BuildPage from './components/BuildPage'; + +// export const buildListRoute = createEntityRoute<[]>('/builds') +// export const buildDetailsRoute = createEntityRoute<[number]>('/builds/:buildId') + +export default createPlugin({ + id: 'github-actions', + + register({ provide }) { + provide(entityViewPage, { + title: 'CI/CD', + path: 'builds', + component: BuildPage, + }); + }, +}); From a8964d45745af5bd6ce3f03d18e5ab49c481ba21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jefferson=20Gir=C3=A3o?= Date: Thu, 6 Feb 2020 09:26:22 +0100 Subject: [PATCH 03/25] chore: delete unused file --- backend/inventory/gen-pb.sh | 2 -- 1 file changed, 2 deletions(-) delete mode 100755 backend/inventory/gen-pb.sh diff --git a/backend/inventory/gen-pb.sh b/backend/inventory/gen-pb.sh deleted file mode 100755 index 6f46277f8e..0000000000 --- a/backend/inventory/gen-pb.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -protoc -I ../../ protos/inventory.proto --go_out=plugins=grpc:. From 52eb8cf8e7a557e53db77471fb946aa55c534804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Thu, 6 Feb 2020 09:32:26 +0100 Subject: [PATCH 04/25] New collapsing navigation --- frontend/packages/app/src/App.tsx | 2 +- .../app/src/components/SideBar/SideBar.tsx | 253 ++++++++++++++---- .../packages/core/src/layout/Header/Header.js | 15 +- 3 files changed, 218 insertions(+), 52 deletions(-) diff --git a/frontend/packages/app/src/App.tsx b/frontend/packages/app/src/App.tsx index a814c94a57..5c88f60c7d 100644 --- a/frontend/packages/app/src/App.tsx +++ b/frontend/packages/app/src/App.tsx @@ -41,7 +41,7 @@ const useStyles = makeStyles(theme => ({ root: { display: 'grid', // FIXME: Don't used a fixed width here - gridTemplateColumns: '224px auto', + gridTemplateColumns: '64px auto', gridTemplateRows: 'auto 1fr', width: '100%', height: '100vh', diff --git a/frontend/packages/app/src/components/SideBar/SideBar.tsx b/frontend/packages/app/src/components/SideBar/SideBar.tsx index 4bf19afcf1..395ae45410 100644 --- a/frontend/packages/app/src/components/SideBar/SideBar.tsx +++ b/frontend/packages/app/src/components/SideBar/SideBar.tsx @@ -1,60 +1,221 @@ -import Avatar from '@material-ui/core/Avatar'; -import Grid from '@material-ui/core/Grid'; -import Link from '@material-ui/core/Link'; -import List from '@material-ui/core/List'; -import ListItem from '@material-ui/core/ListItem'; -import ListItemText from '@material-ui/core/ListItemText'; -import { makeStyles } from '@material-ui/core/styles'; -import Typography from '@material-ui/core/Typography'; -import React, { FC } from 'react'; +import React, { FC, useState, createContext, useContext } from 'react'; +import clsx from 'clsx'; +import { + makeStyles, + SvgIcon, + Typography, + Link, + styled, +} from '@material-ui/core'; + +import HomeIcon from '@material-ui/icons/Home'; +import ServiceIcon from '@material-ui/icons/DeviceHub'; +import WebIcon from '@material-ui/icons/Language'; +import LibIcon from '@material-ui/icons/LocalLibrary'; +import CreateIcon from '@material-ui/icons/AddCircleOutline'; + +const drawerWidthClosed = 64; +const drawerWidthOpen = 220; + +const Context = createContext(false); + +const useSidebarItemStyles = makeStyles({ + root: { + color: '#b5b5b5', + display: 'flex', + flexFlow: 'row nowrap', + alignItems: 'center', + height: 40, + cursor: 'pointer', + }, + closed: { + width: drawerWidthClosed, + justifyContent: 'center', + }, + open: { + width: drawerWidthOpen, + }, + iconContainer: { + height: '100%', + width: drawerWidthClosed, + marginRight: -16, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, +}); + +type SidebarItemProps = { + icon: typeof SvgIcon; + text: string; + to?: string; + onClick?: () => void; +}; + +const SidebarItem: FC = ({ + icon: Icon, + text, + to, + onClick, +}) => { + const classes = useSidebarItemStyles(); + const open = useContext(Context); + + if (!open) { + return ( + + + + ); + } + + return ( + +
+ +
+ {text} + + ); +}; + +const useSidebarLogoStyles = makeStyles({ + root: { + height: drawerWidthClosed, + color: '#fff', + display: 'flex', + flexFlow: 'row nowrap', + alignItems: 'center', + }, + logoContainer: { + width: drawerWidthClosed, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + logo: { + fontSize: 32, + }, + title: { + fontSize: 24, + fontWeight: 'bold', + marginLeft: 22, + whiteSpace: 'nowrap', + }, + titleDot: { + color: '#1DB954', + }, +}); + +const SidebarLogo: FC<{}> = () => { + const classes = useSidebarLogoStyles(); + const open = useContext(Context); + + return ( +
+ + {open ? 'Backstage' : 'B'} + . + +
+ ); +}; + +const Space = styled('div')({ + flex: 1, +}); + +const Spacer = styled('div')({ + height: 8, +}); + +const Divider = styled('hr')({ + height: 1, + width: '100%', + background: '#383838', + border: 'none', +}); const useStyles = makeStyles(theme => ({ root: { - background: '#181818', - color: 'white', + zIndex: 1000, + position: 'relative', + overflow: 'visible', + width: theme.spacing(7) + 1, height: '100vh', - paddingLeft: theme.spacing(2), - paddingTop: theme.spacing(2), + }, + drawer: { + display: 'flex', + flexFlow: 'column nowrap', + alignItems: 'flex-start', + position: 'absolute', + left: 0, + top: 0, + bottom: 0, + padding: 0, + background: '#171717', + overflowX: 'hidden', + width: drawerWidthClosed, + transition: theme.transitions.create('width', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.shortest, + }), + }, + drawerOpen: { + width: drawerWidthOpen, + transition: theme.transitions.create('width', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.shorter, + }), }, })); const SideBar: FC<{}> = () => { const classes = useStyles(); + const [open, setOpen] = useState(false); + + const handleOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; return ( -
- - [Co-brand Logo] - - - - - - - - - - - - - - - - - - - - - A - - $userName - +
+ +
+ + + + + + + + + + + +
+
); }; diff --git a/frontend/packages/core/src/layout/Header/Header.js b/frontend/packages/core/src/layout/Header/Header.js index f78e43ee76..f9441dca12 100644 --- a/frontend/packages/core/src/layout/Header/Header.js +++ b/frontend/packages/core/src/layout/Header/Header.js @@ -26,9 +26,9 @@ class Header extends Component { return typeLink ? ( // - {type} - // + {type} ) : ( + // {type} ); } @@ -69,12 +69,17 @@ class Header extends Component { const { title, pageTitleOverride, children, style, classes } = this.props; const pageTitle = pageTitleOverride || title; if (typeof pageTitle !== 'string') { - console.warn('
title prop is not a string, pageTitleOverride should be provided.'); + console.warn( + '
title prop is not a string, pageTitleOverride should be provided.', + ); } return ( - + {theme => (
@@ -98,7 +103,7 @@ const styles = theme => ({ gridArea: 'pageHeader', padding: theme.spacing(3), minHeight: 118, - width: 'calc(100vw - 224px)', + width: '100vw', boxShadow: '0 0 8px 3px rgba(20, 20, 20, 0.3)', position: 'relative', zIndex: 100, From 07bbd89deedb95a69ecc7f3c6a69748aced52dd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Thu, 6 Feb 2020 09:38:27 +0100 Subject: [PATCH 05/25] Update Header.js --- frontend/packages/core/src/layout/Header/Header.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/packages/core/src/layout/Header/Header.js b/frontend/packages/core/src/layout/Header/Header.js index f9441dca12..5ddb7865e4 100644 --- a/frontend/packages/core/src/layout/Header/Header.js +++ b/frontend/packages/core/src/layout/Header/Header.js @@ -122,6 +122,7 @@ const styles = theme => ({ flexDirection: 'row', flexWrap: 'wrap', alignItems: 'center', + marginRight: theme.spacing(6), }, title: { color: theme.palette.bursts.fontColor, From 940d472df104568c6b6cf23b2f5e6dbdaf37b33b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 6 Feb 2020 10:00:28 +0100 Subject: [PATCH 06/25] frontend/core: simplify plugin output handling --- frontend/packages/app/src/entities/index.ts | 8 +- frontend/packages/core/src/api/api.ts | 6 +- .../packages/core/src/api/app/AppBuilder.tsx | 31 ++++- ...wPageBuilder.tsx => EntityPageBuilder.tsx} | 86 ++++++++------ .../packages/core/src/api/plugin/Plugin.tsx | 106 +++++++++--------- .../core/src/api/plugin/PluginOutputHook.ts | 11 -- .../packages/core/src/api/plugin/outputs.ts | 8 -- .../packages/core/src/api/plugin/types.ts | 41 +++++++ .../src/components/EntityLink/EntityLink.tsx | 2 +- .../src/components/BuildPage/BuildPage.tsx | 22 ---- .../src/components/BuildPage/index.ts | 1 - .../plugins/github-actions/src/plugin.ts | 14 +-- 12 files changed, 187 insertions(+), 149 deletions(-) rename frontend/packages/core/src/api/entityView/{EntityViewPageBuilder.tsx => EntityPageBuilder.tsx} (52%) delete mode 100644 frontend/packages/core/src/api/plugin/PluginOutputHook.ts delete mode 100644 frontend/packages/core/src/api/plugin/outputs.ts create mode 100644 frontend/packages/core/src/api/plugin/types.ts delete mode 100644 frontend/packages/plugins/github-actions/src/components/BuildPage/BuildPage.tsx delete mode 100644 frontend/packages/plugins/github-actions/src/components/BuildPage/index.ts diff --git a/frontend/packages/app/src/entities/index.ts b/frontend/packages/app/src/entities/index.ts index 8b42342e5d..38e529d87f 100644 --- a/frontend/packages/app/src/entities/index.ts +++ b/frontend/packages/app/src/entities/index.ts @@ -1,7 +1,7 @@ import { createEntityKind, createWidgetView, - createEntityView, + createEntityPage, } from '@backstage/core'; import ComputerIcon from '@material-ui/icons/Computer'; import MockEntityPage from './MockEntityPage'; @@ -13,10 +13,10 @@ const serviceOverviewPage = createWidgetView() .addComponent(MockEntityCard) .addComponent(MockEntityCard); -const serviceView = createEntityView() - .addPage('Overview', 'overview', serviceOverviewPage) +const serviceView = createEntityPage() + .addPage('Overview', '/overview', serviceOverviewPage) .register(GithubActionsPlugin) - .addComponent('Deployment', 'deployment', MockEntityPage); + .addComponent('Deployment', '/deployment', MockEntityPage); const serviceEntity = createEntityKind({ kind: 'service', diff --git a/frontend/packages/core/src/api/api.ts b/frontend/packages/core/src/api/api.ts index 73ca5c1da3..9bff1e2a46 100644 --- a/frontend/packages/core/src/api/api.ts +++ b/frontend/packages/core/src/api/api.ts @@ -1,7 +1,7 @@ import AppBuilder from './app/AppBuilder'; import EntityKind, { EntityConfig } from './entity/EntityKind'; import WidgetViewBuilder from './widgetView/WidgetViewBuilder'; -import EntityViewBuilder from './entityView/EntityViewPageBuilder'; +import EntityPageBuilder from './entityView/EntityPageBuilder'; import BackstagePlugin, { PluginConfig } from './plugin/Plugin'; export function createApp() { @@ -16,8 +16,8 @@ export function createWidgetView() { return new WidgetViewBuilder(); } -export function createEntityView() { - return new EntityViewBuilder(); +export function createEntityPage() { + return new EntityPageBuilder(); } export function createPlugin(config: PluginConfig): BackstagePlugin { diff --git a/frontend/packages/core/src/api/app/AppBuilder.tsx b/frontend/packages/core/src/api/app/AppBuilder.tsx index 349d9a7b06..1d2f9deb4d 100644 --- a/frontend/packages/core/src/api/app/AppBuilder.tsx +++ b/frontend/packages/core/src/api/app/AppBuilder.tsx @@ -1,10 +1,10 @@ import React, { ComponentType, FC } from 'react'; -import { Route, Switch, useParams } from 'react-router-dom'; +import { Route, Switch, useParams, Redirect } from 'react-router-dom'; import { AppContextProvider } from './AppContext'; import { App, AppComponentBuilder } from './types'; import EntityKind, { EntityConfig } from '../entity/EntityKind'; import { EntityContextProvider } from '../entityView/EntityContext'; -import BackstagePlugin, { registerSymbol } from '../plugin/Plugin'; +import BackstagePlugin from '../plugin/Plugin'; class AppImpl implements App { constructor(private readonly entities: Map) {} @@ -97,8 +97,31 @@ export default class AppBuilder { const pluginRoutes = new Array(); for (const plugin of this.plugins.values()) { - const { routes = [] } = plugin[registerSymbol](); - pluginRoutes.push(...routes); + for (const output of plugin.output()) { + switch (output.type) { + case 'route': { + const { path, component, options = {} } = output; + const { exact = true } = options; + pluginRoutes.push( + , + ); + break; + } + case 'redirect-route': { + const { path, target, options = {} } = output; + const { exact = true } = options; + pluginRoutes.push( + , + ); + break; + } + } + } } const routes = [...pluginRoutes, ...entityRoutes]; diff --git a/frontend/packages/core/src/api/entityView/EntityViewPageBuilder.tsx b/frontend/packages/core/src/api/entityView/EntityPageBuilder.tsx similarity index 52% rename from frontend/packages/core/src/api/entityView/EntityViewPageBuilder.tsx rename to frontend/packages/core/src/api/entityView/EntityPageBuilder.tsx index e4089e16d7..8dbf6ba2a0 100644 --- a/frontend/packages/core/src/api/entityView/EntityViewPageBuilder.tsx +++ b/frontend/packages/core/src/api/entityView/EntityPageBuilder.tsx @@ -5,8 +5,7 @@ import ListItem from '@material-ui/core/ListItem'; import { AppComponentBuilder, App } from '../app/types'; import { useEntity, useEntityUri, useEntityConfig } from './EntityContext'; import EntityLink from '../../components/EntityLink/EntityLink'; -import BackstagePlugin, { outputSymbol } from '../plugin/Plugin'; -import { entityViewPage } from '../plugin/outputs'; +import BackstagePlugin from '../plugin/Plugin'; const EntityLayout: FC<{}> = ({ children }) => { const config = useEntityConfig(); @@ -34,43 +33,48 @@ const EntitySidebarItem: FC<{ title: string; path: string }> = ({ ); }; -type EntityViewPage = { +type EntityPageNavItem = { title: string; + target: string; +}; + +type EntityPageView = { path: string; component: ComponentType; }; type Props = { - pages: EntityViewPage[]; + navItems: EntityPageNavItem[]; + views: EntityPageView[]; }; -const EntityViewComponent: FC = ({ pages }) => { +const EntityPageComponent: FC = ({ navItems, views }) => { const { kind, id } = useEntity(); const basePath = `/entity/${kind}/${id}`; return ( - {pages.map(({ title, path }) => ( - + {navItems.map(({ title, target }) => ( + ))} - {pages.map(({ path, component }) => ( + {views.map(({ path, component }) => ( ))} - + ); }; -type EntityViewRegistration = +type EntityPageRegistration = | { type: 'page'; title: string; @@ -88,14 +92,14 @@ type EntityViewRegistration = component: ComponentType; }; -export default class EntityViewBuilder extends AppComponentBuilder { - private readonly registrations = new Array(); +export default class EntityPageBuilder extends AppComponentBuilder { + private readonly registrations = new Array(); addPage( title: string, path: string, page: AppComponentBuilder, - ): EntityViewBuilder { + ): EntityPageBuilder { this.registrations.push({ type: 'page', title, path, page }); return this; } @@ -104,42 +108,60 @@ export default class EntityViewBuilder extends AppComponentBuilder { title: string, path: string, component: ComponentType, - ): EntityViewBuilder { + ): EntityPageBuilder { this.registrations.push({ type: 'component', title, path, component }); return this; } - register(plugin: BackstagePlugin): EntityViewBuilder { + register(plugin: BackstagePlugin): EntityPageBuilder { this.registrations.push({ type: 'plugin', plugin }); return this; } build(app: App): ComponentType { - const pages = this.registrations.map(registration => { - switch (registration.type) { + const navItems = new Array(); + const views = new Array(); + + for (const reg of this.registrations) { + switch (reg.type) { case 'page': { - const { title, path, page } = registration; - return { title, path, component: page.build(app) }; + const { title, path, page } = reg; + navItems.push({ title, target: path }); + views.push({ path, component: page.build(app) }); + break; } case 'component': { - const { title, path, component } = registration; - return { title, path, component }; + const { title, path, component } = reg; + navItems.push({ title, target: path }); + views.push({ path, component }); + break; } case 'plugin': { - const { plugin } = registration; - const output = plugin[outputSymbol](entityViewPage); - if (!output) { + let added = false; + for (const output of reg.plugin.output()) { + switch (output.type) { + case 'entity-page-nav-item': + const { title, target } = output; + navItems.push({ title, target }); + added = true; + break; + case 'entity-page-view-route': + const { path, component } = output; + views.push({ path, component }); + added = true; + break; + } + } + if (!added) { throw new Error( - `Plugin ${plugin} was registered as entity view, but did not have any output`, + `Plugin ${reg.plugin} was registered as entity view, but did not provide any output`, ); } - return output; + break; } - default: - throw new Error(`Unknown EntityViewBuilder registration`); } - }); + } - return () => ; + return () => ; } } diff --git a/frontend/packages/core/src/api/plugin/Plugin.tsx b/frontend/packages/core/src/api/plugin/Plugin.tsx index a5714fb009..e351801047 100644 --- a/frontend/packages/core/src/api/plugin/Plugin.tsx +++ b/frontend/packages/core/src/api/plugin/Plugin.tsx @@ -1,6 +1,5 @@ -import React from 'react'; -import { Route, Redirect } from 'react-router-dom'; -import PluginOutputHook from './PluginOutputHook'; +import { ComponentType } from 'react'; +import { PluginOutput, RoutePath, RouteOptions } from './types'; export type PluginConfig = { id: string; @@ -8,101 +7,98 @@ export type PluginConfig = { }; export type PluginHooks = { - router: Router; - provide(ref: PluginOutputHook, value: T): void; + router: RouterHooks; + entityPage: EntityPageHooks; }; -export type RouteOptions = { - // Whether the route path must match exactly, defaults to true. - exact?: boolean; -}; - -export type RedirectOptions = { - // Whether the route path must match exactly, defaults to true. - exact?: boolean; -}; - -export type Router = { +export type RouterHooks = { registerRoute( - path: string, - Component: React.ComponentType, + path: RoutePath, + Component: ComponentType, options?: RouteOptions, ): void; + registerRedirect( - path: string, - target: string, - options?: RedirectOptions, + path: RoutePath, + target: RoutePath, + options?: RouteOptions, ): void; }; -export type PluginRegistrationResult = { - routes?: JSX.Element[]; - outputs?: Map, any>; +type EntityPageSidebarItemOptions = { + title: string; + target: RoutePath; +}; + +export type EntityPageHooks = { + navItem(options: EntityPageSidebarItemOptions): void; + route( + path: RoutePath, + component: ComponentType, + options?: RouteOptions, + ): void; }; export const registerSymbol = Symbol('plugin-register'); export const outputSymbol = Symbol('plugin-output'); export default class Plugin { - private result?: PluginRegistrationResult; + private storedOutput?: PluginOutput[]; constructor(private readonly config: PluginConfig) {} - [registerSymbol](): PluginRegistrationResult { - if (this.result) { - return this.result; + output(): PluginOutput[] { + if (this.storedOutput) { + return this.storedOutput; } if (!this.config.register) { - return {}; + return []; } const { id } = this.config; - const routes = new Array(); - const outputs = new Map, any>(); + const outputs = new Array(); this.config.register({ router: { - registerRoute(path, component, options = {}) { + registerRoute(path, component, options) { if (path.startsWith('/entity/')) { throw new Error( `Plugin ${id} tried to register forbidden route ${path}`, ); } - const { exact = true } = options; - routes.push( - , - ); + outputs.push({ type: 'route', path, component, options }); }, - registerRedirect(path, target, options = {}) { + registerRedirect(path, target, options) { if (path.startsWith('/entity/')) { throw new Error( `Plugin ${id} tried to register forbidden redirect ${path}`, ); } - const { exact = true } = options; - routes.push( - , - ); + outputs.push({ type: 'redirect-route', path, target, options }); }, }, - provide(hook, value) { - outputs.set(hook, value); + entityPage: { + navItem({ title, target }) { + outputs.push({ + type: 'entity-page-nav-item', + target, + title, + }); + }, + route(path, component, options) { + outputs.push({ + type: 'entity-page-view-route', + path, + component, + options, + }); + }, }, }); - this.result = { routes, outputs }; - return this.result; - } - - [outputSymbol](outputHook: PluginOutputHook): T | undefined { - const { outputs } = this[registerSymbol](); - return outputs?.get(outputHook) as T; + this.storedOutput = outputs; + return this.storedOutput; } toString() { diff --git a/frontend/packages/core/src/api/plugin/PluginOutputHook.ts b/frontend/packages/core/src/api/plugin/PluginOutputHook.ts deleted file mode 100644 index 2780117d24..0000000000 --- a/frontend/packages/core/src/api/plugin/PluginOutputHook.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default class PluginOutputHook { - constructor(private readonly name: string) {} - - get T(): T { - throw new Error('use typeof instead'); - } - - toString() { - return `pluginOutput{${this.name}}`; - } -} diff --git a/frontend/packages/core/src/api/plugin/outputs.ts b/frontend/packages/core/src/api/plugin/outputs.ts deleted file mode 100644 index 59c4555cbf..0000000000 --- a/frontend/packages/core/src/api/plugin/outputs.ts +++ /dev/null @@ -1,8 +0,0 @@ -import PluginOutputHook from './PluginOutputHook'; -import { ComponentType } from 'react'; - -export const entityViewPage = new PluginOutputHook<{ - title: string; - path: string; - component: ComponentType; -}>('entity-view-page'); diff --git a/frontend/packages/core/src/api/plugin/types.ts b/frontend/packages/core/src/api/plugin/types.ts new file mode 100644 index 0000000000..ddc6364ec5 --- /dev/null +++ b/frontend/packages/core/src/api/plugin/types.ts @@ -0,0 +1,41 @@ +import { ComponentType } from 'react'; + +export type RouteOptions = { + // Whether the route path must match exactly, defaults to true. + exact?: boolean; +}; + +export type RoutePath = string; + +export type RouteOutput = { + type: 'route'; + path: RoutePath; + component: ComponentType<{}>; + options?: RouteOptions; +}; + +export type RedirectRouteOutput = { + type: 'redirect-route'; + path: RoutePath; + target: RoutePath; + options?: RouteOptions; +}; + +export type EntityPageViewRouteOutput = { + type: 'entity-page-view-route'; + path: RoutePath; + component: ComponentType; + options?: RouteOptions; +}; + +export type EntityPageNavItemOutput = { + type: 'entity-page-nav-item'; + title: string; + target: RoutePath; +}; + +export type PluginOutput = + | RouteOutput + | RedirectRouteOutput + | EntityPageViewRouteOutput + | EntityPageNavItemOutput; diff --git a/frontend/packages/core/src/components/EntityLink/EntityLink.tsx b/frontend/packages/core/src/components/EntityLink/EntityLink.tsx index a060cb1719..04b070e397 100644 --- a/frontend/packages/core/src/components/EntityLink/EntityLink.tsx +++ b/frontend/packages/core/src/components/EntityLink/EntityLink.tsx @@ -16,7 +16,7 @@ type Props = { export function buildPath(kind: string, id?: string, subPath?: string) { if (id) { if (subPath) { - return `/entity/${kind}/${id}/${subPath}`; + return `/entity/${kind}/${id}/${subPath.replace(/^\//, '')}`; } return `/entity/${kind}/${id}`; } diff --git a/frontend/packages/plugins/github-actions/src/components/BuildPage/BuildPage.tsx b/frontend/packages/plugins/github-actions/src/components/BuildPage/BuildPage.tsx deleted file mode 100644 index 6fff20640a..0000000000 --- a/frontend/packages/plugins/github-actions/src/components/BuildPage/BuildPage.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React, { FC } from 'react'; -import { Switch, Route } from 'react-router-dom'; -import BuildListPage from '../BuildListPage'; -import BuildDetailsPage from '../BuildDetailsPage'; -import { useRouteMatch } from 'react-router-dom'; - -const BuildPage: FC<{}> = () => { - const match = useRouteMatch(); - return ( - - ( - - )} - /> - - - ); -}; - -export default BuildPage; diff --git a/frontend/packages/plugins/github-actions/src/components/BuildPage/index.ts b/frontend/packages/plugins/github-actions/src/components/BuildPage/index.ts deleted file mode 100644 index 092d2e639d..0000000000 --- a/frontend/packages/plugins/github-actions/src/components/BuildPage/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './BuildPage'; diff --git a/frontend/packages/plugins/github-actions/src/plugin.ts b/frontend/packages/plugins/github-actions/src/plugin.ts index 359ba2e7f9..cecf142ca6 100644 --- a/frontend/packages/plugins/github-actions/src/plugin.ts +++ b/frontend/packages/plugins/github-actions/src/plugin.ts @@ -1,6 +1,6 @@ import { createPlugin } from '@backstage/core'; -import { entityViewPage } from '@backstage/core/src/api/plugin/outputs'; -import BuildPage from './components/BuildPage'; +import BuildDetailsPage from './components/BuildDetailsPage'; +import BuildListPage from './components/BuildListPage'; // export const buildListRoute = createEntityRoute<[]>('/builds') // export const buildDetailsRoute = createEntityRoute<[number]>('/builds/:buildId') @@ -8,11 +8,9 @@ import BuildPage from './components/BuildPage'; export default createPlugin({ id: 'github-actions', - register({ provide }) { - provide(entityViewPage, { - title: 'CI/CD', - path: 'builds', - component: BuildPage, - }); + register({ entityPage }) { + entityPage.navItem({ title: 'CI/CD', target: '/builds' }); + entityPage.route('/builds', BuildListPage); + entityPage.route('/builds/:buildId', BuildDetailsPage); }, }); From 5be413a44d242edf437bac3bd67e3070e5b5c090 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Wed, 5 Feb 2020 15:49:58 +0100 Subject: [PATCH 07/25] Added a @backstage/protobuf-definitions package --- frontend/packages/proto/package.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 frontend/packages/proto/package.json diff --git a/frontend/packages/proto/package.json b/frontend/packages/proto/package.json new file mode 100644 index 0000000000..ec3f152a7a --- /dev/null +++ b/frontend/packages/proto/package.json @@ -0,0 +1,7 @@ +{ + "name": "@backstage/protobuf-definitions", + "version": "0.0.0", + "devDependencies": { + "ts-protoc-gen": "^0.12.0" + } +} From 94c72a5ea0c82c42aee676b2a68422f71ad8fd93 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Wed, 5 Feb 2020 15:50:15 +0100 Subject: [PATCH 08/25] Ran yarn install --- frontend/yarn.lock | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index d52677c0d4..09e79a8aca 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -7577,6 +7577,11 @@ google-protobuf@^3.11.2: resolved "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.11.2.tgz#43272974521a5cec35a21f62730cf517a5a8e38c" integrity sha512-T4fin7lcYLUPj2ChUZ4DvfuuHtg3xi1621qeRZt2J7SvOQusOzq+sDT4vbotWTCjUXJoR36CA016LlhtPy80uQ== +google-protobuf@^3.6.1: + version "3.11.3" + resolved "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.11.3.tgz#660977f5de29cc8f647172a170602887102fa677" + integrity sha512-Sp8E+0AJLxmiPwAk9VH3MkYAmYYheNUhywIyXOS7wvRkqbIYcHtGzJzIYicNqYsqgKmY35F9hxRkI+ZTqTB4Tg== + got@^6.7.1: version "6.7.1" resolved "https://registry.npmjs.org/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" @@ -15712,7 +15717,14 @@ ts-pnp@1.1.5, ts-pnp@^1.1.2: resolved "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.5.tgz#840e0739c89fce5f3abd9037bb091dbff16d9dec" integrity sha512-ti7OGMOUOzo66wLF3liskw6YQIaSsBgc4GOAlWRnIEj8htCxJUxskanMUoJOD6MDCRAXo36goXJZch+nOS0VMA== -tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: +ts-protoc-gen@^0.12.0: + version "0.12.0" + resolved "https://registry.npmjs.org/ts-protoc-gen/-/ts-protoc-gen-0.12.0.tgz#932e5738f14b67e7202825b06f8c548cb7d8ef34" + integrity sha512-V7jnICJxKqalBrnJSMTW5tB9sGi48gOC325bfcM7TDNUItVOlaMM//rQmuo49ybipk/SyJTnWXgtJnhHCevNJw== + dependencies: + google-protobuf "^3.6.1" + +tslib@^1.8.1, tslib@^1.9.0: version "1.10.0" resolved "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== From b6be0d0f25c03ce759c073d963076b16e8f93f37 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Wed, 5 Feb 2020 15:50:34 +0100 Subject: [PATCH 09/25] Added protobuf-gen-ts to prototool.yaml --- proto/prototool.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proto/prototool.yaml b/proto/prototool.yaml index 458431a36c..b22c4b26eb 100644 --- a/proto/prototool.yaml +++ b/proto/prototool.yaml @@ -1,4 +1,3 @@ - protoc: version: 3.8.0 lint: @@ -16,3 +15,6 @@ generate: type: go flags: plugins=grpc output: ../backend/proto + - name: ts + path: ../frontend/node_modules/ts-protoc-gen/bin/protoc-gen-ts + output: ../frontend/packages/proto/src/generated From 7b039442841844beb049351dba90b520606f8893 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Wed, 5 Feb 2020 15:56:12 +0100 Subject: [PATCH 10/25] Added service=grpc-web flag --- proto/prototool.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/proto/prototool.yaml b/proto/prototool.yaml index b22c4b26eb..44c532aa13 100644 --- a/proto/prototool.yaml +++ b/proto/prototool.yaml @@ -18,3 +18,4 @@ generate: - name: ts path: ../frontend/node_modules/ts-protoc-gen/bin/protoc-gen-ts output: ../frontend/packages/proto/src/generated + flags: service=grpc-web From fdfcdae4e4cd3a8a4a2cba572e72492d7ab842bc Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Wed, 5 Feb 2020 15:56:22 +0100 Subject: [PATCH 11/25] Added generated files --- .../generated/identity/v1/identity_pb.d.ts | 151 ++++++++++++++++++ .../identity/v1/identity_pb_service.d.ts | 82 ++++++++++ .../identity/v1/identity_pb_service.js | 101 ++++++++++++ .../generated/inventory/v1/inventory_pb.d.ts | 149 +++++++++++++++++ .../inventory/v1/inventory_pb_service.d.ts | 82 ++++++++++ .../inventory/v1/inventory_pb_service.js | 101 ++++++++++++ .../scaffolder/v1/scaffolder_pb.d.ts | 74 +++++++++ .../scaffolder/v1/scaffolder_pb_service.d.ts | 63 ++++++++ .../scaffolder/v1/scaffolder_pb_service.js | 61 +++++++ 9 files changed, 864 insertions(+) create mode 100644 frontend/packages/proto/src/generated/identity/v1/identity_pb.d.ts create mode 100644 frontend/packages/proto/src/generated/identity/v1/identity_pb_service.d.ts create mode 100644 frontend/packages/proto/src/generated/identity/v1/identity_pb_service.js create mode 100644 frontend/packages/proto/src/generated/inventory/v1/inventory_pb.d.ts create mode 100644 frontend/packages/proto/src/generated/inventory/v1/inventory_pb_service.d.ts create mode 100644 frontend/packages/proto/src/generated/inventory/v1/inventory_pb_service.js create mode 100644 frontend/packages/proto/src/generated/scaffolder/v1/scaffolder_pb.d.ts create mode 100644 frontend/packages/proto/src/generated/scaffolder/v1/scaffolder_pb_service.d.ts create mode 100644 frontend/packages/proto/src/generated/scaffolder/v1/scaffolder_pb_service.js diff --git a/frontend/packages/proto/src/generated/identity/v1/identity_pb.d.ts b/frontend/packages/proto/src/generated/identity/v1/identity_pb.d.ts new file mode 100644 index 0000000000..163e30b52c --- /dev/null +++ b/frontend/packages/proto/src/generated/identity/v1/identity_pb.d.ts @@ -0,0 +1,151 @@ +// package: spotify.backstage.identity.v1 +// file: identity/v1/identity.proto + +import * as jspb from "google-protobuf"; + +export class GetUserRequest extends jspb.Message { + getId(): string; + setId(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): GetUserRequest.AsObject; + static toObject(includeInstance: boolean, msg: GetUserRequest): GetUserRequest.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: GetUserRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetUserRequest; + static deserializeBinaryFromReader(message: GetUserRequest, reader: jspb.BinaryReader): GetUserRequest; +} + +export namespace GetUserRequest { + export type AsObject = { + id: string, + } +} + +export class GetUserReply extends jspb.Message { + hasUser(): boolean; + clearUser(): void; + getUser(): User | undefined; + setUser(value?: User): void; + + clearGroupsList(): void; + getGroupsList(): Array; + setGroupsList(value: Array): void; + addGroups(value?: Group, index?: number): Group; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): GetUserReply.AsObject; + static toObject(includeInstance: boolean, msg: GetUserReply): GetUserReply.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: GetUserReply, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetUserReply; + static deserializeBinaryFromReader(message: GetUserReply, reader: jspb.BinaryReader): GetUserReply; +} + +export namespace GetUserReply { + export type AsObject = { + user?: User.AsObject, + groupsList: Array, + } +} + +export class GetGroupRequest extends jspb.Message { + getId(): string; + setId(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): GetGroupRequest.AsObject; + static toObject(includeInstance: boolean, msg: GetGroupRequest): GetGroupRequest.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: GetGroupRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetGroupRequest; + static deserializeBinaryFromReader(message: GetGroupRequest, reader: jspb.BinaryReader): GetGroupRequest; +} + +export namespace GetGroupRequest { + export type AsObject = { + id: string, + } +} + +export class GetGroupReply extends jspb.Message { + hasGroup(): boolean; + clearGroup(): void; + getGroup(): Group | undefined; + setGroup(value?: Group): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): GetGroupReply.AsObject; + static toObject(includeInstance: boolean, msg: GetGroupReply): GetGroupReply.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: GetGroupReply, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetGroupReply; + static deserializeBinaryFromReader(message: GetGroupReply, reader: jspb.BinaryReader): GetGroupReply; +} + +export namespace GetGroupReply { + export type AsObject = { + group?: Group.AsObject, + } +} + +export class User extends jspb.Message { + getId(): string; + setId(value: string): void; + + getName(): string; + setName(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): User.AsObject; + static toObject(includeInstance: boolean, msg: User): User.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: User, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): User; + static deserializeBinaryFromReader(message: User, reader: jspb.BinaryReader): User; +} + +export namespace User { + export type AsObject = { + id: string, + name: string, + } +} + +export class Group extends jspb.Message { + getId(): string; + setId(value: string): void; + + clearUsersList(): void; + getUsersList(): Array; + setUsersList(value: Array): void; + addUsers(value?: User, index?: number): User; + + clearGroupsList(): void; + getGroupsList(): Array; + setGroupsList(value: Array): void; + addGroups(value?: Group, index?: number): Group; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): Group.AsObject; + static toObject(includeInstance: boolean, msg: Group): Group.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: Group, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): Group; + static deserializeBinaryFromReader(message: Group, reader: jspb.BinaryReader): Group; +} + +export namespace Group { + export type AsObject = { + id: string, + usersList: Array, + groupsList: Array, + } +} + diff --git a/frontend/packages/proto/src/generated/identity/v1/identity_pb_service.d.ts b/frontend/packages/proto/src/generated/identity/v1/identity_pb_service.d.ts new file mode 100644 index 0000000000..9c9e80f37f --- /dev/null +++ b/frontend/packages/proto/src/generated/identity/v1/identity_pb_service.d.ts @@ -0,0 +1,82 @@ +// package: spotify.backstage.identity.v1 +// file: identity/v1/identity.proto + +import * as identity_v1_identity_pb from "../../identity/v1/identity_pb"; +import {grpc} from "@improbable-eng/grpc-web"; + +type IdentityGetUser = { + readonly methodName: string; + readonly service: typeof Identity; + readonly requestStream: false; + readonly responseStream: false; + readonly requestType: typeof identity_v1_identity_pb.GetUserRequest; + readonly responseType: typeof identity_v1_identity_pb.GetUserReply; +}; + +type IdentityGetGroup = { + readonly methodName: string; + readonly service: typeof Identity; + readonly requestStream: false; + readonly responseStream: false; + readonly requestType: typeof identity_v1_identity_pb.GetGroupRequest; + readonly responseType: typeof identity_v1_identity_pb.GetGroupReply; +}; + +export class Identity { + static readonly serviceName: string; + static readonly GetUser: IdentityGetUser; + static readonly GetGroup: IdentityGetGroup; +} + +export type ServiceError = { message: string, code: number; metadata: grpc.Metadata } +export type Status = { details: string, code: number; metadata: grpc.Metadata } + +interface UnaryResponse { + cancel(): void; +} +interface ResponseStream { + cancel(): void; + on(type: 'data', handler: (message: T) => void): ResponseStream; + on(type: 'end', handler: (status?: Status) => void): ResponseStream; + on(type: 'status', handler: (status: Status) => void): ResponseStream; +} +interface RequestStream { + write(message: T): RequestStream; + end(): void; + cancel(): void; + on(type: 'end', handler: (status?: Status) => void): RequestStream; + on(type: 'status', handler: (status: Status) => void): RequestStream; +} +interface BidirectionalStream { + write(message: ReqT): BidirectionalStream; + end(): void; + cancel(): void; + on(type: 'data', handler: (message: ResT) => void): BidirectionalStream; + on(type: 'end', handler: (status?: Status) => void): BidirectionalStream; + on(type: 'status', handler: (status: Status) => void): BidirectionalStream; +} + +export class IdentityClient { + readonly serviceHost: string; + + constructor(serviceHost: string, options?: grpc.RpcOptions); + getUser( + requestMessage: identity_v1_identity_pb.GetUserRequest, + metadata: grpc.Metadata, + callback: (error: ServiceError|null, responseMessage: identity_v1_identity_pb.GetUserReply|null) => void + ): UnaryResponse; + getUser( + requestMessage: identity_v1_identity_pb.GetUserRequest, + callback: (error: ServiceError|null, responseMessage: identity_v1_identity_pb.GetUserReply|null) => void + ): UnaryResponse; + getGroup( + requestMessage: identity_v1_identity_pb.GetGroupRequest, + metadata: grpc.Metadata, + callback: (error: ServiceError|null, responseMessage: identity_v1_identity_pb.GetGroupReply|null) => void + ): UnaryResponse; + getGroup( + requestMessage: identity_v1_identity_pb.GetGroupRequest, + callback: (error: ServiceError|null, responseMessage: identity_v1_identity_pb.GetGroupReply|null) => void + ): UnaryResponse; +} + diff --git a/frontend/packages/proto/src/generated/identity/v1/identity_pb_service.js b/frontend/packages/proto/src/generated/identity/v1/identity_pb_service.js new file mode 100644 index 0000000000..3fa97ae125 --- /dev/null +++ b/frontend/packages/proto/src/generated/identity/v1/identity_pb_service.js @@ -0,0 +1,101 @@ +// package: spotify.backstage.identity.v1 +// file: identity/v1/identity.proto + +var identity_v1_identity_pb = require("../../identity/v1/identity_pb"); +var grpc = require("@improbable-eng/grpc-web").grpc; + +var Identity = (function () { + function Identity() {} + Identity.serviceName = "spotify.backstage.identity.v1.Identity"; + return Identity; +}()); + +Identity.GetUser = { + methodName: "GetUser", + service: Identity, + requestStream: false, + responseStream: false, + requestType: identity_v1_identity_pb.GetUserRequest, + responseType: identity_v1_identity_pb.GetUserReply +}; + +Identity.GetGroup = { + methodName: "GetGroup", + service: Identity, + requestStream: false, + responseStream: false, + requestType: identity_v1_identity_pb.GetGroupRequest, + responseType: identity_v1_identity_pb.GetGroupReply +}; + +exports.Identity = Identity; + +function IdentityClient(serviceHost, options) { + this.serviceHost = serviceHost; + this.options = options || {}; +} + +IdentityClient.prototype.getUser = function getUser(requestMessage, metadata, callback) { + if (arguments.length === 2) { + callback = arguments[1]; + } + var client = grpc.unary(Identity.GetUser, { + request: requestMessage, + host: this.serviceHost, + metadata: metadata, + transport: this.options.transport, + debug: this.options.debug, + onEnd: function (response) { + if (callback) { + if (response.status !== grpc.Code.OK) { + var err = new Error(response.statusMessage); + err.code = response.status; + err.metadata = response.trailers; + callback(err, null); + } else { + callback(null, response.message); + } + } + } + }); + return { + cancel: function () { + callback = null; + client.close(); + } + }; +}; + +IdentityClient.prototype.getGroup = function getGroup(requestMessage, metadata, callback) { + if (arguments.length === 2) { + callback = arguments[1]; + } + var client = grpc.unary(Identity.GetGroup, { + request: requestMessage, + host: this.serviceHost, + metadata: metadata, + transport: this.options.transport, + debug: this.options.debug, + onEnd: function (response) { + if (callback) { + if (response.status !== grpc.Code.OK) { + var err = new Error(response.statusMessage); + err.code = response.status; + err.metadata = response.trailers; + callback(err, null); + } else { + callback(null, response.message); + } + } + } + }); + return { + cancel: function () { + callback = null; + client.close(); + } + }; +}; + +exports.IdentityClient = IdentityClient; + diff --git a/frontend/packages/proto/src/generated/inventory/v1/inventory_pb.d.ts b/frontend/packages/proto/src/generated/inventory/v1/inventory_pb.d.ts new file mode 100644 index 0000000000..754c935c2e --- /dev/null +++ b/frontend/packages/proto/src/generated/inventory/v1/inventory_pb.d.ts @@ -0,0 +1,149 @@ +// package: spotify.backstage.inventory.v1 +// file: inventory/v1/inventory.proto + +import * as jspb from "google-protobuf"; + +export class GetEntityRequest extends jspb.Message { + hasEntity(): boolean; + clearEntity(): void; + getEntity(): Entity | undefined; + setEntity(value?: Entity): void; + + clearIncludeFactsList(): void; + getIncludeFactsList(): Array; + setIncludeFactsList(value: Array): void; + addIncludeFacts(value: string, index?: number): string; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): GetEntityRequest.AsObject; + static toObject(includeInstance: boolean, msg: GetEntityRequest): GetEntityRequest.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: GetEntityRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetEntityRequest; + static deserializeBinaryFromReader(message: GetEntityRequest, reader: jspb.BinaryReader): GetEntityRequest; +} + +export namespace GetEntityRequest { + export type AsObject = { + entity?: Entity.AsObject, + includeFactsList: Array, + } +} + +export class GetEntityReply extends jspb.Message { + hasEntity(): boolean; + clearEntity(): void; + getEntity(): Entity | undefined; + setEntity(value?: Entity): void; + + clearFactsList(): void; + getFactsList(): Array; + setFactsList(value: Array): void; + addFacts(value?: Fact, index?: number): Fact; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): GetEntityReply.AsObject; + static toObject(includeInstance: boolean, msg: GetEntityReply): GetEntityReply.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: GetEntityReply, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetEntityReply; + static deserializeBinaryFromReader(message: GetEntityReply, reader: jspb.BinaryReader): GetEntityReply; +} + +export namespace GetEntityReply { + export type AsObject = { + entity?: Entity.AsObject, + factsList: Array, + } +} + +export class CreateEntityRequest extends jspb.Message { + hasEntity(): boolean; + clearEntity(): void; + getEntity(): Entity | undefined; + setEntity(value?: Entity): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): CreateEntityRequest.AsObject; + static toObject(includeInstance: boolean, msg: CreateEntityRequest): CreateEntityRequest.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: CreateEntityRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): CreateEntityRequest; + static deserializeBinaryFromReader(message: CreateEntityRequest, reader: jspb.BinaryReader): CreateEntityRequest; +} + +export namespace CreateEntityRequest { + export type AsObject = { + entity?: Entity.AsObject, + } +} + +export class CreateEntityReply extends jspb.Message { + hasEntity(): boolean; + clearEntity(): void; + getEntity(): Entity | undefined; + setEntity(value?: Entity): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): CreateEntityReply.AsObject; + static toObject(includeInstance: boolean, msg: CreateEntityReply): CreateEntityReply.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: CreateEntityReply, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): CreateEntityReply; + static deserializeBinaryFromReader(message: CreateEntityReply, reader: jspb.BinaryReader): CreateEntityReply; +} + +export namespace CreateEntityReply { + export type AsObject = { + entity?: Entity.AsObject, + } +} + +export class Entity extends jspb.Message { + getUri(): string; + setUri(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): Entity.AsObject; + static toObject(includeInstance: boolean, msg: Entity): Entity.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: Entity, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): Entity; + static deserializeBinaryFromReader(message: Entity, reader: jspb.BinaryReader): Entity; +} + +export namespace Entity { + export type AsObject = { + uri: string, + } +} + +export class Fact extends jspb.Message { + getName(): string; + setName(value: string): void; + + getValue(): string; + setValue(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): Fact.AsObject; + static toObject(includeInstance: boolean, msg: Fact): Fact.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: Fact, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): Fact; + static deserializeBinaryFromReader(message: Fact, reader: jspb.BinaryReader): Fact; +} + +export namespace Fact { + export type AsObject = { + name: string, + value: string, + } +} + diff --git a/frontend/packages/proto/src/generated/inventory/v1/inventory_pb_service.d.ts b/frontend/packages/proto/src/generated/inventory/v1/inventory_pb_service.d.ts new file mode 100644 index 0000000000..1b7ef69516 --- /dev/null +++ b/frontend/packages/proto/src/generated/inventory/v1/inventory_pb_service.d.ts @@ -0,0 +1,82 @@ +// package: spotify.backstage.inventory.v1 +// file: inventory/v1/inventory.proto + +import * as inventory_v1_inventory_pb from "../../inventory/v1/inventory_pb"; +import {grpc} from "@improbable-eng/grpc-web"; + +type InventoryGetEntity = { + readonly methodName: string; + readonly service: typeof Inventory; + readonly requestStream: false; + readonly responseStream: false; + readonly requestType: typeof inventory_v1_inventory_pb.GetEntityRequest; + readonly responseType: typeof inventory_v1_inventory_pb.GetEntityReply; +}; + +type InventoryCreateEntity = { + readonly methodName: string; + readonly service: typeof Inventory; + readonly requestStream: false; + readonly responseStream: false; + readonly requestType: typeof inventory_v1_inventory_pb.CreateEntityRequest; + readonly responseType: typeof inventory_v1_inventory_pb.CreateEntityReply; +}; + +export class Inventory { + static readonly serviceName: string; + static readonly GetEntity: InventoryGetEntity; + static readonly CreateEntity: InventoryCreateEntity; +} + +export type ServiceError = { message: string, code: number; metadata: grpc.Metadata } +export type Status = { details: string, code: number; metadata: grpc.Metadata } + +interface UnaryResponse { + cancel(): void; +} +interface ResponseStream { + cancel(): void; + on(type: 'data', handler: (message: T) => void): ResponseStream; + on(type: 'end', handler: (status?: Status) => void): ResponseStream; + on(type: 'status', handler: (status: Status) => void): ResponseStream; +} +interface RequestStream { + write(message: T): RequestStream; + end(): void; + cancel(): void; + on(type: 'end', handler: (status?: Status) => void): RequestStream; + on(type: 'status', handler: (status: Status) => void): RequestStream; +} +interface BidirectionalStream { + write(message: ReqT): BidirectionalStream; + end(): void; + cancel(): void; + on(type: 'data', handler: (message: ResT) => void): BidirectionalStream; + on(type: 'end', handler: (status?: Status) => void): BidirectionalStream; + on(type: 'status', handler: (status: Status) => void): BidirectionalStream; +} + +export class InventoryClient { + readonly serviceHost: string; + + constructor(serviceHost: string, options?: grpc.RpcOptions); + getEntity( + requestMessage: inventory_v1_inventory_pb.GetEntityRequest, + metadata: grpc.Metadata, + callback: (error: ServiceError|null, responseMessage: inventory_v1_inventory_pb.GetEntityReply|null) => void + ): UnaryResponse; + getEntity( + requestMessage: inventory_v1_inventory_pb.GetEntityRequest, + callback: (error: ServiceError|null, responseMessage: inventory_v1_inventory_pb.GetEntityReply|null) => void + ): UnaryResponse; + createEntity( + requestMessage: inventory_v1_inventory_pb.CreateEntityRequest, + metadata: grpc.Metadata, + callback: (error: ServiceError|null, responseMessage: inventory_v1_inventory_pb.CreateEntityReply|null) => void + ): UnaryResponse; + createEntity( + requestMessage: inventory_v1_inventory_pb.CreateEntityRequest, + callback: (error: ServiceError|null, responseMessage: inventory_v1_inventory_pb.CreateEntityReply|null) => void + ): UnaryResponse; +} + diff --git a/frontend/packages/proto/src/generated/inventory/v1/inventory_pb_service.js b/frontend/packages/proto/src/generated/inventory/v1/inventory_pb_service.js new file mode 100644 index 0000000000..02f48d037d --- /dev/null +++ b/frontend/packages/proto/src/generated/inventory/v1/inventory_pb_service.js @@ -0,0 +1,101 @@ +// package: spotify.backstage.inventory.v1 +// file: inventory/v1/inventory.proto + +var inventory_v1_inventory_pb = require("../../inventory/v1/inventory_pb"); +var grpc = require("@improbable-eng/grpc-web").grpc; + +var Inventory = (function () { + function Inventory() {} + Inventory.serviceName = "spotify.backstage.inventory.v1.Inventory"; + return Inventory; +}()); + +Inventory.GetEntity = { + methodName: "GetEntity", + service: Inventory, + requestStream: false, + responseStream: false, + requestType: inventory_v1_inventory_pb.GetEntityRequest, + responseType: inventory_v1_inventory_pb.GetEntityReply +}; + +Inventory.CreateEntity = { + methodName: "CreateEntity", + service: Inventory, + requestStream: false, + responseStream: false, + requestType: inventory_v1_inventory_pb.CreateEntityRequest, + responseType: inventory_v1_inventory_pb.CreateEntityReply +}; + +exports.Inventory = Inventory; + +function InventoryClient(serviceHost, options) { + this.serviceHost = serviceHost; + this.options = options || {}; +} + +InventoryClient.prototype.getEntity = function getEntity(requestMessage, metadata, callback) { + if (arguments.length === 2) { + callback = arguments[1]; + } + var client = grpc.unary(Inventory.GetEntity, { + request: requestMessage, + host: this.serviceHost, + metadata: metadata, + transport: this.options.transport, + debug: this.options.debug, + onEnd: function (response) { + if (callback) { + if (response.status !== grpc.Code.OK) { + var err = new Error(response.statusMessage); + err.code = response.status; + err.metadata = response.trailers; + callback(err, null); + } else { + callback(null, response.message); + } + } + } + }); + return { + cancel: function () { + callback = null; + client.close(); + } + }; +}; + +InventoryClient.prototype.createEntity = function createEntity(requestMessage, metadata, callback) { + if (arguments.length === 2) { + callback = arguments[1]; + } + var client = grpc.unary(Inventory.CreateEntity, { + request: requestMessage, + host: this.serviceHost, + metadata: metadata, + transport: this.options.transport, + debug: this.options.debug, + onEnd: function (response) { + if (callback) { + if (response.status !== grpc.Code.OK) { + var err = new Error(response.statusMessage); + err.code = response.status; + err.metadata = response.trailers; + callback(err, null); + } else { + callback(null, response.message); + } + } + } + }); + return { + cancel: function () { + callback = null; + client.close(); + } + }; +}; + +exports.InventoryClient = InventoryClient; + diff --git a/frontend/packages/proto/src/generated/scaffolder/v1/scaffolder_pb.d.ts b/frontend/packages/proto/src/generated/scaffolder/v1/scaffolder_pb.d.ts new file mode 100644 index 0000000000..a04b41d40a --- /dev/null +++ b/frontend/packages/proto/src/generated/scaffolder/v1/scaffolder_pb.d.ts @@ -0,0 +1,74 @@ +// package: spotify.backstage.scaffolder.v1 +// file: scaffolder/v1/scaffolder.proto + +import * as jspb from "google-protobuf"; +import * as identity_v1_identity_pb from "../../identity/v1/identity_pb"; + +export class Empty extends jspb.Message { + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): Empty.AsObject; + static toObject(includeInstance: boolean, msg: Empty): Empty.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: Empty, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): Empty; + static deserializeBinaryFromReader(message: Empty, reader: jspb.BinaryReader): Empty; +} + +export namespace Empty { + export type AsObject = { + } +} + +export class GetAllTemplatesResponse extends jspb.Message { + clearTemplatesList(): void; + getTemplatesList(): Array