From 564c0954ec08827baeead75a678cf60ea87eb760 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Thu, 30 Apr 2020 11:29:43 +0200 Subject: [PATCH] feat(circleci-plugin): nice table view --- .../src/components/CITable/CITable.tsx | 87 ++++++++++ .../circleci/src/components/CITable/index.ts | 1 + .../components/CircleCIFetch/CirleCIFetch.tsx | 88 ++++++++++ .../src/components/CircleCIFetch/index.ts | 1 + .../CircleCIPage.tsx} | 10 +- .../src/components/CircleCIPage/index.ts | 1 + .../ExampleComponent.test.tsx | 34 ---- .../src/components/ExampleComponent/index.ts | 17 -- .../ExampleFetchComponent.test.tsx | 28 --- .../ExampleFetchComponent.tsx | 161 ------------------ .../components/ExampleFetchComponent/index.ts | 17 -- plugins/circleci/src/plugin.ts | 4 +- 12 files changed, 185 insertions(+), 264 deletions(-) create mode 100644 plugins/circleci/src/components/CITable/CITable.tsx create mode 100644 plugins/circleci/src/components/CITable/index.ts create mode 100644 plugins/circleci/src/components/CircleCIFetch/CirleCIFetch.tsx create mode 100644 plugins/circleci/src/components/CircleCIFetch/index.ts rename plugins/circleci/src/components/{ExampleComponent/ExampleComponent.tsx => CircleCIPage/CircleCIPage.tsx} (87%) create mode 100644 plugins/circleci/src/components/CircleCIPage/index.ts delete mode 100644 plugins/circleci/src/components/ExampleComponent/ExampleComponent.test.tsx delete mode 100644 plugins/circleci/src/components/ExampleComponent/index.ts delete mode 100644 plugins/circleci/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx delete mode 100644 plugins/circleci/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx delete mode 100644 plugins/circleci/src/components/ExampleFetchComponent/index.ts diff --git a/plugins/circleci/src/components/CITable/CITable.tsx b/plugins/circleci/src/components/CITable/CITable.tsx new file mode 100644 index 0000000000..21cb03ce6c --- /dev/null +++ b/plugins/circleci/src/components/CITable/CITable.tsx @@ -0,0 +1,87 @@ +// Idea for this component to be somehow reusable representation of CI table view +import React, { FC } from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableContainer from '@material-ui/core/TableContainer'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; + +const useStyles = makeStyles({ + table: { + minWidth: 650, + }, + avatar: { + height: 32, + width: 32, + borderRadius: '50%', + }, +}); + +export type CITableBuildInfo = { + id: string; + buildName: string; + source: { + branchName: string; + commit: { + hash: string; + url: string; + }; + }; + status: 'success' | 'pending' | 'error'; + tests?: { + total: number; + passed: number; + skipped: number; + failed: number; + testUrl: string; //fixme better name + }; + onRetriggerClick: () => void; +}; + +export const CITable: FC<{ + builds: CITableBuildInfo[]; +}> = ({ builds }) => { + const classes = useStyles(); + + return ( + + + + + Build + Source + Status + Tests + Retrigger + + + + {builds.map(build => ( + + {build.buildName} + +
+
{build.source.branchName}
+
{build.source.commit.hash}
+
+
+ {build.status} + + {build.tests && ( + <> + {build.tests.passed}/{build.tests.total} ( + {build.tests.failed ? build.tests.failed + ', ' : ''} + {build.tests.skipped ? build.tests.skipped : ''}) + + )} + + Retrigger +
+ ))} +
+
+
+ ); +}; diff --git a/plugins/circleci/src/components/CITable/index.ts b/plugins/circleci/src/components/CITable/index.ts new file mode 100644 index 0000000000..279634bce6 --- /dev/null +++ b/plugins/circleci/src/components/CITable/index.ts @@ -0,0 +1 @@ +export { CITable, CITableBuildInfo } from './CITable'; diff --git a/plugins/circleci/src/components/CircleCIFetch/CirleCIFetch.tsx b/plugins/circleci/src/components/CircleCIFetch/CirleCIFetch.tsx new file mode 100644 index 0000000000..9ca7f93a0a --- /dev/null +++ b/plugins/circleci/src/components/CircleCIFetch/CirleCIFetch.tsx @@ -0,0 +1,88 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in wr iting, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { FC, useRef, useEffect } from 'react'; + +// import Alert from '@material-ui/lab/Alert'; +import { useAsync } from 'react-use'; +// import { Progress } from '@backstage/core'; + +import { CircleCI, GitType, CircleCIOptions, BuildSummary } from 'circleci-api'; + +import { CITable, CITableBuildInfo } from '../CITable'; +const options: Partial = { + // Required for all requests + // token: CIRCLECI_TOKEN, // Set your CircleCi API token + + // Optional + // Anything set here can be overriden when making the request + + // Git information is required for project/build/etc endpoints + vcs: { + type: GitType.GITHUB, // default: github + owner: 'CircleCITest3', + repo: 'circleci-test', + }, + + // Optional query params for requests + // options: { + // branch: "master", // default: master + // } +}; + +const transform = (buildsData: BuildSummary[]): CITableBuildInfo[] => { + return buildsData.map(buildData => { + const tableBuildInfo: CITableBuildInfo = { + id: String(buildData.build_num), + buildName: String(buildData.subject), + onRetriggerClick: () => null, + source: { + branchName: String(buildData.branch), + commit: { + hash: String(buildData.vcs_revision), + url: 'todo', + }, + }, + status: String(buildData.status) as 'success' | 'error' | 'pending', + tests: { + failed: 0, + passed: 10, + skipped: 3, + testUrl: 'nourlnow', + total: 13, + }, + }; + return tableBuildInfo; + }); +}; + +export const CircleCIFetch: FC<{ token: string }> = ({ token }) => { + const api = useRef(null); + useEffect(() => { + if (token !== '') api.current = new CircleCI({ ...options, token }); + }, [token]); + + const { value, loading, error } = useAsync(() => { + if (api.current) return api.current.builds(); + return Promise.reject('Api token not provided'); + }, [token]); + + if (loading) return
loading
; + if (error) return
{JSON.stringify(error, null, 2)}
; + + const builds = transform(value || []); + return ; +}; diff --git a/plugins/circleci/src/components/CircleCIFetch/index.ts b/plugins/circleci/src/components/CircleCIFetch/index.ts new file mode 100644 index 0000000000..e204a791cd --- /dev/null +++ b/plugins/circleci/src/components/CircleCIFetch/index.ts @@ -0,0 +1 @@ +export { CircleCIFetch } from './CirleCIFetch'; diff --git a/plugins/circleci/src/components/ExampleComponent/ExampleComponent.tsx b/plugins/circleci/src/components/CircleCIPage/CircleCIPage.tsx similarity index 87% rename from plugins/circleci/src/components/ExampleComponent/ExampleComponent.tsx rename to plugins/circleci/src/components/CircleCIPage/CircleCIPage.tsx index 966debd11f..f27ac80a6f 100644 --- a/plugins/circleci/src/components/ExampleComponent/ExampleComponent.tsx +++ b/plugins/circleci/src/components/CircleCIPage/CircleCIPage.tsx @@ -26,9 +26,9 @@ import { HeaderLabel, SupportButton, } from '@backstage/core'; -import ExampleFetchComponent from '../ExampleFetchComponent'; +import { CircleCIFetch } from '../CircleCIFetch'; -const ExampleComponent: FC<{}> = () => { +export const CircleCIPage: FC<{}> = () => { const [token, setToken] = useState(''); return ( @@ -55,8 +55,8 @@ const ExampleComponent: FC<{}> = () => { - - + + @@ -65,4 +65,4 @@ const ExampleComponent: FC<{}> = () => { ); }; -export default ExampleComponent; +export default CircleCIPage; diff --git a/plugins/circleci/src/components/CircleCIPage/index.ts b/plugins/circleci/src/components/CircleCIPage/index.ts new file mode 100644 index 0000000000..188ed6d61f --- /dev/null +++ b/plugins/circleci/src/components/CircleCIPage/index.ts @@ -0,0 +1 @@ +export { CircleCIPage } from './CircleCIPage'; diff --git a/plugins/circleci/src/components/ExampleComponent/ExampleComponent.test.tsx b/plugins/circleci/src/components/ExampleComponent/ExampleComponent.test.tsx deleted file mode 100644 index ef0f3f9632..0000000000 --- a/plugins/circleci/src/components/ExampleComponent/ExampleComponent.test.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react'; -import { render } from '@testing-library/react'; -import mockFetch from 'jest-fetch-mock'; -import ExampleComponent from './ExampleComponent'; -import { ThemeProvider } from '@material-ui/core'; -import { lightTheme } from '@backstage/theme'; - -describe('ExampleComponent', () => { - it('should render', () => { - mockFetch.mockResponse(() => new Promise(() => {})); - const rendered = render( - - - , - ); - expect(rendered.getByText('Welcome to circleci!')).toBeInTheDocument(); - }); -}); diff --git a/plugins/circleci/src/components/ExampleComponent/index.ts b/plugins/circleci/src/components/ExampleComponent/index.ts deleted file mode 100644 index e785d45082..0000000000 --- a/plugins/circleci/src/components/ExampleComponent/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export { default } from './ExampleComponent'; diff --git a/plugins/circleci/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx b/plugins/circleci/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx deleted file mode 100644 index 7fecdc6f11..0000000000 --- a/plugins/circleci/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react'; -import { render } from '@testing-library/react'; -import mockFetch from 'jest-fetch-mock'; -import ExampleFetchComponent from './ExampleFetchComponent'; - -describe('ExampleFetchComponent', () => { - it('should render', async () => { - mockFetch.mockResponse(() => new Promise(() => {})); - const rendered = render(); - expect(await rendered.findByTestId('progress')).toBeInTheDocument(); - }); -}); diff --git a/plugins/circleci/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx b/plugins/circleci/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx deleted file mode 100644 index c9bed14117..0000000000 --- a/plugins/circleci/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in wr iting, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React, { FC, useRef, useEffect } from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import Table from '@material-ui/core/Table'; -import TableBody from '@material-ui/core/TableBody'; -import TableCell from '@material-ui/core/TableCell'; -import TableContainer from '@material-ui/core/TableContainer'; -import TableHead from '@material-ui/core/TableHead'; -import TableRow from '@material-ui/core/TableRow'; -// import Alert from '@material-ui/lab/Alert'; -import { useAsync } from 'react-use'; -// import { Progress } from '@backstage/core'; - -import { CircleCI, GitType, CircleCIOptions, BuildSummary } from 'circleci-api'; - -// const CIRCLECI_TOKEN: string = '943aa82531ccaab192b4c4bc614507dff31c094c'; - -// Configure the factory with some defaults - -// const api = new CircleCI(options); - -// api -// .builds() -// .then(d => console.log('token is valid', d)) -// .catch(() => console.error('invalid token')); - -const useStyles = makeStyles({ - table: { - minWidth: 650, - }, - avatar: { - height: 32, - width: 32, - borderRadius: '50%', - }, -}); - -type User = { - gender: string; // "male" - name: { - title: string; // "Mr", - first: string; // "Duane", - last: string; // "Reed" - }; - location: object; // {street: {number: 5060, name: "Hickory Creek Dr"}, city: "Albany", state: "New South Wales",…} - email: string; // "duane.reed@example.com" - login: object; // {uuid: "4b785022-9a23-4ab9-8a23-cb3fb43969a9", username: "blackdog796", password: "patch",…} - dob: object; // {date: "1983-06-22T12:30:23.016Z", age: 37} - registered: object; // {date: "2006-06-13T18:48:28.037Z", age: 14} - phone: string; // "07-2154-5651" - cell: string; // "0405-592-879" - id: { - name: string; // "TFN", - value: string; // "796260432" - }; - picture: { medium: string }; // {medium: "https://randomuser.me/api/portraits/men/95.jpg",…} - nat: string; // "AU" -}; - -type DenseTableProps = { - users: User[]; -}; - -export const DenseTable: FC = ({ users }) => { - const classes = useStyles(); - - return ( - - - - - Avatar - Name - Email - Nationality - - - - {users.map(user => ( - - - {user.name.first} - - - {user.name.first} {user.name.last} - - {user.email} - {user.nat} - - ))} - -
-
- ); -}; -const options: Partial = { - // Required for all requests - // token: CIRCLECI_TOKEN, // Set your CircleCi API token - - // Optional - // Anything set here can be overriden when making the request - - // Git information is required for project/build/etc endpoints - vcs: { - type: GitType.GITHUB, // default: github - owner: 'CircleCITest3', - repo: 'circleci-test', - }, - - // Optional query params for requests - // options: { - // branch: "master", // default: master - // } -}; - -const BuildList: React.FC<{ builds: BuildSummary[] }> = ({ builds }) => ( -
    - {builds.map(build => ( -
  • - #{build.build_num} ({build.subject}) -
  • - ))} -
-); -const ExampleFetchComponent: FC<{ token: string }> = ({ token }) => { - const api = useRef(null); - useEffect(() => { - if (token !== '') api.current = new CircleCI({ ...options, token }); - }, [token]); - - const { value, loading, error } = useAsync(() => { - if (api.current) return api.current.builds(); - return Promise.reject('Api token not provided'); - }, [token]); - - if (loading) return
loading
; - if (error) return
{JSON.stringify(error, null, 2)}
; - - return ; -}; - -export default ExampleFetchComponent; diff --git a/plugins/circleci/src/components/ExampleFetchComponent/index.ts b/plugins/circleci/src/components/ExampleFetchComponent/index.ts deleted file mode 100644 index 28482f9fe1..0000000000 --- a/plugins/circleci/src/components/ExampleFetchComponent/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export { default } from './ExampleFetchComponent'; diff --git a/plugins/circleci/src/plugin.ts b/plugins/circleci/src/plugin.ts index 4aec241bdb..5c1eca9be3 100644 --- a/plugins/circleci/src/plugin.ts +++ b/plugins/circleci/src/plugin.ts @@ -14,11 +14,11 @@ * limitations under the License. */ import { createPlugin } from '@backstage/core'; -import ExampleComponent from './components/ExampleComponent'; +import { CircleCIPage } from './components/CircleCIPage'; export const plugin = createPlugin({ id: 'circleci', register({ router }) { - router.registerRoute('/circleci', ExampleComponent); + router.registerRoute('/circleci', CircleCIPage); }, });