Merge pull request #2 from Nek/feature/log-view

Feature/log view
This commit is contained in:
Nikita Dudnik
2020-05-06 11:31:57 +02:00
committed by GitHub
17 changed files with 211 additions and 85 deletions
+5
View File
@@ -31,6 +31,7 @@ export const circleCIApiRef = new ApiRef<CircleCIApi>({
});
export class CircleCIApi {
private token: string = '';
options: Partial<CircleCIOptions>;
@@ -104,4 +105,8 @@ export class CircleCIApi {
async getUser() {
return this.api.me();
}
async getBuild(buildId: string) {
return this.api.build(parseInt(buildId, 10));
}
}
@@ -0,0 +1,47 @@
import React, { useEffect, useState, FC } from 'react';
import {
ExpansionPanel,
ExpansionPanelSummary,
Typography,
ExpansionPanelDetails,
} from '@material-ui/core';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import { BuildStepAction } from 'circleci-api';
export const ActionOutput: FC<{
url: string;
name: string;
action: BuildStepAction;
}> = ({ url, name }) => {
const [messages, setMessages] = useState([]);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(actionOutput => {
actionOutput &&
setMessages(
actionOutput.map(({ message }: { message: string }) => message),
);
});
}, [url]);
console.log(messages);
return (
<ExpansionPanel>
<ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography>{name}</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
{messages.length === 0
? 'Nothing here...'
: messages.map(message => (
<p style={{ whiteSpace: 'pre-wrap' }}>{message}</p>
))}
</ExpansionPanelDetails>
</ExpansionPanel>
);
};
@@ -0,0 +1 @@
export { ActionOutput } from './ActionOutput';
@@ -9,10 +9,10 @@ import {
TableHead,
TableContainer,
TableRow,
Link,
CircularProgress,
} from '@material-ui/core';
import { Replay as RetryIcon } from '@material-ui/icons';
import { Link } from 'react-router-dom';
import {
StatusFailed,
StatusOK,
@@ -94,7 +94,7 @@ export const CITable: FC<{
<TableRow key={build.id}>
<TableCell>{build.id}</TableCell>
<TableCell>
<Link href={build.buildUrl} target="_blank">
<Link to={`/circleci/build/${build.id}`}>
{build.buildName}
</Link>
</TableCell>
@@ -1,69 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { FC } from 'react';
import { Route } from 'react-router';
import { Link as RouterLink } from 'react-router-dom';
import { Grid, Button } from '@material-ui/core';
import { Settings as SettingsIcon } from '@material-ui/icons';
import {
InfoCard,
Header,
Page,
pageTheme,
Content,
ContentHeader,
HeaderLabel,
SupportButton,
} from '@backstage/core';
import { CircleCIFetch } from '../CircleCIFetch';
import { SettingsPage } from '../SettingsPage';
export const CircleCIPage: FC<{}> = () => {
return (
<>
<Route path="/circleci/settings" component={SettingsPage} />
<Page theme={pageTheme.tool}>
<Header title="Welcome to circleci!" subtitle="Optional subtitle">
<HeaderLabel label="Owner" value="Team X" />
<HeaderLabel label="Lifecycle" value="Alpha" />
</Header>
<Content>
<ContentHeader title="Circle CI">
<Button
component={RouterLink}
to="/circleci/settings"
startIcon={<SettingsIcon />}
>
Settings
</Button>
<SupportButton>
A description of your plugin goes here.
</SupportButton>
</ContentHeader>
<Grid container spacing={3} direction="column">
<Grid item>
<InfoCard title="Pipelines">
<CircleCIFetch />
</InfoCard>
</Grid>
</Grid>
</Content>
</Page>
</>
);
};
export default CircleCIPage;
@@ -1 +0,0 @@
export { CircleCIPage } from './CircleCIPage';
@@ -0,0 +1,12 @@
import React from 'react';
import { Header, Page, pageTheme, HeaderLabel } from '@backstage/core';
export const Layout: React.FC = ({ children }) => (
<Page theme={pageTheme.tool}>
<Header title="Welcome to circleci!" subtitle="Optional subtitle">
<HeaderLabel label="Owner" value="Team X" />
<HeaderLabel label="Lifecycle" value="Alpha" />
</Header>
{children}
</Page>
);
@@ -0,0 +1 @@
export * from './Layout';
@@ -0,0 +1,18 @@
import React from 'react';
import { Link as RouterLink } from 'react-router-dom';
import { ContentHeader, SupportButton } from '@backstage/core';
import { Button } from '@material-ui/core';
import { Settings as SettingsIcon } from '@material-ui/icons';
export const PluginHeader = () => (
<ContentHeader title="Circle CI">
<Button
component={RouterLink}
to="/circleci/settings"
startIcon={<SettingsIcon />}
>
Settings
</Button>
<SupportButton>A description of your plugin goes here.</SupportButton>
</ContentHeader>
);
@@ -0,0 +1 @@
export * from './PluginHeader';
@@ -0,0 +1,36 @@
import React, { FC } from 'react';
import { Link as RouterLink } from 'react-router-dom';
import {
Content,
ContentHeader,
SupportButton,
InfoCard,
} from '@backstage/core';
import { Button, Grid } from '@material-ui/core';
import { CircleCIFetch } from 'components/CircleCIFetch';
import { Settings as SettingsIcon } from '@material-ui/icons';
import { Layout } from 'components/Layout';
export const BuildsPage: FC<{}> = () => (
<Layout>
<Content>
<ContentHeader title="Circle CI">
<Button
component={RouterLink}
to="/circleci/settings"
startIcon={<SettingsIcon />}
>
Settings
</Button>
<SupportButton>A description of your plugin goes here.</SupportButton>
</ContentHeader>
<Grid container spacing={3} direction="column">
<Grid item>
<InfoCard title="Pipelines">
<CircleCIFetch />
</InfoCard>
</Grid>
</Grid>
</Content>
</Layout>
);
@@ -0,0 +1 @@
export * from './BuildsPage';
@@ -0,0 +1,78 @@
import React, { FC } from 'react';
import { Content, InfoCard, useApi } from '@backstage/core';
import { Grid, Box } from '@material-ui/core';
import { PluginHeader } from 'components/PluginHeader';
import { BuildWithSteps, BuildStepAction } from 'circleci-api';
import { circleCIApiRef } from 'api';
import { useParams } from 'react-router-dom';
import { ActionOutput } from '../../components/ActionOutput/ActionOutput';
import { Layout } from 'components/Layout';
export const DetailedViewPage: FC<{}> = () => {
let { buildId = '' } = useParams();
const [authed, setAuthed] = React.useState(false);
const [build, setBuild] = React.useState<BuildWithSteps | null>(null);
const api = useApi(circleCIApiRef);
React.useEffect(() => {
const getBuildAsync = async () => {
if (!authed) {
await api.restorePersistedSettings();
await api
.validateToken()
.then(() => {
setAuthed(true);
})
.catch(() => setAuthed(false));
}
api.getBuild(buildId).then(setBuild);
};
getBuildAsync();
}, [authed, buildId]);
return (
<Layout>
<Content>
<PluginHeader />
{!api.authed ? (
<div>Not authenticated</div>
) : (
<Grid container spacing={3} direction="column">
<Grid item>
<InfoCard title="Pipelines">
<BuildsList build={build} />
</InfoCard>
</Grid>
</Grid>
)}
</Content>
</Layout>
);
};
const BuildsList: FC<{ build: BuildWithSteps | null }> = ({ build }) => (
<Box>
{build &&
build.steps &&
build.steps.map(
({ name, actions }: { name: string; actions: BuildStepAction[] }) => (
<ActionsList name={name} actions={actions} />
),
)}
</Box>
);
const ActionsList: FC<{ actions: BuildStepAction[]; name: string }> = ({
actions,
name,
}) => (
<Box key={name}>
{actions.map((action: BuildStepAction) => (
<ActionOutput
action={action}
name={action.name}
url={action.output_url || ''}
/>
))}
</Box>
);
@@ -0,0 +1 @@
export * from './DetailedViewPage';
@@ -4,16 +4,13 @@ import { circleCIApiRef } from 'api';
import {
InfoCard,
useApi,
Header,
Page,
pageTheme,
Content,
ContentHeader,
HeaderLabel,
SupportButton,
} from '@backstage/core';
import { ProjectInput } from 'components/ProjectInput/ProjectInput';
import { Link as RouterLink } from 'react-router-dom';
import { Layout } from 'components/Layout';
export const SettingsPage = () => {
const api = useApi(circleCIApiRef);
@@ -29,11 +26,7 @@ export const SettingsPage = () => {
}, []);
return (
<Page theme={pageTheme.tool}>
<Header title="Circle CI" subtitle="Settings">
<HeaderLabel label="Owner" value="Team X" />
<HeaderLabel label="Lifecycle" value="Alpha" />
</Header>
<Layout>
<Content>
<ContentHeader title="Settings">
<Button component={RouterLink} to="/circleci">
@@ -90,6 +83,6 @@ export const SettingsPage = () => {
</Grid>
</Grid>
</Content>
</Page>
</Layout>
);
};
+5 -3
View File
@@ -14,13 +14,15 @@
* limitations under the License.
*/
import { createPlugin } from '@backstage/core';
import { CircleCIPage } from './components/CircleCIPage';
import { SettingsPage } from './components/SettingsPage';
import { BuildsPage } from './pages/BuildsPage';
import { SettingsPage } from './pages/SettingsPage';
import { DetailedViewPage } from './pages/DetailedViewPage';
export const plugin = createPlugin({
id: 'circleci',
register({ router }) {
router.registerRoute('/circleci', CircleCIPage);
router.registerRoute('/circleci', BuildsPage);
router.registerRoute('/circleci/build/:buildId', DetailedViewPage);
router.registerRoute('/circleci/settings', SettingsPage);
},
});