format cron from quartz to unix in applications (#15038)

* fix initial value cron value in applications

* added unit test for cron utils functions

* remove quartz cron for applicaion from ui install and edit

* changes as per comments and fix unit test

* fix unit test

* fix user cypress failure on click
This commit is contained in:
Ashish Gupta 2024-02-09 15:17:14 +05:30 committed by GitHub
parent 8bea265f0a
commit 847b2d8bc9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 91 additions and 60 deletions

View File

@ -85,8 +85,7 @@ export const softDeleteUser = (username: string, displayName: string) => {
verifyResponseStatusCode('@searchUser', 200);
// Click on delete button
cy.get(`[data-testid="delete-user-btn-${username}"]`);
cy.get(':nth-child(4) > .ant-space > .ant-space-item > .ant-btn').click();
cy.get(`[data-testid="delete-user-btn-${username}"]`).click();
// Soft deleting the user
cy.get('[data-testid="soft-delete"]').click();
cy.get('[data-testid="confirmation-text-input"]').type('DELETE');

View File

@ -45,7 +45,6 @@ export interface TestSuiteSchedulerProps {
initialData?: string;
onSubmit: (repeatFrequency: string) => void;
onCancel: () => void;
isQuartzCron?: boolean;
buttonProps?: {
okText: string;
cancelText: string;

View File

@ -11,7 +11,7 @@
* limitations under the License.
*/
import { Button, Col, Row, Space } from 'antd';
import { Button, Col, Form, Row, Space } from 'antd';
import { t } from 'i18next';
import React, { useEffect, useState } from 'react';
import CronEditor from '../../common/CronEditor/CronEditor';
@ -22,7 +22,6 @@ const TestSuiteScheduler: React.FC<TestSuiteSchedulerProps> = ({
buttonProps,
onCancel,
onSubmit,
isQuartzCron = false,
includePeriodOptions,
}) => {
const [repeatFrequency, setRepeatFrequency] = useState<string | undefined>(
@ -38,12 +37,13 @@ const TestSuiteScheduler: React.FC<TestSuiteSchedulerProps> = ({
return (
<Row gutter={[16, 32]}>
<Col span={24}>
<CronEditor
includePeriodOptions={includePeriodOptions}
isQuartzCron={isQuartzCron}
value={repeatFrequency}
onChange={(value: string) => setRepeatFrequency(value)}
/>
<Form data-testid="schedule-container">
<CronEditor
includePeriodOptions={includePeriodOptions}
value={repeatFrequency}
onChange={(value: string) => setRepeatFrequency(value)}
/>
</Form>
</Col>
<Col span={24}>
<Space className="w-full justify-end" size={16}>

View File

@ -348,7 +348,6 @@ const AppDetails = () => {
{appData && (
<AppSchedule
appData={appData}
onCancel={onBrowseAppsClick}
onDemandTrigger={onDemandTrigger}
onDeployTrigger={onDeployTrigger}
onSave={onAppScheduleSave}

View File

@ -124,17 +124,14 @@ jest.mock('../AppRunsHistory/AppRunsHistory.component', () =>
jest.mock('../AppSchedule/AppSchedule.component', () =>
jest
.fn()
.mockImplementation(
({ onCancel, onSave, onDemandTrigger, onDeployTrigger }) => (
<>
AppSchedule
<button onClick={onCancel}>Cancel AppSchedule</button>
<button onClick={onSave}>Save AppSchedule</button>
<button onClick={onDemandTrigger}>DemandTrigger AppSchedule</button>
<button onClick={onDeployTrigger}>DeployTrigger AppSchedule</button>
</>
)
)
.mockImplementation(({ onSave, onDemandTrigger, onDeployTrigger }) => (
<>
AppSchedule
<button onClick={onSave}>Save AppSchedule</button>
<button onClick={onDemandTrigger}>DemandTrigger AppSchedule</button>
<button onClick={onDeployTrigger}>DeployTrigger AppSchedule</button>
</>
))
);
jest.mock('./ApplicationSchemaClassBase', () => ({
@ -233,9 +230,5 @@ describe('AppDetails component', () => {
expect(mockDeployApp).toHaveBeenCalled();
expect(mockGetApplicationByName).toHaveBeenCalled();
userEvent.click(screen.getByRole('button', { name: 'Cancel AppSchedule' }));
expect(mockPush).toHaveBeenCalled();
});
});

View File

@ -24,9 +24,7 @@ import {
AppScheduleClass,
AppType,
} from '../../../generated/entity/applications/app';
import { PipelineType } from '../../../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { getIngestionPipelineByFqn } from '../../../rest/ingestionPipelineAPI';
import { getIngestionFrequency } from '../../../utils/CommonUtils';
import TestSuiteScheduler from '../../AddDataQualityTest/components/TestSuiteScheduler';
import Loader from '../../Loader/Loader';
import AppRunsHistory from '../AppRunsHistory/AppRunsHistory.component';
@ -74,10 +72,6 @@ const AppSchedule = ({
return cronstrue.toString(cronExp, {
throwExceptionOnParseError: false,
// Quartz cron format accepts 1-7 or SUN-SAT so need to increment index by 1
// Ref: https://www.quartz-scheduler.org/api/2.1.7/org/quartz/CronExpression.html
dayOfWeekStartIndexZero: false,
monthStartIndexZero: false,
});
}
@ -225,13 +219,14 @@ const AppSchedule = ({
open={showModal}
title={t('label.update-entity', { entity: t('label.schedule') })}>
<TestSuiteScheduler
isQuartzCron
buttonProps={{
cancelText: t('label.cancel'),
okText: t('label.save'),
}}
includePeriodOptions={initialOptions}
initialData={getIngestionFrequency(PipelineType.Application)}
initialData={
(appData.appSchedule as AppScheduleClass)?.cronExpression ?? ''
}
onCancel={onDialogCancel}
onSubmit={onDialogSave}
/>

View File

@ -35,10 +35,6 @@ jest.mock('../../../rest/ingestionPipelineAPI', () => ({
.mockImplementation((...args) => mockGetIngestionPipelineByFqn(...args)),
}));
jest.mock('../../../utils/CommonUtils', () => ({
getIngestionFrequency: jest.fn(),
}));
jest.mock('../../AddDataQualityTest/components/TestSuiteScheduler', () =>
jest.fn().mockImplementation(({ onSubmit, onCancel }) => (
<div>
@ -73,7 +69,6 @@ const mockProps1 = {
name: 'DataInsightsReportApplication',
},
onSave: mockOnSave,
onCancel: jest.fn(),
onDemandTrigger: mockOnDemandTrigger,
onDeployTrigger: mockOnDeployTrigger,
};

View File

@ -14,7 +14,6 @@ import { App } from '../../../generated/entity/applications/app';
export interface AppScheduleProps {
appData: App;
onCancel: () => void;
onSave: (cron: string) => void;
onDemandTrigger: () => void;
onDeployTrigger: () => void;

View File

@ -125,10 +125,10 @@ const TeamHierarchy: FC<TeamHierarchyProps> = ({
render: (description: string) => (
<Typography.Paragraph
className="m-b-0"
style={{whiteSpace:"pre-wrap"}}
ellipsis={{
rows: 2,
}}
style={{ whiteSpace: 'pre-wrap' }}
title={description}>
{isEmpty(description) ? '--' : description}
</Typography.Paragraph>

View File

@ -38,14 +38,11 @@ import {
ScheduleTimeline,
} from '../../generated/entity/applications/createAppRequest';
import { AppMarketPlaceDefinition } from '../../generated/entity/applications/marketplace/appMarketPlaceDefinition';
import { PipelineType } from '../../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { useFqn } from '../../hooks/useFqn';
import { installApplication } from '../../rest/applicationAPI';
import { getMarketPlaceApplicationByFqn } from '../../rest/applicationMarketPlaceAPI';
import {
getEntityMissingError,
getIngestionFrequency,
} from '../../utils/CommonUtils';
import { getEntityMissingError } from '../../utils/CommonUtils';
import { getCronInitialValue } from '../../utils/CronUtils';
import { formatFormDataForSubmit } from '../../utils/JSONSchemaFormUtils';
import {
getMarketPlaceAppDetailsPath,
@ -72,6 +69,24 @@ const AppInstall = () => {
[appData]
);
const { initialOptions, initialValue } = useMemo(() => {
let initialOptions;
if (appData?.name === 'DataInsightsReportApplication') {
initialOptions = ['Week'];
} else if (appData?.appType === AppType.External) {
initialOptions = ['Day'];
}
return {
initialOptions,
initialValue: getCronInitialValue(
appData?.appType ?? AppType.Internal,
appData?.name ?? ''
),
};
}, [appData?.name, appData?.appType]);
const fetchAppDetails = useCallback(async () => {
setIsLoading(true);
try {
@ -126,16 +141,6 @@ const AppInstall = () => {
setActiveServiceStep(3);
};
const initialOptions = useMemo(() => {
if (appData?.name === 'DataInsightsReportApplication') {
return ['Week'];
} else if (appData?.appType === AppType.External) {
return ['Day'];
}
return undefined;
}, [appData?.name, appData?.appType]);
const RenderSelectedTab = useCallback(() => {
if (!appData || !jsonSchema) {
return <></>;
@ -180,9 +185,8 @@ const AppInstall = () => {
<div className="w-500 p-md border rounded-4">
<Typography.Title level={5}>{t('label.schedule')}</Typography.Title>
<TestSuiteScheduler
isQuartzCron
includePeriodOptions={initialOptions}
initialData={getIngestionFrequency(PipelineType.Application)}
initialData={initialValue}
onCancel={() =>
setActiveServiceStep(appData.allowConfiguration ? 2 : 1)
}

View File

@ -11,7 +11,8 @@
* limitations under the License.
*/
import { getQuartzCronExpression } from './CronUtils';
import { AppType } from '../generated/entity/applications/app';
import { getCronInitialValue, getQuartzCronExpression } from './CronUtils';
describe('getQuartzCronExpression function', () => {
it('should generate cron expression for every minute', () => {
@ -94,3 +95,32 @@ describe('getQuartzCronExpression function', () => {
expect(result).toEqual('0 30 10 ? * 4');
});
});
describe('getCronInitialValue function', () => {
it('should generate hour cron expression if appType is internal and appName is not DataInsightsReportApplication', () => {
const result = getCronInitialValue(
AppType.Internal,
'SearchIndexingApplication'
);
expect(result).toEqual('0 * * * *');
});
it('should generate week cron expression if appName is DataInsightsReportApplication', () => {
const result = getCronInitialValue(
AppType.Internal,
'DataInsightsReportApplication'
);
expect(result).toEqual('0 0 * * 0');
});
it('should generate day cron expression if appType is external', () => {
const result = getCronInitialValue(
AppType.External,
'DataInsightsApplication'
);
expect(result).toEqual('0 0 * * *');
});
});

View File

@ -31,6 +31,7 @@ import {
StateValue,
ToDisplay,
} from '../components/common/CronEditor/CronEditor.interface';
import { AppType } from '../generated/entity/applications/app';
export const getQuartzCronExpression = (state: StateValue) => {
const {
@ -172,3 +173,20 @@ export const getStateValue = (valueStr: string) => {
return stateVal;
};
export const getCronInitialValue = (appType: AppType, appName: string) => {
const value = {
min: 0,
hour: 0,
};
let initialValue = getHourCron(value);
if (appName === 'DataInsightsReportApplication') {
initialValue = getWeekCron({ ...value, dow: 0 });
} else if (appType === AppType.External) {
initialValue = getDayCron(value);
}
return initialValue;
};