Skip to content

Commit

Permalink
add store
Browse files Browse the repository at this point in the history
  • Loading branch information
GnsP committed Dec 31, 2024
1 parent f116f11 commit 3f3fdb9
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 47 deletions.
2 changes: 2 additions & 0 deletions app/cdap/api/pipeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export const MyPipelineApi = {
getStatistics: apiCreator(dataSrc, 'GET', 'REQUEST', statsPath),
getMetadataEndpoints: apiCreator(dataSrc, 'GET', 'REQUEST', metadataPath),
getRunDetails: apiCreator(dataSrc, 'GET', 'REQUEST', `${programPath}/runs/:runid`),
getRunErrorDetails: apiCreator(dataSrc, 'POST', 'REQUEST', `${programPath}/runs/:runid/classify`),

getRuns: apiCreator(dataSrc, 'GET', 'REQUEST', `${programPath}/runs`),
getVersionedRuns: apiCreator(dataSrc, 'GET', 'REQUEST', `${versionedProgramPath}/runs`),
pollRuns: apiCreator(dataSrc, 'GET', 'POLL', `${programPath}/runs`),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,20 @@
*/

import React, { useEffect, useState } from 'react';
import { useSelector, Provider } from 'react-redux';
import T from 'i18n-react';
import { useSelector, Provider, useDispatch } 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';
import { ACTIONS } from '../store';
import { MyPipelineApi } from 'api/pipeline';
import { getCurrentNamespace } from 'services/NamespaceStore';
import { getDataTestid } from '@cdap-ui/testids/TestidsProvider';

const PREFIX = 'features.PipelineDetails.ErrorDetails';
const TEST_PREFIX = 'features.pipelineDetails.errorDetails';

const PipelineRunErrorDetailsWrapper = styled.div`
width: 100%;
Expand Down Expand Up @@ -74,7 +82,11 @@ 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>;
return (
<p data-testid={getDataTestid(`${TEST_PREFIX}.crrorCountMessage`)}>
{T.translate(`${PREFIX}.errorCountMessage`, { errorCount })}
</p>
);
};

interface IErrorEntry {
Expand All @@ -91,8 +103,22 @@ interface IErrorClassificationResponse {
export default function PipelineRunErrorDetails() {
const [detailsExpanded, setDetailsExpanded] = useState<boolean>(false);
const [logsOpened, setLogsOpened] = useState<boolean>(false);
const [errorDetails, setErrorDetails] = useState<IErrorClassificationResponse>(null);
const [loading, setLoading] = useState<boolean>(false);

const appId = useSelector((state) => state.name);
const currentRun = useSelector((state) => state.currentRun);
const errorDetails = useSelector((state) => state.runErrorDetails[currentRun?.runid]);
const dispatch = useDispatch();

function setErrorDetails(runid: string, errors: IErrorEntry[]) {
dispatch({
type: ACTIONS.SET_RUN_ERROR_DETAILS,
payload: {
runid,
errors,
},
});
}

function toggleErrorDetails() {
setDetailsExpanded((x) => !x);
Expand All @@ -107,39 +133,24 @@ export default function PipelineRunErrorDetails() {
}, []);

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);
setLoading(true);
// POST `/namespaces/:namespace/apps/:appid/:programType/:programName/runs/:runid/classify?isPreview`
MyPipelineApi.getRunErrorDetails({
namespace: getCurrentNamespace(),
appId,
programType: 'workflows',
programName: 'DataPipelineWorkflow',
runid: currentRun?.runid,
}).subscribe(
(res: IErrorEntry[]) => {
setErrorDetails(currentRun?.runid, res);
setLoading(false);
},
(err) => {
setErrorDetails(currentRun?.runid, []);
setLoading(false);
}
);
}

function viewLogs() {
Expand All @@ -152,10 +163,12 @@ export default function PipelineRunErrorDetails() {
}

useEffect(() => {
fetchErrorDetails();
}, [currentRun.runid, currentRun.status]);
if (!errorDetails && !loading && currentRun?.status === 'FAILED') {
fetchErrorDetails();
}
}, [currentRun?.runid, currentRun?.status, errorDetails, loading]);

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

Expand All @@ -165,22 +178,30 @@ export default function PipelineRunErrorDetails() {
<Provider store={PipelineMetricsStore}>
<ShortErrorMessage>
<PipelineErrorCountMessage />
<Button color="inherit" onClick={toggleErrorDetails}>
{detailsExpanded ? 'Close' : 'View details'}
<Button
color="inherit"
onClick={toggleErrorDetails}
data-testid={getDataTestid(
detailsExpanded ? `${TEST_PREFIX}.closeButton` : `${TEST_PREFIX}.viewDetailsButton`
)}
>
{detailsExpanded
? T.translate(`${PREFIX}.closeButton`)
: T.translate(`${PREFIX}.viewDetailsButton`)}
</Button>
</ShortErrorMessage>
{detailsExpanded && (
<ErrorDetailsContainer>
<Table>
<TableHead>
<TableRow>
<TableCell>Error category</TableCell>
<TableCell>Error message</TableCell>
<TableCell>Error reason</TableCell>
<TableCell>{T.translate(`${PREFIX}.errorCategoryHeader`)}</TableCell>
<TableCell>{T.translate(`${PREFIX}.errorMessageHeader`)}</TableCell>
<TableCell>{T.translate(`${PREFIX}.errorReasonHeader`)}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{errorDetails?.errors?.map((err) => (
{errorDetails?.map((err) => (
<TableRow>
<TableCell>{err.errorCategory}</TableCell>
<TableCell>{err.errorMessage}</TableCell>
Expand All @@ -193,8 +214,12 @@ export default function PipelineRunErrorDetails() {
<p>
Help us improve the error classification. Raise improvement <a href="#">here</a>.
</p>
<Button color="secondary" onClick={viewLogs}>
View logs
<Button
color="secondary"
onClick={viewLogs}
data-testid={getDataTestid(`${TEST_PREFIX}.viewLogsButton`)}
>
{T.translate(`${PREFIX}.viewLogsButton`)}
</Button>
</ErrorImprovementMessage>
</ErrorDetailsContainer>
Expand Down
10 changes: 10 additions & 0 deletions app/cdap/components/PipelineDetails/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const ACTIONS = {
SET_USER_RUNTIME_ARGUMENTS: 'SET_USER_RUNTIME_ARGUMENTS',
SET_MACROS_AND_USER_RUNTIME_ARGUMENTS: 'SET_MACROS_AND_USER_RUNTIME_ARGUMENTS',
SET_RUNTIME_ARGUMENTS_FOR_DISPLAY: 'SET_RUNTIME_ARGUMENTS_FOR_DISPLAY',
SET_RUN_ERROR_DETAILS: 'SET_RUN_ERROR_DETAILS',

// Loading and error states Actions
SET_RUN_BUTTON_LOADING: 'SET_RUN_BUTTON_LOADING',
Expand Down Expand Up @@ -84,6 +85,7 @@ const DEFAULT_PIPELINE_DETAILS = {
// `runtimeArgsForDisplay` combines `macrosMap` and `userRuntimeArgumentsMap` objects
// to create an object that can be used as a prop to the KeyValuePairs component
runtimeArgsForDisplay: {},
runErrorDetails: {},

// loading and error states
runButtonLoading: false,
Expand Down Expand Up @@ -229,6 +231,14 @@ const pipelineDetails = (state = DEFAULT_PIPELINE_DETAILS, action = defaultActio
...state,
runtimeArgsForDisplay: action.payload.args,
};
case ACTIONS.SET_RUN_ERROR_DETAILS:
return {
...state,
runErrorDetails: {
...state.runErrorDetails,
[action.payload.runid]: action.payload.errors,
},
};
case ACTIONS.SET_RUN_BUTTON_LOADING:
return {
...state,
Expand Down
6 changes: 6 additions & 0 deletions app/cdap/testids/testids.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -333,3 +333,9 @@ features:
home:
wrangler:
pageLink: ~
pipelineDetails:
errorDetails:
closeButton: ~
errorCountMessage: ~
viewDetailsButton: ~
viewLogsButton: ~
10 changes: 10 additions & 0 deletions app/cdap/text/text-en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2463,6 +2463,16 @@ features:

PipelineDetails:
duration: Duration
ErrorDetails:
errorCountMessage:
1: Pipeline execution failed with {errorCount} error.
_: Pipeline execution failed with {errorCount} errors.
closeButton: Close
viewDetailsButton: View details
viewLogsButton: View logs
errorCategoryHeader: Error category
errorMessageHeader: Error Message
errorReasonHeader: Error reason
PipelineRuntimeArgsDropdownBtn:
RuntimeArgsTabContent:
ProvidedPopover:
Expand Down

0 comments on commit 3f3fdb9

Please sign in to comment.