Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds backfill detail screen #143

Merged
merged 11 commits into from
Aug 9, 2017
3 changes: 3 additions & 0 deletions core/src/main/javascript/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import Workflow from "./app/pages/Workflow";
import { Started, Stuck, Paused, Finished } from "./app/pages/ExecutionLogs";
import Execution from "./app/pages/Execution";
import TimeSeriesExecutions from "./app/pages/TimeSeriesExecutions";
import Backfill from "./app/pages/Backfill";
import Backfills from "./app/pages/Backfills";
import BackfillCreate from "./app/pages/BackfillCreate";
import Favicon from "./app/components/Favicon";
Expand Down Expand Up @@ -104,6 +105,8 @@ class App extends React.Component {
return <Backfills />;
case "timeseries/backfills/create":
return <BackfillCreate />;
case "timeseries/backfills/detail":
return <Backfill backfillId={page.backfillId} />;
default:
return null;
}
Expand Down
5 changes: 4 additions & 1 deletion core/src/main/javascript/ApplicationState.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ export type Page =
sort?: string,
order?: "asc" | "desc"
}
| { id: "timeseries/backfills/create" };
| { id: "timeseries/backfills/create" }
| {
id: "timeseries/backfills/detail", backfillId: string
};

export type State = {
page: Page,
Expand Down
45 changes: 45 additions & 0 deletions core/src/main/javascript/app/components/Context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// @flow

import React from "react";
import moment from "moment";

import Link from "./Link"
import CalendarIcon from "react-icons/lib/md/date-range";
import BreakIcon from "react-icons/lib/md/keyboard-control";

type Props = {
className?: string,
context : {
start: string;
end: string;
};
};

const Context = ({ context } : Props) => {
// Need to be dynamically linked with the scehduler but for now let's
// assume that it is a TimeseriesContext
let format = date => moment(date).utc().format("MMM-DD HH:mm");
let URLFormat = date => moment(date).utc().format("YYYY-MM-DDTHH") + "Z";

return (
<Link
href={`/timeseries/calendar/${URLFormat(context.start)}_${URLFormat(context.end)}`}
>
<CalendarIcon
style={{
fontSize: "1.2em",
verticalAlign: "middle",
transform: "translateY(-2px)"
}}
/>
{" "}
{format(context.start)}
{" "}
<BreakIcon />
{" "}
{format(context.end)} UTC
</Link>
);
}

export default Context;
13 changes: 9 additions & 4 deletions core/src/main/javascript/app/components/Window.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
import React from "react";
import injectSheet from "react-jss";
import { connect } from "react-redux";
import { goBack } from "redux-url";
import { goBack, navigate } from "redux-url";
import CloseIcon from "react-icons/lib/md/close";

type Props = {
classes: any,
title: string,
children: any,
back: () => void
back: () => void,
closeUrl: ?string
};

const Window = ({ classes, title, children, back }: Props) => (
Expand Down Expand Up @@ -65,9 +66,13 @@ const styles = {
}
};

const mapDispatchToProps = dispatch => ({
const mapDispatchToProps = (dispatch, ownProps) => ({
back() {
dispatch(goBack());
if (ownProps.closeUrl){
dispatch(navigate(ownProps.closeUrl));
} else {
dispatch(goBack());
}
}
});

Expand Down
171 changes: 171 additions & 0 deletions core/src/main/javascript/app/pages/Backfill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// @flow

import React from "react";
import { connect } from "react-redux";
import injectSheet from "react-jss";
import FancyTable from "../components/FancyTable";

import Window from "../components/Window";
import type { Backfill, ExecutionLog, Workflow } from "../../datamodel";
import { listenEvents } from "../../Utils";
import Context from "../components/Context";
import JobStatus from "../components/JobStatus";
import { markdown } from "markdown";
import Link from "../components/Link";

import { BackfillsExecutions } from "./ExecutionLogs";

type Props = {
classes: any,
backfillId: string,
workflow: Workflow
};
type State = {
backfill: ?Backfill,
query: ?string,
eventSource: ?any,
error: ?any,
executionsEventSource: ?any,
executions: Array<ExecutionLog>,
completion?: number;
};

class BackfillDetail extends React.Component {
props: Props;
state: State;
constructor(props: Props) {
super(props);
this.state = {
backfill: undefined,
query: null,
eventSource: null,
error: null,
completion : null
};
}

componentWillMount() {
this.listen();
}

listen() {
let { query, eventSource } = this.state;
let { backfillId } = this.props;
let newQuery = `/api/timeseries/backfills/${backfillId}?events=true`;
if (newQuery != query) {
eventSource && eventSource.close();
eventSource = listenEvents(
newQuery,
this.updateData.bind(this),
this.notFound.bind(this)
);
this.setState({
query: newQuery,
backfill: null,
eventSource
});
}
}

notFound(error) {
this.setState({
error
});
}

updateData(json: Backfill) {
this.setState({
...this.state,
backfill: json
});
}

render() {
const { backfill, error } = this.state;
const { classes } = this.props;

const created = b => `by ${b.created_by} on ${b.created}`;
const jobIdFromName = (n: String) =>
this.props.workflow.jobs.find(j => j.name == n).id;

const jobLinks =
backfill &&
backfill.jobs.split(",").map(jobName => {
const jobId = jobIdFromName(jobName);
return (
<Link className="job-badge" href={`/workflow/${jobId}`}>
<span>{jobName}</span>
</Link>
);
});

return (
<div className={classes.main}>
<Window closeUrl={`/timeseries/backfills`} title="Backfill">
{backfill
? <FancyTable key="properties">
<dt key="name">Name:</dt>
<dd key="name_">{backfill.name}</dd>
<dt key="jobs">Jobs:</dt>
<dd key="jobs_">{jobLinks}</dd>
<dt key="created">Created:</dt>
<dd key="created_">{created(backfill)}</dd>
<dt key="description">Description:</dt>
<dd key="description_" style={{ lineHeight : 'inherit'}}
dangerouslySetInnerHTML={{
__html: markdown.toHTML(backfill.description)
}}/>
<dt key="context">Context:</dt>
<dd key="context_">
<Context
context={{ start: backfill.start, end: backfill.end }}
/>
</dd>
<dt key="status">Status:</dt>
<dd key="status_"><JobStatus status={backfill.status} />
<span className="backfill-completion">
{this.state.completion ? `${(this.state.completion*100).toFixed(2)} % completed`: null}
</span></dd>
</FancyTable>
: null}
<BackfillsExecutions
backfillId={this.props.backfillId}
completionNotifier={(c) => this.setState({ completion : c})}/>
</Window>
</div>
);
}

componentWillUnmount() {
let { eventSource } = this.state;
eventSource && eventSource.close();
}
}

const mapStateToProps = ({ app: { workflow } }) => ({
workflow
});

const styles = {
main: {
"& .job-badge": {
display: "inline-block",
backgroundColor: "#5E6A87",
color: "#FFF",
fontFamily: "Arial",
fontWeight: "normal",
lineHeight: "18px",
fontSize: "11px",
borderRadius: "2px",
padding: "0 .5em",
maxHeight: "18px",
textAlign: "center",
marginLeft: "5px"
},
"& .backfill-completion" : {
marginLeft : "5px"
}
}
};

export default connect(mapStateToProps)(injectSheet(styles)(BackfillDetail));
2 changes: 1 addition & 1 deletion core/src/main/javascript/app/pages/BackfillCreate.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ class BackfillCreate extends React.Component<any, Props, void> {

return fetch(
`/api/timeseries/backfill?` +
`name=${name}&description=${description}&` +
`name=${name}&description=${description || ""}&` +
`jobs=${jobs.join(",")}&priority=${priority}&` +
`startDate=${start.toISOString()}&endDate=${end.toISOString()}`,
{ method: "POST", credentials: "include" }
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/javascript/app/pages/Backfills.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ class Backfills extends React.Component {
) => {
switch (column) {
case "name":
return <Link href={`/backfills/${id}`}>{name}</Link>; //TODO detail screen + links repetition
return <Link href={`/timeseries/backfills/${id}`}>{name}</Link>;
case "jobs":
return <span>{jobs.length}</span>;
case "period":
Expand All @@ -174,7 +174,7 @@ class Backfills extends React.Component {
return (
<Link
className={classes.openIcon}
href={`/backfills/${id}`}
href={`/timeseries/backfills/${id}`}
>
<BackfillStatus status={status} />
</Link>
Expand All @@ -183,7 +183,7 @@ class Backfills extends React.Component {
return (
<Link
className={classes.openIcon}
href={`/backfills/${id}`}
href={`/timeseries/backfills/${id}`}
>
<OpenIcon />
</Link>
Expand Down
31 changes: 2 additions & 29 deletions core/src/main/javascript/app/pages/Execution.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ import injectSheet from "react-jss";
import FullscreenIcon from "react-icons/lib/md/fullscreen";
import ExitFullscreenIcon from "react-icons/lib/md/fullscreen-exit";
import AutoScrollIcon from "react-icons/lib/md/arrow-downward";
import BreakIcon from "react-icons/lib/md/keyboard-control";
import CalendarIcon from "react-icons/lib/md/date-range";
import ReactTooltip from "react-tooltip";

import moment from "moment";

import Context from "../components/Context";
import Window from "../components/Window";
import FancyTable from "../components/FancyTable";
import Error from "../components/Error";
Expand Down Expand Up @@ -220,32 +219,6 @@ class Execution extends React.Component {
});
}

renderContext(ctx) {
// Need to be dynamically linked with the scehduler but for now let's
// assume that it is a TimeseriesContext
let format = date => moment(date).utc().format("MMM-DD HH:mm");
let URLFormat = date => moment(date).utc().format("YYYY-MM-DDTHH") + "Z";
return (
<Link
href={`/timeseries/calendar/${URLFormat(ctx.start)}_${URLFormat(ctx.end)}`}
>
<CalendarIcon
style={{
fontSize: "1.2em",
verticalAlign: "middle",
transform: "translateY(-2px)"
}}
/>
{" "}
{format(ctx.start)}
{" "}
<BreakIcon />
{" "}
{format(ctx.end)} UTC
</Link>
);
}

render() {
let { classes, execution } = this.props;
let { data, error, streams } = this.state;
Expand All @@ -262,7 +235,7 @@ class Execution extends React.Component {
<Link href={`/workflow/${data.job}`}>{data.job}</Link>
</dd>
<dt key="context">Context:</dt>
<dd key="context_">{this.renderContext(data.context)}</dd>
<dd key="context_"><Context context={data.context} /></dd>
<dt key="status">Status:</dt>
<dd key="status_"><JobStatus status={data.status} /></dd>
{data.startTime
Expand Down
Loading