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

feat(frontend): Wizard - Completion Steps Wire-Up #3546

Merged
merged 3 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions web/src/BaseApp.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import Router from 'components/Router';
import SettingsValuesProvider from 'providers/SettingsValues';
import Wrapper from './components/Wizard/Wrapper';

const BaseApp = () => (
<SettingsValuesProvider>
<Router />
</SettingsValuesProvider>
<Wrapper>
<SettingsValuesProvider>
<Router />
</SettingsValuesProvider>
</Wrapper>
);

export default BaseApp;
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import * as S from './DataStoreConfiguration.styled';
import TestConnectionStatus from '../TestConnectionStatus';

interface IProps {
isTestConnectionSuccess?: boolean;
isTestConnectionSuccess: boolean;
isSubmitLoading: boolean;
isValid: boolean;
dataStoreType: SupportedDataStores;
onSubmit(): void;
onTestConnection(): void;
Expand All @@ -22,7 +21,6 @@ const DataStoreConfiguration = ({
onTestConnection,
isSubmitLoading,
isTestConnectionSuccess,
isValid,
dataStoreType,
isWizard = false,
}: IProps) => (
Expand Down Expand Up @@ -50,7 +48,7 @@ const DataStoreConfiguration = ({
<TestConnectionStatus onTestConnection={onTestConnection} />
<AllowButton
operation={Operation.Configure}
disabled={!isValid || (isWizard && !isTestConnectionSuccess)}
disabled={!isTestConnectionSuccess}
loading={isSubmitLoading}
type="primary"
onClick={onSubmit}
Expand Down
25 changes: 3 additions & 22 deletions web/src/components/RunDetailLayout/RunDetailLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Tabs, TabsProps} from 'antd';
import {useEffect, useMemo, useState} from 'react';
import {useMemo} from 'react';
import {useParams} from 'react-router-dom';
import RunDetailAutomate from 'components/RunDetailAutomate';
import RunDetailTest from 'components/RunDetailTest';
Expand All @@ -8,17 +8,14 @@ import RunDetailTrigger from 'components/RunDetailTrigger';
import {RunDetailModes} from 'constants/TestRun.constants';
import useDocumentTitle from 'hooks/useDocumentTitle';
import Test from 'models/Test.model';
import {isRunStateSucceeded} from 'models/TestRun.model';
import {useNotification} from 'providers/Notification/Notification.provider';
import {useSettingsValues} from 'providers/SettingsValues/SettingsValues.provider';
import {useTestRun} from 'providers/TestRun/TestRun.provider';
import {useAppSelector} from 'redux/hooks';
import UserSelectors from 'selectors/User.selectors';
import TestRunAnalyticsService from 'services/Analytics/TestRunAnalytics.service';
import {ConfigMode} from 'types/DataStore.types';
import HeaderLeft from './HeaderLeft';
import HeaderRight from './HeaderRight';
import * as S from './RunDetailLayout.styled';
import useRunCompletion from './hooks/useRunCompletion';

interface IProps {
test: Test;
Expand All @@ -38,27 +35,11 @@ const renderTab = (title: string, testId: string, runId: number, mode: string) =

const RunDetailLayout = ({test: {id, name, trigger, skipTraceCollection}, test}: IProps) => {
const {mode = RunDetailModes.TRIGGER} = useParams();
const {showNotification} = useNotification();
const {isError, run, runEvents} = useTestRun();
const {dataStoreConfig} = useSettingsValues();
const [prevState, setPrevState] = useState(run.state);
useDocumentTitle(`${name} - ${run.state}`);
const runOriginPath = useAppSelector(UserSelectors.selectRunOriginPath);

useEffect(() => {
const isNoTracingMode = dataStoreConfig.mode === ConfigMode.NO_TRACING_MODE;

if (isRunStateSucceeded(run.state) && !isRunStateSucceeded(prevState)) {
showNotification({
type: 'success',
title: isNoTracingMode
? 'Response received. Skipping looking for trace as you are in No-Tracing Mode'
: 'Trace has been fetched successfully',
});
}

setPrevState(run.state);
}, [dataStoreConfig.mode, prevState, run.state, showNotification]);
useRunCompletion();

const tabBarExtraContent = useMemo(
() => ({
Expand Down
40 changes: 40 additions & 0 deletions web/src/components/RunDetailLayout/hooks/useRunCompletion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {useCallback, useEffect, useState} from 'react';
import {isRunStateSucceeded} from 'models/TestRun.model';
import {useNotification} from 'providers/Notification/Notification.provider';
import {useSettingsValues} from 'providers/SettingsValues/SettingsValues.provider';
import {useTestRun} from 'providers/TestRun/TestRun.provider';
import {ConfigMode} from 'types/DataStore.types';
import {useWizard} from 'providers/Wizard';

const useRunCompletion = () => {
const {showNotification} = useNotification();
const {run, isSkippedPolling} = useTestRun();
const {dataStoreConfig} = useSettingsValues();
const [prevState, setPrevState] = useState(run.state);
const {onCompleteStep} = useWizard();

const handleCompletion = useCallback(() => {
const isNoTracingMode = dataStoreConfig.mode === ConfigMode.NO_TRACING_MODE;

if (isRunStateSucceeded(run.state) && !isRunStateSucceeded(prevState)) {
showNotification({
type: 'success',
title: isNoTracingMode
? 'Response received. Skipping looking for trace as you are in No-Tracing Mode'
: 'Trace has been fetched successfully',
});

if (!isSkippedPolling) {
onCompleteStep('create_test');
}
}

setPrevState(run.state);
}, [dataStoreConfig.mode, isSkippedPolling, onCompleteStep, prevState, run.state, showNotification]);

useEffect(() => {
handleCompletion();
}, [dataStoreConfig.mode, handleCompletion, prevState, run.state, showNotification]);
};

export default useRunCompletion;
4 changes: 2 additions & 2 deletions web/src/components/Settings/DataStore/DataStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as S from './DataStore.styled';

const DataStore = () => {
const {dataStoreConfig} = useSettingsValues();
const {isLoading, isFormValid, onIsFormValid, onSaveConfig, onTestConnection} = useDataStore();
const {isLoading, onIsFormValid, onSaveConfig, onTestConnection, isTestConnectionSuccessful} = useDataStore();
const [form] = Form.useForm<TDraftDataStore>();

const handleOnSubmit = useCallback(
Expand All @@ -27,12 +27,12 @@ const DataStore = () => {
<S.Wrapper>
<S.FormContainer>
<DataStoreForm
isTestConnectionSuccess={isTestConnectionSuccessful}
form={form}
dataStoreConfig={dataStoreConfig}
onSubmit={handleOnSubmit}
onTestConnection={handleTestConnection}
isLoading={isLoading}
isFormValid={isFormValid}
onIsFormValid={onIsFormValid}
/>
</S.FormContainer>
Expand Down
6 changes: 3 additions & 3 deletions web/src/components/Settings/DataStoreForm/DataStoreForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface IProps {
onIsFormValid(isValid: boolean): void;
onTestConnection(): void;
isLoading: boolean;
isFormValid: boolean;
isTestConnectionSuccess: boolean;
}

const DataStoreForm = ({
Expand All @@ -26,7 +26,7 @@ const DataStoreForm = ({
onIsFormValid,
onTestConnection,
isLoading,
isFormValid,
isTestConnectionSuccess,
}: IProps) => {
const configuredDataStoreType = dataStoreConfig.defaultDataStore.type as SupportedDataStores;
const initialValues = useMemo(
Expand Down Expand Up @@ -69,9 +69,9 @@ const DataStoreForm = ({
</Form.Item>
<S.FactoryContainer>
<DataStoreConfiguration
isTestConnectionSuccess={isTestConnectionSuccess}
onSubmit={() => form.submit()}
onTestConnection={onTestConnection}
isValid={isFormValid}
isSubmitLoading={isLoading}
dataStoreType={dataStoreType ?? SupportedDataStores.JAEGER}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
import {Form} from 'antd';
import {useEffect, useMemo} from 'react';
import {useCallback, useEffect, useMemo, useState} from 'react';
import {useDataStore} from 'providers/DataStore/DataStore.provider';
import {TDraftDataStore} from 'types/DataStore.types';
import TracetestAPI from 'redux/apis/Tracetest/Tracetest.api';
import DataStoreService from 'services/DataStore.service';

export type TConnectionStatus = 'loading' | 'success' | 'error' | 'idle';

const POLL_INTERVAL = 1000;

const useTestConnectionStatus = () => {
// had to do this at this level because of this issue
// https://github.com/reduxjs/redux-toolkit/issues/2055
const {useResetTestOtlpConnectionMutation} = TracetestAPI.instance;
const {useResetTestOtlpConnectionMutation, useLazyTestOtlpConnectionQuery} = TracetestAPI.instance;
const [resetOtlpCount] = useResetTestOtlpConnectionMutation();
const [pollingInterval, setPollInterval] = useState<number | undefined>(undefined);
const [
testOtlpConnection,
{isLoading: isOtlpTestConnectionLoading, data: otlpTestConnectionResponse, isError: isOtlpTestConnectionError},
] = useLazyTestOtlpConnectionQuery({
pollingInterval,
});

const {
isTestConnectionLoading,
isOtlpTestConnectionError,
resetTestConnection,
otlpTestConnectionResponse,
onOtlpTestConnection,
} = useDataStore();
const {isTestConnectionLoading, resetTestConnection, onSetOtlpTestConnectionResponse} = useDataStore();
const form = Form.useFormInstance<TDraftDataStore>();

const status = useMemo<TConnectionStatus>(() => {
Expand All @@ -32,20 +35,48 @@

// listens to all form changes
const data = Form.useWatch([], form);
const isOtlpBased = useMemo(() => DataStoreService.getIsOtlpBased(form.getFieldsValue()), [data]);

Check warning on line 38 in web/src/components/TestConnectionStatus/hooks/useTestConnnectionStatus.ts

View workflow job for this annotation

GitHub Actions / WebUI unit tests

React Hook useMemo has a missing dependency: 'form'. Either include it or remove the dependency array

const onStartPolling = useCallback(async () => {
setPollInterval(POLL_INTERVAL);
await testOtlpConnection(undefined).unwrap();
}, [testOtlpConnection]);

const onReset = useCallback(() => {
// stops the polling
setPollInterval(undefined);

// resets backend otlp count to 0
resetOtlpCount(undefined);

// resets the test connection result to undefined
onSetOtlpTestConnectionResponse(undefined);

// resets the direct test connection
resetTestConnection();
}, [onSetOtlpTestConnectionResponse, resetOtlpCount, resetTestConnection]);

useEffect(() => {
if (isOtlpBased) onOtlpTestConnection();
// resets the test connection results and data
onReset();

// if its otlp, starts the polling mechanism
if (isOtlpBased) {
onStartPolling();
}
}, [isOtlpBased]);

Check warning on line 67 in web/src/components/TestConnectionStatus/hooks/useTestConnnectionStatus.ts

View workflow job for this annotation

GitHub Actions / WebUI unit tests

React Hook useEffect has missing dependencies: 'onReset' and 'onStartPolling'. Either include them or remove the dependency array

useEffect(() => {
return () => {
resetOtlpCount(undefined);
resetTestConnection();
};
}, []);
/// if we are polling, refresh the provider result with the response information
if (pollingInterval) onSetOtlpTestConnectionResponse(otlpTestConnectionResponse);
}, [onSetOtlpTestConnectionResponse, otlpTestConnectionResponse, pollingInterval]);

return {status, isOtlpBased, otlpResponse: otlpTestConnectionResponse, isLoading: isTestConnectionLoading};
return {
status,
isOtlpBased,
otlpResponse: otlpTestConnectionResponse,
isLoading: isTestConnectionLoading || isOtlpTestConnectionLoading,
};
};

export default useTestConnectionStatus;
13 changes: 2 additions & 11 deletions web/src/components/Wizard/Steps/TracingBackend/Configuration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,7 @@ interface IProps {
}

const Configuration = ({dataStore, onBack}: IProps) => {
const {
isLoading,
isFormValid,
onIsFormValid,
onSaveConfig,
onTestConnection,
testConnectionResponse,
otlpTestConnectionResponse,
} = useDataStore();
const {isLoading, onIsFormValid, onSaveConfig, onTestConnection, isTestConnectionSuccessful} = useDataStore();
const [form] = Form.useForm<TDraftDataStore>();

const handleTestConnection = useCallback(async () => {
Expand Down Expand Up @@ -81,8 +73,7 @@ const Configuration = ({dataStore, onBack}: IProps) => {
onTestConnection={handleTestConnection}
dataStoreType={dataStore.type as SupportedDataStores}
isSubmitLoading={isLoading}
isValid={isFormValid}
isTestConnectionSuccess={testConnectionResponse?.allPassed || !!otlpTestConnectionResponse?.spanCount}
isTestConnectionSuccess={isTestConnectionSuccessful}
isWizard
/>
</Form>
Expand Down
10 changes: 1 addition & 9 deletions web/src/components/Wizard/Wrapper/Wrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import {TWizardMap} from 'types/Wizard.types';
import WizardProvider from 'providers/Wizard/Wizard.provider';
import {withCustomization} from 'providers/Customization';
import DataStoreProvider from 'providers/DataStore/DataStore.provider';
import SettingsProvider from 'providers/Settings/Settings.provider';
import TracingBackend, {TracingBackendTab} from '../Steps/TracingBackend';
import CreateTest, {CreateTestTab} from '../Steps/CreateTest';

Expand Down Expand Up @@ -34,12 +32,6 @@ interface IProps {
children: React.ReactNode;
}

const Wrapper = ({children}: IProps) => (
<DataStoreProvider>
<SettingsProvider>
<WizardProvider stepsMap={steps}>{children}</WizardProvider>
</SettingsProvider>
</DataStoreProvider>
);
const Wrapper = ({children}: IProps) => <WizardProvider stepsMap={steps}>{children}</WizardProvider>;

export default withCustomization(Wrapper, 'wizardWrapper');
36 changes: 24 additions & 12 deletions web/src/pages/Wizard/Wizard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import withAnalytics from 'components/WithAnalytics/WithAnalytics';
import Header from 'components/Wizard/Header';
import Content from 'components/Wizard/Content';
import DataStoreProvider from 'providers/DataStore/DataStore.provider';
import SettingsProvider from 'providers/Settings/Settings.provider';
import {useWizard} from 'providers/Wizard';
import * as S from './Wizard.styled';

Expand All @@ -9,19 +11,29 @@ const Wizard = () => {
const completedSteps = steps.filter(({state}) => state === 'completed').length;

return (
<S.Container>
<S.Header>
<S.Title>Welcome to Tracetest!</S.Title>
<S.Text>
Here&apos;s a guide to get started and help you test your modern applications with OpenTelemetry.
</S.Text>
</S.Header>
<DataStoreProvider>
<SettingsProvider>
<S.Container>
<S.Header>
<S.Title>Welcome to Tracetest!</S.Title>
<S.Text>
Here&apos;s a guide to get started and help you test your modern applications with OpenTelemetry.
</S.Text>
</S.Header>

<S.Body>
<Header activeStep={completedSteps} totalCompleteSteps={steps.length} />
<Content activeStepId={activeStepId} isLoading={isLoading} onChange={onGoTo} onNext={onNext} steps={steps} />
</S.Body>
</S.Container>
<S.Body>
<Header activeStep={completedSteps} totalCompleteSteps={steps.length} />
<Content
activeStepId={activeStepId}
isLoading={isLoading}
onChange={onGoTo}
onNext={onNext}
steps={steps}
/>
</S.Body>
</S.Container>
</SettingsProvider>
</DataStoreProvider>
);
};

Expand Down
Loading
Loading