From 60828973825e1234faa5180846494d73999abf59 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Thu, 30 Apr 2020 16:19:00 +0200 Subject: [PATCH] feat: proper api flow implementation --- packages/app/src/apis.ts | 4 +- plugins/circleci/src/api/index.ts | 61 +++++++++++++++++++ .../components/CircleCIFetch/CirleCIFetch.tsx | 58 ++++++------------ .../components/CircleCIPage/CircleCIPage.tsx | 20 ++---- .../src/components/LoginCard/LoginCard.tsx | 46 ++++++++++++++ .../src/components/LoginCard/index.ts | 1 + plugins/circleci/src/index.ts | 1 + 7 files changed, 137 insertions(+), 54 deletions(-) create mode 100644 plugins/circleci/src/api/index.ts create mode 100644 plugins/circleci/src/components/LoginCard/LoginCard.tsx create mode 100644 plugins/circleci/src/components/LoginCard/index.ts diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index a24a25e205..c00aee1f5c 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -36,6 +36,8 @@ import { loadSampleData, } from '@backstage/plugin-tech-radar'; +import { CircleCIApi, circleCIApiRef } from '@backstage/plugin-circleci'; + const builder = ApiRegistry.builder(); export const alertApiForwarder = new AlertApiForwarder(); @@ -43,7 +45,7 @@ builder.add(alertApiRef, alertApiForwarder); export const errorApiForwarder = new ErrorApiForwarder(alertApiForwarder); builder.add(errorApiRef, errorApiForwarder); - +builder.add(circleCIApiRef, new CircleCIApi()); builder.add(featureFlagsApiRef, new FeatureFlags()); builder.add(lighthouseApiRef, new LighthouseRestApi('http://localhost:3003')); diff --git a/plugins/circleci/src/api/index.ts b/plugins/circleci/src/api/index.ts new file mode 100644 index 0000000000..849ac4ce1a --- /dev/null +++ b/plugins/circleci/src/api/index.ts @@ -0,0 +1,61 @@ +/* + * 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 { CircleCI, GitType, CircleCIOptions } from 'circleci-api'; +import { ApiRef } from '@backstage/core'; + +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', + }, +}; + +export class CircleCIApi { + api: null | CircleCI = null; + constuctor() {} + async authenticate(token: string) { + try { + if (token === '') return Promise.reject(); + this.api = new CircleCI({ ...options, token }); + // await this.api.me(); + return Promise.resolve(); + } catch (e) { + this.api = null; + return this.cantAuth(); + } + } + async cantAuth() { + return Promise.reject("Can't auth"); + } + async getBuilds() { + if (!this.api) return this.cantAuth(); + return this.api.builds(); + } +} + +export const circleCIApiRef = new ApiRef({ + id: 'plugin.circleci.service', + description: 'Used by the CircleCI plugin to make requests', +}); diff --git a/plugins/circleci/src/components/CircleCIFetch/CirleCIFetch.tsx b/plugins/circleci/src/components/CircleCIFetch/CirleCIFetch.tsx index fff0c25e50..50ca39b3a6 100644 --- a/plugins/circleci/src/components/CircleCIFetch/CirleCIFetch.tsx +++ b/plugins/circleci/src/components/CircleCIFetch/CirleCIFetch.tsx @@ -14,40 +14,22 @@ * limitations under the License. */ -import React, { FC, useRef, useEffect } from 'react'; +import React, { FC } 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 { 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 - // } -}; +import { circleCIApiRef } from 'api'; +import { useApi } from '@backstage/core'; // "lifecycle" : "finished", // :queued, :scheduled, :not_run, :not_running, :running or :finished // "outcome" : "failed", // :canceled, :infrastructure_fail, :timedout, :failed, :no_tests or :success const makeReadableStatus = (status: string | undefined) => { - if (typeof status === 'undefined') return "" + if (typeof status === 'undefined') return ''; return ({ retried: 'Retried', canceled: 'Canceled', @@ -62,7 +44,8 @@ const makeReadableStatus = (status: string | undefined) => { no_tests: 'No tests', fixed: 'Fixed', success: 'Success', - } as Record)[status]}; + } as Record)[status]; +}; const transform = (buildsData: BuildSummary[]): CITableBuildInfo[] => { return buildsData.map(buildData => { @@ -90,20 +73,19 @@ const transform = (buildsData: BuildSummary[]): CITableBuildInfo[] => { }); }; -export const CircleCIFetch: FC<{ token: string }> = ({ token }) => { - const api = useRef(null); - useEffect(() => { - if (token !== '') api.current = new CircleCI({ ...options, token }); - }, [token]); +export const CircleCIFetch: FC<{}> = () => { + const [builds, setBuilds] = React.useState([]); + const api = useApi(circleCIApiRef); - const { value, loading, error } = useAsync(() => { - if (api.current) return api.current.builds(); - return Promise.reject('Api token not provided'); - }, [token]); + React.useEffect(() => { + const intervalId = setInterval(() => { + if (!api.api) return; + api.getBuilds().then(setBuilds); + }, 1500); + return () => clearInterval(intervalId); + }, []); - if (loading) return
loading
; - if (error) return
{JSON.stringify(error, null, 2)}
; - - const builds = transform(value || []); - return ; + if (!api.api) return
Not authenticated
; + const transformedBuilds = transform(builds || []); + return ; }; diff --git a/plugins/circleci/src/components/CircleCIPage/CircleCIPage.tsx b/plugins/circleci/src/components/CircleCIPage/CircleCIPage.tsx index f27ac80a6f..23ac5c0d7d 100644 --- a/plugins/circleci/src/components/CircleCIPage/CircleCIPage.tsx +++ b/plugins/circleci/src/components/CircleCIPage/CircleCIPage.tsx @@ -14,8 +14,8 @@ * limitations under the License. */ -import React, { FC, useState } from 'react'; -import { Typography, Grid, Input } from '@material-ui/core'; +import React, { FC } from 'react'; +import { Grid } from '@material-ui/core'; import { InfoCard, Header, @@ -27,10 +27,9 @@ import { SupportButton, } from '@backstage/core'; import { CircleCIFetch } from '../CircleCIFetch'; +import { LoginCard } from '../LoginCard'; export const CircleCIPage: FC<{}> = () => { - const [token, setToken] = useState(''); - return (
@@ -43,20 +42,11 @@ export const CircleCIPage: FC<{}> = () => { - - - Please paste your CircleCI token here - setToken(e.target.value)} - type="password" - value={token} - > - - + - + diff --git a/plugins/circleci/src/components/LoginCard/LoginCard.tsx b/plugins/circleci/src/components/LoginCard/LoginCard.tsx new file mode 100644 index 0000000000..09c0514064 --- /dev/null +++ b/plugins/circleci/src/components/LoginCard/LoginCard.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { + Typography, + Button, + TextField, + List, + ListItem, +} from '@material-ui/core'; +import { Person as PersonIcon } from '@material-ui/icons'; +import { InfoCard, useApi } from '@backstage/core'; +import { circleCIApiRef } from 'api'; + +export const LoginCard = () => { + const [token, setToken] = React.useState(''); + const api = useApi(circleCIApiRef); + + return ( + + + CircleCI Auth + + + + setToken(e.target.value)} + /> + + + + + + + + ); +}; diff --git a/plugins/circleci/src/components/LoginCard/index.ts b/plugins/circleci/src/components/LoginCard/index.ts new file mode 100644 index 0000000000..1d3c3bcf3e --- /dev/null +++ b/plugins/circleci/src/components/LoginCard/index.ts @@ -0,0 +1 @@ +export * from './LoginCard'; diff --git a/plugins/circleci/src/index.ts b/plugins/circleci/src/index.ts index 3a0a0fe2d3..d67bc6a864 100644 --- a/plugins/circleci/src/index.ts +++ b/plugins/circleci/src/index.ts @@ -15,3 +15,4 @@ */ export { plugin } from './plugin'; +export * from './api';