feat: proper api flow implementation
This commit is contained in:
@@ -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'));
|
||||
|
||||
@@ -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<CircleCIOptions> = {
|
||||
// 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<CircleCIApi>({
|
||||
id: 'plugin.circleci.service',
|
||||
description: 'Used by the CircleCI plugin to make requests',
|
||||
});
|
||||
@@ -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<CircleCIOptions> = {
|
||||
// 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<string, string>)[status]};
|
||||
} as Record<string, string>)[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<CircleCI | null>(null);
|
||||
useEffect(() => {
|
||||
if (token !== '') api.current = new CircleCI({ ...options, token });
|
||||
}, [token]);
|
||||
export const CircleCIFetch: FC<{}> = () => {
|
||||
const [builds, setBuilds] = React.useState<BuildSummary[]>([]);
|
||||
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 <div>loading</div>;
|
||||
if (error) return <div>{JSON.stringify(error, null, 2)}</div>;
|
||||
|
||||
const builds = transform(value || []);
|
||||
return <CITable builds={builds} />;
|
||||
if (!api.api) return <div>Not authenticated</div>;
|
||||
const transformedBuilds = transform(builds || []);
|
||||
return <CITable builds={transformedBuilds} />;
|
||||
};
|
||||
|
||||
@@ -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<string>('');
|
||||
|
||||
return (
|
||||
<Page theme={pageTheme.tool}>
|
||||
<Header title="Welcome to circleci!" subtitle="Optional subtitle">
|
||||
@@ -43,20 +42,11 @@ export const CircleCIPage: FC<{}> = () => {
|
||||
</ContentHeader>
|
||||
<Grid container spacing={3} direction="column">
|
||||
<Grid item>
|
||||
<InfoCard title="Information card">
|
||||
<Typography variant="body1">
|
||||
Please paste your CircleCI token here
|
||||
<Input
|
||||
onChange={e => setToken(e.target.value)}
|
||||
type="password"
|
||||
value={token}
|
||||
></Input>
|
||||
</Typography>
|
||||
</InfoCard>
|
||||
<LoginCard />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<InfoCard title="CI/CD">
|
||||
<CircleCIFetch token={token} />
|
||||
<CircleCIFetch />
|
||||
</InfoCard>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -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 (
|
||||
<InfoCard>
|
||||
<Typography variant="h6">
|
||||
<PersonIcon /> CircleCI Auth
|
||||
</Typography>
|
||||
<List>
|
||||
<ListItem>
|
||||
<TextField
|
||||
name="circleci-token"
|
||||
type="password"
|
||||
label="Token"
|
||||
value={token}
|
||||
onChange={e => setToken(e.target.value)}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<Button
|
||||
data-testid="github-auth-button"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={() => api.authenticate(token)}
|
||||
>
|
||||
Authenticate
|
||||
</Button>
|
||||
</ListItem>
|
||||
</List>
|
||||
</InfoCard>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './LoginCard';
|
||||
@@ -15,3 +15,4 @@
|
||||
*/
|
||||
|
||||
export { plugin } from './plugin';
|
||||
export * from './api';
|
||||
|
||||
Reference in New Issue
Block a user