@orchestrator-ui/orchestrator-ui-components 7.5.1 → 7.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +7 -7
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-test.log +7 -7
- package/CHANGELOG.md +26 -0
- package/dist/index.d.ts +542 -53
- package/dist/index.js +1297 -1101
- package/dist/index.js.map +1 -1
- package/jest.config.cjs +4 -1
- package/package.json +6 -6
- package/src/components/WfoAgent/ExportButton/ExportButton.tsx +5 -11
- package/src/components/WfoAgent/WfoAgent/WfoAgent.tsx +79 -31
- package/src/components/WfoAgent/WfoAgentChart/WfoAgentLineChart.tsx +2 -2
- package/src/components/WfoAgent/WfoAgentChart/WfoAgentPieChart.tsx +2 -2
- package/src/components/WfoAgent/WfoAgentTable/WfoAgentTable.tsx +9 -9
- package/src/components/WfoAgent/WfoAgentVisualization/WfoAgentVisualization.tsx +2 -2
- package/src/components/WfoAgent/WfoPlanProgress/WfoPlanProgress.tsx +107 -0
- package/src/components/WfoAgent/WfoPlanProgress/index.ts +1 -0
- package/src/components/WfoAgent/WfoPlanProgress/styles.ts +62 -0
- package/src/components/WfoAgent/WfoQueryArtifact/WfoQueryArtifact.tsx +40 -0
- package/src/components/WfoAgent/WfoQueryArtifact/index.ts +1 -0
- package/src/components/WfoAgent/index.ts +2 -0
- package/src/components/WfoBadges/WfoEngineStatusBadge/WfoEngineStatusBadge.tsx +3 -1
- package/src/components/WfoBadges/WfoVersionIncompatibleBadge/WfoVersionIncompatibleBadge.tsx +7 -6
- package/src/components/WfoBadges/WfoWebsocketStatusBadge/WfoWebsocketStatusBadge.tsx +9 -3
- package/src/components/WfoKeyValueTable/WfoValueCell.tsx +1 -1
- package/src/components/WfoPydanticForm/fields/WfoInteger.tsx +22 -3
- package/src/components/WfoSubscription/WfoSubscriptionActions/WfoSubscriptionActions.tsx +3 -2
- package/src/components/WfoSubscription/WfoSubscriptionGeneralSections/WfoSubscriptionDetailSection.tsx +34 -3
- package/src/components/WfoSubscriptionsList/WfoSubscriptionsList.tsx +9 -9
- package/src/components/WfoTable/WfoAdvancedTable/WfoAdvancedTable.tsx +1 -1
- package/src/components/WfoTable/WfoFirstPartUUID/WfoFirstPartUUID.tsx +1 -1
- package/src/components/WfoTitleWithWebsocketBadge/WfoTitleWithWebsocketBadge.tsx +3 -2
- package/src/components/WfoWorkflowSteps/WfoStep/WfoStep.tsx +57 -40
- package/src/components/WfoWorkflowSteps/WfoStep/useStepDetailOverride.ts +9 -0
- package/src/components/WfoWorkflowSteps/WfoWorkflowStepList/WfoStepListHeader.tsx +0 -1
- package/src/configuration/constants.ts +3 -0
- package/src/configuration/version.ts +1 -1
- package/src/hooks/useAgentPlanEvents.ts +188 -0
- package/src/messages/en-GB.json +7 -0
- package/src/messages/nl-NL.json +2 -0
- package/src/pages/metadata/WfoScheduleTaskFormPage.tsx +153 -35
- package/src/rtk/endpoints/agentQueryResults.ts +19 -0
- package/src/rtk/endpoints/forms.ts +1 -1
- package/src/rtk/endpoints/index.ts +1 -0
- package/src/rtk/endpoints/metadata/scheduledTasks.ts +9 -17
- package/src/rtk/endpoints/streamMessages.ts +10 -23
- package/src/rtk/slices/orchestratorComponentOverride.ts +5 -1
- package/src/types/search.ts +19 -4
- package/src/utils/compareVersions.spec.ts +5 -0
- package/src/utils/compareVersions.ts +55 -23
- package/src/components/WfoAgent/ToolProgress/DiscoverFilterPathsDisplay.tsx +0 -99
- package/src/components/WfoAgent/ToolProgress/RunSearchDisplay.tsx +0 -34
- package/src/components/WfoAgent/ToolProgress/SetFilterTreeDisplay.styles.ts +0 -62
- package/src/components/WfoAgent/ToolProgress/SetFilterTreeDisplay.tsx +0 -107
- package/src/components/WfoAgent/ToolProgress/StartNewSearchDisplay.tsx +0 -60
- package/src/components/WfoAgent/ToolProgress/ToolProgress.tsx +0 -98
- package/src/components/WfoAgent/ToolProgress/index.ts +0 -1
- package/src/components/WfoAgent/ToolProgress/styles.ts +0 -52
|
@@ -1,27 +1,33 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useCallback, useMemo } from 'react';
|
|
2
2
|
|
|
3
3
|
import _ from 'lodash';
|
|
4
4
|
import { useTranslations } from 'next-intl';
|
|
5
5
|
import { useRouter } from 'next/router';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from '
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
PydanticFormDefinitionResponse,
|
|
15
|
-
PydanticFormSuccessResponse,
|
|
16
|
-
RawJsonProperties,
|
|
17
|
-
} from 'pydantic-forms';
|
|
18
|
-
|
|
19
|
-
import { Footer, PATH_METADATA_SCHEDULED_TASKS, WfoContentHeader, WfoLoading } from '@/components';
|
|
6
|
+
import { PydanticForm } from 'pydantic-forms';
|
|
7
|
+
import type { PydanticFormApiProvider } from 'pydantic-forms';
|
|
8
|
+
import { PydanticFormApiResponseType, PydanticFormFieldFormat, PydanticFormFieldType } from 'pydantic-forms';
|
|
9
|
+
import type { PydanticFormDefinitionResponse, PydanticFormSuccessResponse, RawJsonProperties } from 'pydantic-forms';
|
|
10
|
+
|
|
11
|
+
import { PATH_METADATA_SCHEDULED_TASKS } from '@/components';
|
|
12
|
+
import { WfoContentHeader, WfoLoading } from '@/components';
|
|
13
|
+
import { Footer } from '@/components/WfoPydanticForm/Footer';
|
|
20
14
|
import { NUMBER_OF_ITEMS_REPRESENTING_ALL_ITEMS } from '@/configuration';
|
|
21
|
-
import {
|
|
22
|
-
import
|
|
23
|
-
import {
|
|
24
|
-
|
|
15
|
+
import { useShowToastMessage } from '@/hooks';
|
|
16
|
+
import { useGetPydanticFormsConfig } from '@/hooks/useGetPydanticFormsConfig';
|
|
17
|
+
import {
|
|
18
|
+
HttpStatus,
|
|
19
|
+
ScheduledTaskPostPayload,
|
|
20
|
+
isFetchBaseQueryError,
|
|
21
|
+
isRecord,
|
|
22
|
+
useCreateScheduledTaskMutation,
|
|
23
|
+
} from '@/rtk';
|
|
24
|
+
import type { CronKwargs } from '@/rtk';
|
|
25
|
+
import { useGetTasksQuery } from '@/rtk';
|
|
26
|
+
import { useStartFormMutation } from '@/rtk/endpoints/forms';
|
|
27
|
+
import { useGetVersionsQuery } from '@/rtk/endpoints/versions';
|
|
28
|
+
import { ToastTypes } from '@/types';
|
|
29
|
+
import { Intervals, TaskDefinition, TaskType } from '@/types';
|
|
30
|
+
import { compareVersions } from '@/utils/compareVersions';
|
|
25
31
|
|
|
26
32
|
type CreateScheduleFormStep1 = {
|
|
27
33
|
workflowId: TaskDefinition['workflowId'];
|
|
@@ -50,7 +56,8 @@ type CreateScheduleFormStep2 =
|
|
|
50
56
|
|
|
51
57
|
type CreateScheduleFormInput = [CreateScheduleFormStep1, CreateScheduleFormStep2];
|
|
52
58
|
|
|
53
|
-
|
|
59
|
+
// TODO: remove after 5.0.0 releases and rename WfoScheduleTaskFormPageBackend to WfoScheduleTaskFormPage
|
|
60
|
+
export const WfoScheduleTaskFormPageHardCoded = () => {
|
|
54
61
|
const t = useTranslations('metadata.scheduleTaskForm');
|
|
55
62
|
const { showToastMessage } = useShowToastMessage();
|
|
56
63
|
|
|
@@ -256,13 +263,15 @@ export const WfoScheduleTaskFormPage = () => {
|
|
|
256
263
|
|
|
257
264
|
if (userInputStep1.taskType === TaskType.DATE) {
|
|
258
265
|
return {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
266
|
+
scheduled_type: 'create',
|
|
267
|
+
workflow_id: task.workflowId,
|
|
268
|
+
workflow_name: task.name,
|
|
269
|
+
name: task.description,
|
|
270
|
+
trigger: userInputStep1.taskType,
|
|
271
|
+
trigger_kwargs: {
|
|
264
272
|
run_date: startDate,
|
|
265
273
|
},
|
|
274
|
+
user_inputs: [],
|
|
266
275
|
};
|
|
267
276
|
} else if (userInputStep1.taskType === TaskType.INTERVAL) {
|
|
268
277
|
const step2Input = userInputStep2 as CreateScheduleFormStep2Interval;
|
|
@@ -273,24 +282,28 @@ export const WfoScheduleTaskFormPage = () => {
|
|
|
273
282
|
}
|
|
274
283
|
|
|
275
284
|
return {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
285
|
+
scheduled_type: 'create',
|
|
286
|
+
workflow_id: task.workflowId,
|
|
287
|
+
workflow_name: task.name,
|
|
288
|
+
name: task.description,
|
|
289
|
+
trigger: userInputStep1.taskType,
|
|
290
|
+
trigger_kwargs: {
|
|
281
291
|
start_date: startDate,
|
|
282
292
|
...intervalArg,
|
|
283
293
|
},
|
|
294
|
+
user_inputs: [],
|
|
284
295
|
};
|
|
285
296
|
} else if (userInputStep1.taskType === TaskType.CRON) {
|
|
286
297
|
const step2Input = userInputStep2 as CreateScheduleFormStep2Cron;
|
|
287
298
|
// minute hour day month weekday
|
|
288
299
|
return {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
300
|
+
scheduled_type: 'create',
|
|
301
|
+
workflow_id: task.workflowId,
|
|
302
|
+
workflow_name: task.name,
|
|
303
|
+
name: task.description,
|
|
304
|
+
trigger: userInputStep1.taskType,
|
|
305
|
+
trigger_kwargs: getCronKwargs(step2Input.cron, startDate),
|
|
306
|
+
user_inputs: [],
|
|
294
307
|
};
|
|
295
308
|
}
|
|
296
309
|
throw new Error('Unknown or missing task type');
|
|
@@ -420,3 +433,108 @@ export const WfoScheduleTaskFormPage = () => {
|
|
|
420
433
|
</>
|
|
421
434
|
);
|
|
422
435
|
};
|
|
436
|
+
|
|
437
|
+
const START_SCHEDULE_PAYLOAD = {};
|
|
438
|
+
|
|
439
|
+
export const WfoScheduleTaskFormPageBackend = () => {
|
|
440
|
+
const { showToastMessage } = useShowToastMessage();
|
|
441
|
+
|
|
442
|
+
const generateFormId = useMemo(() => {
|
|
443
|
+
return `${JSON.stringify(START_SCHEDULE_PAYLOAD)}`;
|
|
444
|
+
}, []);
|
|
445
|
+
|
|
446
|
+
const [startForm] = useStartFormMutation();
|
|
447
|
+
const [createScheduledTask, mutationState] = useCreateScheduledTaskMutation();
|
|
448
|
+
const router = useRouter();
|
|
449
|
+
|
|
450
|
+
const onSuccess = useCallback(
|
|
451
|
+
async (_fieldValues: object, req: object) => {
|
|
452
|
+
const request = req as {
|
|
453
|
+
status: HttpStatus;
|
|
454
|
+
data: ScheduledTaskPostPayload;
|
|
455
|
+
};
|
|
456
|
+
if (request?.data?.workflow_id) {
|
|
457
|
+
const resp = await createScheduledTask(request.data);
|
|
458
|
+
if (!resp?.error) {
|
|
459
|
+
router.replace(PATH_METADATA_SCHEDULED_TASKS);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
[router, createScheduledTask],
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
const getPydanticFormProvider = useCallback(() => {
|
|
467
|
+
const pydanticFormProvider: PydanticFormApiProvider = async ({ requestBody = [], formKey }) => {
|
|
468
|
+
const userInputs =
|
|
469
|
+
_.isEmpty(START_SCHEDULE_PAYLOAD) ? [...requestBody] : [{ ...START_SCHEDULE_PAYLOAD }, ...requestBody];
|
|
470
|
+
|
|
471
|
+
const response = startForm({
|
|
472
|
+
formKey,
|
|
473
|
+
userInputs,
|
|
474
|
+
});
|
|
475
|
+
return response
|
|
476
|
+
.then(({ error, data }) => {
|
|
477
|
+
return new Promise<Record<string, unknown>>((resolve) => {
|
|
478
|
+
if (isFetchBaseQueryError(error) && isRecord(error.data)) {
|
|
479
|
+
if (error.status === HttpStatus.FormNotComplete) {
|
|
480
|
+
resolve(error.data);
|
|
481
|
+
} else if (error.status === HttpStatus.BadRequest) {
|
|
482
|
+
resolve({
|
|
483
|
+
...error.data,
|
|
484
|
+
status: error.status,
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
} else if (data) {
|
|
488
|
+
resolve({
|
|
489
|
+
data,
|
|
490
|
+
status: HttpStatus.Created,
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
resolve({});
|
|
495
|
+
});
|
|
496
|
+
})
|
|
497
|
+
.catch((error) => {
|
|
498
|
+
return new Promise<Record<string, object>>((resolve, reject) => {
|
|
499
|
+
if (error.status === HttpStatus.FormNotComplete) {
|
|
500
|
+
resolve(error.data);
|
|
501
|
+
}
|
|
502
|
+
reject(error);
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
return pydanticFormProvider;
|
|
508
|
+
}, [startForm]);
|
|
509
|
+
|
|
510
|
+
const config = useGetPydanticFormsConfig(getPydanticFormProvider, (props) => <Footer {...props} />);
|
|
511
|
+
|
|
512
|
+
const handleCancel = useCallback(() => {
|
|
513
|
+
const pfBasePath = PATH_METADATA_SCHEDULED_TASKS;
|
|
514
|
+
router.replace(pfBasePath);
|
|
515
|
+
}, [router]);
|
|
516
|
+
|
|
517
|
+
if (mutationState.isError) {
|
|
518
|
+
showToastMessage(ToastTypes.ERROR, '', 'Error while saving scheduled task');
|
|
519
|
+
console.error('Error saving scheduled task', mutationState);
|
|
520
|
+
return undefined;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return (
|
|
524
|
+
<PydanticForm
|
|
525
|
+
formKey="configure_schedule"
|
|
526
|
+
formId={generateFormId}
|
|
527
|
+
onSuccess={onSuccess}
|
|
528
|
+
onCancel={handleCancel}
|
|
529
|
+
config={config}
|
|
530
|
+
/>
|
|
531
|
+
);
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
export const WfoScheduleTaskFormPage = () => {
|
|
535
|
+
const { data } = useGetVersionsQuery();
|
|
536
|
+
const coreVersion = data?.version.applicationVersions[0].split(' ')[1] ?? '';
|
|
537
|
+
|
|
538
|
+
const isCompatible = compareVersions(coreVersion, '5.0.0a7') !== -1;
|
|
539
|
+
return isCompatible ? <WfoScheduleTaskFormPageBackend /> : <WfoScheduleTaskFormPageHardCoded />;
|
|
540
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SEARCH_QUERY_RESULTS_ENDPOINT } from '@/configuration';
|
|
2
|
+
import { BaseQueryTypes, orchestratorApi } from '@/rtk';
|
|
3
|
+
import { QueryResultsData } from '@/types';
|
|
4
|
+
|
|
5
|
+
const agentQueryResultsApi = orchestratorApi.injectEndpoints({
|
|
6
|
+
endpoints: (builder) => ({
|
|
7
|
+
getAgentQueryResults: builder.query<QueryResultsData, string>({
|
|
8
|
+
query: (queryId) => ({
|
|
9
|
+
url: `${SEARCH_QUERY_RESULTS_ENDPOINT}/${queryId}/results`,
|
|
10
|
+
method: 'GET',
|
|
11
|
+
}),
|
|
12
|
+
extraOptions: {
|
|
13
|
+
baseQueryType: BaseQueryTypes.fetch,
|
|
14
|
+
},
|
|
15
|
+
}),
|
|
16
|
+
}),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const { useGetAgentQueryResultsQuery } = agentQueryResultsApi;
|
|
@@ -2,7 +2,7 @@ import { BaseQueryTypes, orchestratorApi } from '@/rtk';
|
|
|
2
2
|
|
|
3
3
|
const PROCESS_ENDPOINT = 'processes';
|
|
4
4
|
const RESUME_ENDPOINT = 'resume';
|
|
5
|
-
const FORMS_ENDPOINT = '
|
|
5
|
+
const FORMS_ENDPOINT = 'forms/'; // It is still being used by example-wfo-ui
|
|
6
6
|
|
|
7
7
|
const formsApi = orchestratorApi.injectEndpoints({
|
|
8
8
|
endpoints: (build) => ({
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
GraphqlQueryVariables,
|
|
8
8
|
ScheduledTaskDefinition,
|
|
9
9
|
ScheduledTasksDefinitionsResult,
|
|
10
|
-
TaskType,
|
|
11
10
|
} from '@/types';
|
|
12
11
|
|
|
13
12
|
export const scheduledTasks = `
|
|
@@ -41,7 +40,7 @@ export type ScheduledTasksResponse = {
|
|
|
41
40
|
schedules: ScheduledTaskDefinition[];
|
|
42
41
|
} & BaseGraphQlResult;
|
|
43
42
|
|
|
44
|
-
/*
|
|
43
|
+
/*
|
|
45
44
|
https://apscheduler.readthedocs.io/en/3.x/modules/triggers/interval.html#module-apscheduler.triggers.interval
|
|
46
45
|
Possible parameters:
|
|
47
46
|
weeks (int) – number of weeks to wait
|
|
@@ -105,11 +104,13 @@ export type CronKwargs = {
|
|
|
105
104
|
type Kwargs = DateKwargs | InterValKwargs | CronKwargs;
|
|
106
105
|
|
|
107
106
|
export type ScheduledTaskPostPayload = {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
workflow_id: string;
|
|
108
|
+
workflow_name: string;
|
|
109
|
+
name: string;
|
|
110
|
+
trigger: string;
|
|
111
|
+
trigger_kwargs: Kwargs;
|
|
112
|
+
scheduled_type: string;
|
|
113
|
+
user_inputs: object[];
|
|
113
114
|
};
|
|
114
115
|
|
|
115
116
|
const scheduledTasksApi = orchestratorApi.injectEndpoints({
|
|
@@ -149,22 +150,13 @@ const scheduledTasksApi = orchestratorApi.injectEndpoints({
|
|
|
149
150
|
}),
|
|
150
151
|
createScheduledTask: builder.mutation<unknown, ScheduledTaskPostPayload>({
|
|
151
152
|
query: (payload) => {
|
|
152
|
-
const scheduleTaskPayload = {
|
|
153
|
-
scheduled_type: 'create',
|
|
154
|
-
name: payload.workflowDescription,
|
|
155
|
-
workflow_name: payload.workflowName,
|
|
156
|
-
workflow_id: payload.workflowId,
|
|
157
|
-
trigger: payload.type,
|
|
158
|
-
trigger_kwargs: payload.kwargs,
|
|
159
|
-
};
|
|
160
|
-
|
|
161
153
|
return {
|
|
162
154
|
url: METADATA_SCHEDULES_ENDPOINT,
|
|
163
155
|
method: 'POST',
|
|
164
156
|
headers: {
|
|
165
157
|
'Content-Type': 'application/json',
|
|
166
158
|
},
|
|
167
|
-
body:
|
|
159
|
+
body: payload,
|
|
168
160
|
};
|
|
169
161
|
},
|
|
170
162
|
extraOptions: {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { debounce } from 'lodash';
|
|
2
2
|
|
|
3
3
|
import { getWebSocket, orchestratorApi } from '@/rtk';
|
|
4
|
-
import
|
|
5
|
-
import { CacheTag, CacheTagType } from '@/types';
|
|
4
|
+
import { CacheTag } from '@/types';
|
|
6
5
|
|
|
7
6
|
const PING_INTERVAL_MS = 30000;
|
|
8
7
|
const NO_PONG_RECEIVED_TIMEOUT_MS = 35000;
|
|
@@ -29,32 +28,26 @@ enum MessageTypes {
|
|
|
29
28
|
* - It invalidates the cache entry with the tag received in the message event
|
|
30
29
|
* - WfoWebsocketStatusBadge contains logic that handles automatic reconnection and their circumstances
|
|
31
30
|
*/
|
|
31
|
+
|
|
32
32
|
const streamMessagesApi = orchestratorApi.injectEndpoints({
|
|
33
33
|
endpoints: (build) => ({
|
|
34
|
-
streamMessages: build.query<boolean,
|
|
34
|
+
streamMessages: build.query<boolean, string>({
|
|
35
35
|
queryFn: () => {
|
|
36
36
|
return { data: true };
|
|
37
37
|
},
|
|
38
|
-
async onCacheEntryAdded(
|
|
38
|
+
async onCacheEntryAdded(wsEndpoint, { cacheDataLoaded, cacheEntryRemoved, dispatch, updateCachedData }) {
|
|
39
39
|
const cleanUp = () => {
|
|
40
40
|
clearInterval(pingInterval);
|
|
41
41
|
updateCachedData(() => false);
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
const invalidateTag = (cacheTag: CacheTag) => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
dispatch(cacheInvalidationAction);
|
|
48
|
-
} else {
|
|
49
|
-
console.error(`Trying to invalidate a cache entry with an unknown tag: ${cacheTag.type}`);
|
|
50
|
-
}
|
|
45
|
+
const cacheInvalidationAction = orchestratorApi.util.invalidateTags([cacheTag]);
|
|
46
|
+
dispatch(cacheInvalidationAction);
|
|
51
47
|
};
|
|
52
48
|
|
|
53
49
|
await cacheDataLoaded;
|
|
54
50
|
let initialConnection = true;
|
|
55
|
-
const state = getState() as RootState;
|
|
56
|
-
const { orchestratorWebsocketUrl } = state.orchestratorConfig;
|
|
57
|
-
const validCacheTags = Object.values(CacheTagType);
|
|
58
51
|
|
|
59
52
|
const getDebounce = (delay: number) => {
|
|
60
53
|
return debounce(() => {
|
|
@@ -68,8 +61,7 @@ const streamMessagesApi = orchestratorApi.injectEndpoints({
|
|
|
68
61
|
const closeConnectionAfterFirstPing = getDebounce(INITIAL_CONNECTION_CHECK_INTERVAL_MS);
|
|
69
62
|
const debounceClosingConnection = getDebounce(NO_PONG_RECEIVED_TIMEOUT_MS);
|
|
70
63
|
|
|
71
|
-
|
|
72
|
-
const webSocket = await getWebSocket(orchestratorWebsocketUrl);
|
|
64
|
+
const webSocket = await getWebSocket(wsEndpoint);
|
|
73
65
|
|
|
74
66
|
const sendPing = () => {
|
|
75
67
|
if (webSocket.readyState === WebSocket.OPEN) {
|
|
@@ -82,9 +74,7 @@ const streamMessagesApi = orchestratorApi.injectEndpoints({
|
|
|
82
74
|
// run less frequently at the discretion of the browser causing the websocket to disconnect
|
|
83
75
|
// sometimes. WfoWebsocketStatusBadge contains logic to reconnect based on the pageVisibility api
|
|
84
76
|
// to handle that situation.
|
|
85
|
-
const pingInterval = setInterval(
|
|
86
|
-
sendPing();
|
|
87
|
-
}, PING_INTERVAL_MS);
|
|
77
|
+
const pingInterval = setInterval(sendPing, PING_INTERVAL_MS);
|
|
88
78
|
|
|
89
79
|
webSocket.onopen = () => {
|
|
90
80
|
// Check the connection right after it is established
|
|
@@ -106,6 +96,7 @@ const streamMessagesApi = orchestratorApi.injectEndpoints({
|
|
|
106
96
|
}
|
|
107
97
|
return;
|
|
108
98
|
}
|
|
99
|
+
|
|
109
100
|
const message = JSON.parse(data) as WebSocketMessage;
|
|
110
101
|
if (message.name === MessageTypes.invalidateCache) {
|
|
111
102
|
invalidateTag(message.value);
|
|
@@ -114,17 +105,13 @@ const streamMessagesApi = orchestratorApi.injectEndpoints({
|
|
|
114
105
|
}
|
|
115
106
|
});
|
|
116
107
|
|
|
117
|
-
webSocket.onerror = (event) =>
|
|
118
|
-
console.error('WebSocket error', event);
|
|
119
|
-
};
|
|
120
|
-
|
|
108
|
+
webSocket.onerror = (event) => console.error('WebSocket error', event);
|
|
121
109
|
webSocket.onclose = () => {
|
|
122
110
|
console.error('WebSocket closed');
|
|
123
111
|
cleanUp();
|
|
124
112
|
};
|
|
125
113
|
|
|
126
114
|
await cacheEntryRemoved;
|
|
127
|
-
|
|
128
115
|
webSocket.close();
|
|
129
116
|
},
|
|
130
117
|
}),
|
|
@@ -2,7 +2,7 @@ import { ReactElement, ReactNode } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { Slice, createSlice } from '@reduxjs/toolkit';
|
|
4
4
|
|
|
5
|
-
import { FieldValue, RenderableFieldValue, SubscriptionDetail } from '@/types';
|
|
5
|
+
import { FieldValue, RenderableFieldValue, Step, SubscriptionDetail } from '@/types';
|
|
6
6
|
|
|
7
7
|
export type ValueOverrideFunction = (
|
|
8
8
|
fieldValue: FieldValue | RenderableFieldValue,
|
|
@@ -19,6 +19,10 @@ export type OrchestratorComponentOverride = {
|
|
|
19
19
|
startPage?: {
|
|
20
20
|
summaryCardConfigurationOverride?: (defaultItems: ReactElement[]) => ReactElement[];
|
|
21
21
|
};
|
|
22
|
+
stepDetail?: {
|
|
23
|
+
stepHeader?: React.JSXElementConstructor<{ step?: Step }>;
|
|
24
|
+
stepBody?: React.JSXElementConstructor<{ step?: Step }>;
|
|
25
|
+
};
|
|
22
26
|
subscriptionDetail?: {
|
|
23
27
|
valueOverrides?: ValueOverrideConfiguration;
|
|
24
28
|
generalSectionConfigurationOverride?: (
|
package/src/types/search.ts
CHANGED
|
@@ -156,7 +156,7 @@ export type PathInfo = {
|
|
|
156
156
|
|
|
157
157
|
/** ---------- Agent visualization types ---------- */
|
|
158
158
|
|
|
159
|
-
export type
|
|
159
|
+
export type ResultRow = {
|
|
160
160
|
group_values: Record<string, string>;
|
|
161
161
|
aggregations: Record<string, number>;
|
|
162
162
|
};
|
|
@@ -172,11 +172,26 @@ export enum VisualizationType {
|
|
|
172
172
|
TABLE = 'table',
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
export type
|
|
176
|
-
results:
|
|
177
|
-
|
|
175
|
+
export type QueryResultsData = {
|
|
176
|
+
results: ResultRow[];
|
|
177
|
+
total_results: number;
|
|
178
178
|
metadata: SearchMetadata;
|
|
179
179
|
visualization_type: {
|
|
180
180
|
type: VisualizationType;
|
|
181
181
|
};
|
|
182
182
|
};
|
|
183
|
+
|
|
184
|
+
export type QueryArtifact = {
|
|
185
|
+
query_id: string;
|
|
186
|
+
total_results: number;
|
|
187
|
+
visualization_type: {
|
|
188
|
+
type: VisualizationType;
|
|
189
|
+
};
|
|
190
|
+
description: string;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
export type ExportArtifact = {
|
|
194
|
+
query_id: string;
|
|
195
|
+
download_url: string;
|
|
196
|
+
description: string;
|
|
197
|
+
};
|
|
@@ -48,4 +48,9 @@ describe('isOrchestratorUiVersionCompatible', () => {
|
|
|
48
48
|
expect(getOrchestratorCoreVersionIfNotCompatible('1.0.0', '2.10.0', TEST_VERSIONS)).toBe(null); // falls back to first MAPPED_VERSION
|
|
49
49
|
expect(getOrchestratorCoreVersionIfNotCompatible('1.0.0', '2.9.9', TEST_VERSIONS)).toBe('2.10.0');
|
|
50
50
|
});
|
|
51
|
+
|
|
52
|
+
test('handles prerelease orchestratorCoreVersion (e.g. 5.0.0a7)', () => {
|
|
53
|
+
expect(getOrchestratorCoreVersionIfNotCompatible('3.10.0', '5.0.0a7', TEST_VERSIONS)).toBe(null);
|
|
54
|
+
expect(getOrchestratorCoreVersionIfNotCompatible('3.10.0', '3.1.1a1', TEST_VERSIONS)).toBe('3.1.1');
|
|
55
|
+
});
|
|
51
56
|
});
|
|
@@ -1,33 +1,62 @@
|
|
|
1
1
|
import { MappedVersion } from '@/types';
|
|
2
2
|
|
|
3
|
-
const compareVersions = (
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
export const compareVersions = (v1: string, v2: string): number => {
|
|
4
|
+
const parse = (v: string) => {
|
|
5
|
+
const parts = v.split('.');
|
|
6
|
+
|
|
7
|
+
const major = parseInt(parts[0]);
|
|
8
|
+
const minor = parseInt(parts[1]);
|
|
9
|
+
|
|
10
|
+
const patchPart = parts[2] ?? '';
|
|
11
|
+
|
|
12
|
+
let patch = 0;
|
|
13
|
+
let suffix = '';
|
|
14
|
+
|
|
15
|
+
// extract numeric patch and suffix manually
|
|
16
|
+
for (let i = 0; i < patchPart.length; i++) {
|
|
17
|
+
const char = patchPart[i];
|
|
18
|
+
|
|
19
|
+
if (char >= '0' && char <= '9') {
|
|
20
|
+
patch = patch * 10 + Number(char);
|
|
21
|
+
} else {
|
|
22
|
+
suffix = patchPart.slice(i);
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
12
25
|
}
|
|
13
|
-
|
|
14
|
-
|
|
26
|
+
|
|
27
|
+
return { major, minor, patch, suffix };
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const a = parse(v1);
|
|
31
|
+
const b = parse(v2);
|
|
32
|
+
|
|
33
|
+
// compare numeric parts
|
|
34
|
+
if (a.major !== b.major) return a.major > b.major ? 1 : -1;
|
|
35
|
+
if (a.minor !== b.minor) return a.minor > b.minor ? 1 : -1;
|
|
36
|
+
if (a.patch !== b.patch) return a.patch > b.patch ? 1 : -1;
|
|
37
|
+
|
|
38
|
+
// compare suffix
|
|
39
|
+
if (a.suffix === b.suffix) return 0;
|
|
40
|
+
if (!a.suffix) return 1; // stable > pre-release
|
|
41
|
+
if (!b.suffix) return -1;
|
|
42
|
+
|
|
43
|
+
return a.suffix > b.suffix ? 1 : -1;
|
|
15
44
|
};
|
|
16
45
|
|
|
17
46
|
const findMinimumOrchestratorCoreVersion = (
|
|
18
47
|
orchestratorUiVersion: string,
|
|
19
48
|
versionMappings: MappedVersion[],
|
|
20
|
-
): string => {
|
|
21
|
-
|
|
22
|
-
|
|
49
|
+
): string | null => {
|
|
50
|
+
// sort mappings descending by UI version. This is done just in case the input versionMappings are not sorted
|
|
51
|
+
const sorted = [...versionMappings].sort((a, b) => compareVersions(b.orchestratorUiVersion, a.orchestratorUiVersion));
|
|
23
52
|
|
|
24
|
-
for (
|
|
25
|
-
if (
|
|
26
|
-
|
|
53
|
+
for (const mapping of sorted) {
|
|
54
|
+
if (compareVersions(orchestratorUiVersion, mapping.orchestratorUiVersion) >= 0) {
|
|
55
|
+
return mapping.minimumOrchestratorCoreVersion;
|
|
27
56
|
}
|
|
28
57
|
}
|
|
29
58
|
|
|
30
|
-
return
|
|
59
|
+
return sorted[sorted.length - 1]?.minimumOrchestratorCoreVersion ?? null;
|
|
31
60
|
};
|
|
32
61
|
|
|
33
62
|
export const getOrchestratorCoreVersionIfNotCompatible = (
|
|
@@ -35,10 +64,13 @@ export const getOrchestratorCoreVersionIfNotCompatible = (
|
|
|
35
64
|
orchestratorCoreVersion: string,
|
|
36
65
|
versionMappings: MappedVersion[],
|
|
37
66
|
): string | null => {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
return
|
|
67
|
+
const minimumVersion = findMinimumOrchestratorCoreVersion(orchestratorUiVersion, versionMappings);
|
|
68
|
+
|
|
69
|
+
if (!minimumVersion) {
|
|
70
|
+
return null;
|
|
42
71
|
}
|
|
43
|
-
|
|
72
|
+
|
|
73
|
+
const isCompatible = compareVersions(orchestratorCoreVersion, minimumVersion) !== -1;
|
|
74
|
+
|
|
75
|
+
return isCompatible ? null : minimumVersion;
|
|
44
76
|
};
|