blob: f94928c4162ea105a92e27ec636c6ff244c9bb0b [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 axios from 'axios';
import {Table, Tabs} from 'antd';
import './Pipelines.less';
import {PaginationConfig} from "antd/lib/pagination";
import prettyMilliseconds from "pretty-ms";
import moment from 'moment';
import {ReplicationIcon} from "../../utils/themeIcons";
const {TabPane} = Tabs;
export type PipelineStatus = "active" | "inactive";
interface PipelineResponse {
pipelineId: string;
status: PipelineStatus;
replicationType: string;
leaderNode: string;
datanodes: string[];
lastLeaderElection: number;
duration: number;
leaderElections: number;
replicationFactor: number;
containers: number;
}
interface PipelinesResponse {
totalCount: number;
pipelines: PipelineResponse[];
}
interface PipelinesState {
activeLoading: boolean;
activeDataSource: PipelineResponse[];
activeTotalCount: number;
inactiveLoading: boolean;
inactiveDataSource: PipelineResponse[];
inactiveTotalCount: number;
}
const COLUMNS = [
{
title: 'Pipeline ID',
dataIndex: 'pipelineId',
key: 'pipelineId',
sorter: (a: PipelineResponse, b: PipelineResponse) => a.pipelineId.localeCompare(b.pipelineId)
},
{
title: 'Replication Type & Factor',
dataIndex: 'replicationType',
key: 'replicationType',
render: (replicationType: string, record: PipelineResponse) => {
const replicationFactor = record.replicationFactor;
return (
<span>
<ReplicationIcon replicationFactor={replicationFactor} replicationType={replicationType}/>
{replicationType} ({replicationFactor})
</span>
)
},
sorter: (a: PipelineResponse, b: PipelineResponse) =>
(a.replicationType + a.replicationFactor).localeCompare(b.replicationType + b.replicationFactor),
defaultSortOrder: 'descend' as const
},
{
title: 'Status',
dataIndex: 'status',
key: 'status',
sorter: (a: PipelineResponse, b: PipelineResponse) => a.status.localeCompare(b.status)
},
{
title: 'Containers',
dataIndex: 'containers',
key: 'containers',
sorter: (a: PipelineResponse, b: PipelineResponse) => a.containers - b.containers
},
{
title: 'Datanodes',
dataIndex: 'datanodes',
key: 'datanodes',
render: (datanodes: string[]) => <div>{datanodes.map(datanode => <div key={datanode}>{datanode}</div>)}</div>
},
{
title: 'Leader',
dataIndex: 'leaderNode',
key: 'leaderNode',
sorter: (a: PipelineResponse, b: PipelineResponse) => a.leaderNode.localeCompare(b.leaderNode)
},
{
title: 'Last Leader Election',
dataIndex: 'lastLeaderElection',
key: 'lastLeaderElection',
render: (lastLeaderElection: number) => lastLeaderElection > 0 ?
moment(lastLeaderElection).format('lll') : 'NA',
sorter: (a: PipelineResponse, b: PipelineResponse) => a.lastLeaderElection - b.lastLeaderElection
},
{
title: 'Lifetime',
dataIndex: 'duration',
key: 'duration',
render: (duration: number) => prettyMilliseconds(duration, {compact: true}),
sorter: (a: PipelineResponse, b: PipelineResponse) => a.duration - b.duration
},
{
title: 'No. of Elections',
dataIndex: 'leaderElections',
key: 'leaderElections',
sorter: (a: PipelineResponse, b: PipelineResponse) => a.leaderElections - b.leaderElections
}
];
export class Pipelines extends React.Component<any, PipelinesState> {
constructor(props: any) {
super(props);
this.state = {
activeLoading: false,
activeDataSource: [],
activeTotalCount: 0,
inactiveLoading: false,
inactiveDataSource: [],
inactiveTotalCount: 0
}
}
componentDidMount(): void {
// Fetch pipelines on component mount
this.setState({
activeLoading: true
});
axios.get('/api/v1/pipelines').then(response => {
const pipelinesResponse: PipelinesResponse = response.data;
const totalCount = pipelinesResponse.totalCount;
const pipelines: PipelineResponse[] = pipelinesResponse.pipelines;
this.setState({
activeLoading: false,
activeDataSource: pipelines,
activeTotalCount: totalCount
});
});
}
onShowSizeChange = (current: number, pageSize: number) => {
// TODO: Implement this method once server side pagination is enabled
console.log(current, pageSize);
};
onTabChange = (activeKey: string) => {
// Fetch inactive pipelines if tab is switched to "Inactive"
if (activeKey === "2") {
// TODO: Trigger an ajax request to fetch inactive pipelines
}
};
render () {
const {activeDataSource, activeLoading, activeTotalCount} = this.state;
const paginationConfig: PaginationConfig = {
showTotal: (total: number, range) => `${range[0]}-${range[1]} of ${total} pipelines`,
showSizeChanger: true,
onShowSizeChange: this.onShowSizeChange
};
return (
<div className="pipelines-container">
<div className="page-header">
Pipelines ({activeTotalCount})
</div>
<div className="content-div">
<Tabs defaultActiveKey="1" onChange={this.onTabChange}>
<TabPane key="1" tab="Active">
<Table dataSource={activeDataSource} columns={COLUMNS} loading={activeLoading} pagination={paginationConfig} rowKey="pipelineId"/>
</TabPane>
<TabPane key="2" tab="Inactive">
</TabPane>
</Tabs>
</div>
</div>
);
}
}