Skip to content

Commit

Permalink
add error classification ui component
Browse files Browse the repository at this point in the history
  • Loading branch information
GnsP committed Nov 26, 2024
1 parent ccda48a commit 9712ce8
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/*
* Copyright © 2024 Cask Data, Inc.
*
* 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, { useEffect, useState } from 'react';
import { useSelector, Provider } from 'react-redux';
import { Button, Table, TableBody, TableCell, TableHead, TableRow } from '@material-ui/core';
import styled from 'styled-components';
import PipelineMetricsStore from 'services/PipelineMetricsStore';
import PipelineLogViewer from '../RunLevelInfo/PipelineLogViewer';
import ThemeWrapper from 'components/ThemeWrapper';

const PipelineRunErrorDetailsWrapper = styled.div`
width: 100%;
background: red;
position: relative;
color: white;
`;

const ShortErrorMessage = styled.div`
width: 100%;
display: flex;
padding: 0 20px;
gap: 40px;
align-items: center;
justify-content: space-between;
p {
margin: 0;
}
`;

const ErrorDetailsContainer = styled.div`
position: absolute;
top: 100%;
left: 0;
right: 0;
background: #ffefef;
box-shadow: 0 4px 4px rgba(0, 0, 0, 0.2);
z-index: 1000;
padding: 20px;
color: black;
p {
margin: 0;
}
`;

const ErrorImprovementMessage = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
margin-top: 20px;
`;

const PipelineErrorCountMessage = () => {
const logsMetrics = useSelector((state) => state?.logsMetrics || {});
const errorCount = logsMetrics['system.app.log.error'] || 0;

return <p>Pipeline execution failed with {errorCount} errors.</p>;
};

interface IErrorEntry {
errorCategory: string;
errorMessage: string;
errorReason: string;
stageName: string;
}

interface IErrorClassificationResponse {
errors: IErrorEntry[];
}

export default function PipelineRunErrorDetails() {
const [detailsExpanded, setDetailsExpanded] = useState<boolean>(false);
const [logsOpened, setLogsOpened] = useState<boolean>(false);
const [errorDetails, setErrorDetails] = useState<IErrorClassificationResponse>(null);
const currentRun = useSelector((state) => state.currentRun);

function toggleErrorDetails() {
setDetailsExpanded((x) => !x);
}

async function fetchErrorDetails() {
// TODO: call the following API when it's ready.
// `/namespaces/<namespace>/apps/<application>/<program_type>/<program_type>/runs/<run_id>/classify?isPreview`

const mockResponse: IErrorClassificationResponse = {
errors: [
{
errorCategory: 'CATEGORY_1',
errorMessage: `Pipeline 'logs_generator' failed.`,
errorReason: 'File does not exist',
stageName: 'gcs_data_source',
},
{
errorCategory: 'CATEGORY_2',
errorMessage: `MapReduce Program 'phase-1' failed.`,
errorReason: 'Misconfiguration of compute profile',
stageName: 'stage_transform',
},
{
errorCategory: 'CATEGORY_891 ',
errorMessage: `Workflow service 'workflow.default.logs_generator.DataPipelineWorkflow.1564e8f8-aafc-11ef-b59d-000000524bf1' failed.`,
errorReason: 'File does not exist',
stageName: 'gcs_data_source',
},
{
errorCategory: 'CATEGORY_1',
errorMessage: `Pipeline 'logs_generator' failed.`,
errorReason: 'File does not exist',
stageName: 'gcs_data_source',
},
],
};

setErrorDetails(mockResponse);
}

function viewLogs() {
setDetailsExpanded(false);
setLogsOpened(true);
}

function toggleLogs() {
setLogsOpened((x) => !x);
}

useEffect(() => {
fetchErrorDetails();
}, [currentRun.runid, currentRun.status]);

if (currentRun.status !== 'FAILED') {
return null;
}

return (
<>
<PipelineRunErrorDetailsWrapper>
<Provider store={PipelineMetricsStore}>
<ShortErrorMessage>
<PipelineErrorCountMessage />
<Button color="inherit" onClick={toggleErrorDetails}>
{detailsExpanded ? 'Close details' : 'View details'}
</Button>
</ShortErrorMessage>
{detailsExpanded && (
<ErrorDetailsContainer>
<Table>
<TableHead>
<TableRow>
<TableCell>Error category</TableCell>
<TableCell>Error message</TableCell>
<TableCell>Error reason</TableCell>
</TableRow>
</TableHead>
<TableBody>
{errorDetails?.errors?.map((err) => (
<TableRow>
<TableCell>{err.errorCategory}</TableCell>
<TableCell>{err.errorMessage}</TableCell>
<TableCell>{err.errorReason}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<ErrorImprovementMessage>
<p>
Help us improve the error classification. Raise improvement <a href="#">here</a>.
</p>
<Button color="secondary" onClick={viewLogs}>
View logs
</Button>
</ErrorImprovementMessage>
</ErrorDetailsContainer>
)}
</Provider>
</PipelineRunErrorDetailsWrapper>
{logsOpened && (
<ThemeWrapper>
<PipelineLogViewer toggleLogViewer={toggleLogs} />
</ThemeWrapper>
)}
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import React, { useEffect } from 'react';
import styled from 'styled-components';
import { Provider, connect } from 'react-redux';
import PipelineDetailsMetadata from 'components/PipelineDetails/PipelineDetailsTopPanel/PipelineDetailsMetadata';
import PipelineDetailsButtons from 'components/PipelineDetails/PipelineDetailsTopPanel/PipelineDetailsButtons';
Expand All @@ -27,6 +28,7 @@ import PlusButton from 'components/shared/PlusButton';
import { fetchAndUpdateRuntimeArgs } from 'components/PipelineConfigurations/Store/ActionCreator';
import { FeatureProvider } from 'services/react/providers/featureFlagProvider';
import { setEditDraftId } from '../store/ActionCreator';
import PipelineRunErrorDetails from './PipelineRunErrorDetails';

require('./PipelineDetailsTopPanel.scss');

Expand All @@ -51,6 +53,12 @@ const mapStateToButtonsProps = (state) => {

const ConnectedPipelineDetailsButtons = connect(mapStateToButtonsProps)(PipelineDetailsButtons);

const PipelineDetailsWrapper = styled.div`
width: 100%;
height: 100%;
position: relative;
`;

export const PipelineDetailsTopPanel = () => {
useEffect(() => {
const pipelineDetailStore = PipelineDetailStore.getState();
Expand All @@ -73,15 +81,19 @@ export const PipelineDetailsTopPanel = () => {
window.localStorage.removeItem('editDraftId');
}
}, []);

return (
<FeatureProvider>
<Provider store={PipelineDetailStore}>
<div className="pipeline-details-top-panel">
<PipelineDetailsMetadata />
<ConnectedPipelineDetailsButtons />
<PipelineDetailsDetailsActions />
<PlusButton mode={PlusButton.MODE.resourcecenter} />
</div>
<PipelineDetailsWrapper>
<PipelineRunErrorDetails />
<div className="pipeline-details-top-panel">
<PipelineDetailsMetadata />
<ConnectedPipelineDetailsButtons />
<PipelineDetailsDetailsActions />
<PlusButton mode={PlusButton.MODE.resourcecenter} />
</div>
</PipelineDetailsWrapper>
</Provider>
</FeatureProvider>
);
Expand Down

0 comments on commit 9712ce8

Please sign in to comment.