@orchestrator-ui/orchestrator-ui-components 0.11.1 → 0.13.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 +4 -4
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-test.log +13 -12
- package/CHANGELOG.md +17 -0
- package/dist/index.d.ts +36 -21
- package/dist/index.js +164 -142
- package/package.json +1 -1
- package/src/api/index.ts +12 -18
- package/src/components/WfoForms/CreateForm.tsx +1 -1
- package/src/components/WfoForms/index.ts +1 -1
- package/src/graphqlQueries/index.ts +0 -1
- package/src/hooks/useQueryWithFetch.ts +7 -2
- package/src/pages/metadata/WfoWorkflowsPage.tsx +35 -30
- package/src/pages/processes/WfoProcessDetailPage.tsx +7 -11
- package/src/pages/startPage/WfoStartPage.tsx +1 -1
- package/src/rtk/endpoints/processDetail.ts +66 -0
- package/src/rtk/endpoints/processList.ts +1 -1
- package/src/types/index.ts +1 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/onlyUnique.spec.ts +19 -0
- package/src/utils/onlyUnique.ts +3 -0
- package/src/utils/string.spec.ts +37 -1
- package/src/utils/strings.ts +9 -0
- package/src/graphqlQueries/processDetailQuery.ts +0 -46
package/src/api/index.ts
CHANGED
|
@@ -24,9 +24,9 @@ import { ProductDefinition } from '@/types';
|
|
|
24
24
|
|
|
25
25
|
import { getAxiosInstance } from './axios';
|
|
26
26
|
|
|
27
|
-
const CIM_FORMS_ENDPOINT = 'surf/cim/forms/';
|
|
28
27
|
const PROCESS_ENDPOINT = 'processes/';
|
|
29
28
|
const PRODUCTS_ENDPOINT = 'products/';
|
|
29
|
+
const FORMS_ENDPOINT = 'surf/forms/';
|
|
30
30
|
|
|
31
31
|
export class BaseApiClient {
|
|
32
32
|
private _axiosInstance: AxiosInstance;
|
|
@@ -105,14 +105,20 @@ export class BaseApiClient {
|
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
export class ApiClient extends BaseApiClient {
|
|
109
|
+
startForm = (
|
|
110
110
|
formKey: string,
|
|
111
111
|
userInputs: object[],
|
|
112
|
-
) =>
|
|
113
|
-
|
|
112
|
+
): Promise<{ id: string }> => {
|
|
113
|
+
return this.postPutJson(
|
|
114
|
+
`${FORMS_ENDPOINT}${formKey}`,
|
|
115
|
+
userInputs,
|
|
116
|
+
'post',
|
|
117
|
+
false,
|
|
118
|
+
true,
|
|
119
|
+
);
|
|
120
|
+
};
|
|
114
121
|
|
|
115
|
-
export class ApiClient extends ApiClientInterface {
|
|
116
122
|
startProcess = (
|
|
117
123
|
workflowName: string,
|
|
118
124
|
processInput: object,
|
|
@@ -142,18 +148,6 @@ export class ApiClient extends ApiClientInterface {
|
|
|
142
148
|
productById = (productId: string): Promise<ProductDefinition> => {
|
|
143
149
|
return this.fetchJson(`${PRODUCTS_ENDPOINT}${productId}`);
|
|
144
150
|
};
|
|
145
|
-
cimStartForm = (
|
|
146
|
-
formKey: string,
|
|
147
|
-
userInputs: object[],
|
|
148
|
-
): Promise<{ id: string }> => {
|
|
149
|
-
return this.postPutJson(
|
|
150
|
-
`${CIM_FORMS_ENDPOINT}${formKey}`,
|
|
151
|
-
userInputs,
|
|
152
|
-
'post',
|
|
153
|
-
false,
|
|
154
|
-
true,
|
|
155
|
-
);
|
|
156
|
-
};
|
|
157
151
|
prefix_filters = (): Promise<IpPrefix[]> => {
|
|
158
152
|
return this.fetchJson('surf/ipam/prefix_filters');
|
|
159
153
|
};
|
|
@@ -35,7 +35,7 @@ export function CreateForm(props: IProps) {
|
|
|
35
35
|
|
|
36
36
|
const submit = useCallback(
|
|
37
37
|
(userInputs: object[]) => {
|
|
38
|
-
return apiClient.
|
|
38
|
+
return apiClient.startForm(formKey, userInputs).then((form) => {
|
|
39
39
|
handleSubmit(form);
|
|
40
40
|
});
|
|
41
41
|
},
|
|
@@ -3,7 +3,6 @@ export * from './productBlocksQuery';
|
|
|
3
3
|
export * from './productsQuery';
|
|
4
4
|
export * from './resourceTypesQuery';
|
|
5
5
|
export * from './subscriptionDetailQuery';
|
|
6
|
-
export * from './processDetailQuery';
|
|
7
6
|
export * from './subscriptionsDropdownOptionsQuery';
|
|
8
7
|
export * from './processStepsQuery';
|
|
9
8
|
export * from './processListQuery';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useQuery } from 'react-query';
|
|
1
|
+
import { UseQueryOptions, useQuery } from 'react-query';
|
|
2
2
|
|
|
3
3
|
import { Variables } from 'graphql-request/build/cjs/types';
|
|
4
4
|
import { signOut } from 'next-auth/react';
|
|
@@ -9,6 +9,7 @@ export const useQueryWithFetch = <T, V extends Variables>(
|
|
|
9
9
|
url: string,
|
|
10
10
|
queryVars: V,
|
|
11
11
|
queryKey: string,
|
|
12
|
+
options?: UseQueryOptions<T, unknown, T, [string, ...unknown[]]>,
|
|
12
13
|
) => {
|
|
13
14
|
const { session } = useWfoSession();
|
|
14
15
|
const requestHeaders = {
|
|
@@ -29,5 +30,9 @@ export const useQueryWithFetch = <T, V extends Variables>(
|
|
|
29
30
|
}
|
|
30
31
|
return (await response.json()) as T;
|
|
31
32
|
};
|
|
32
|
-
return useQuery(
|
|
33
|
+
return useQuery(
|
|
34
|
+
[queryKey, ...Object.values(queryVars)],
|
|
35
|
+
fetchData,
|
|
36
|
+
options,
|
|
37
|
+
);
|
|
33
38
|
};
|
|
@@ -5,6 +5,21 @@ import { useTranslations } from 'next-intl';
|
|
|
5
5
|
import { EuiBadgeGroup } from '@elastic/eui';
|
|
6
6
|
import type { Pagination } from '@elastic/eui/src/components';
|
|
7
7
|
|
|
8
|
+
import {
|
|
9
|
+
useDataDisplayParams,
|
|
10
|
+
useQueryWithGraphql,
|
|
11
|
+
useQueryWithGraphqlLazy,
|
|
12
|
+
useShowToastMessage,
|
|
13
|
+
useStoredTableConfig,
|
|
14
|
+
} from '@/hooks';
|
|
15
|
+
import type { GraphqlQueryVariables, WorkflowDefinition } from '@/types';
|
|
16
|
+
import { BadgeType, SortOrder } from '@/types';
|
|
17
|
+
import {
|
|
18
|
+
getQueryVariablesForExport,
|
|
19
|
+
onlyUnique,
|
|
20
|
+
parseDateToLocaleDateTimeString,
|
|
21
|
+
parseIsoString,
|
|
22
|
+
} from '@/utils';
|
|
8
23
|
import {
|
|
9
24
|
csvDownloadHandler,
|
|
10
25
|
getCsvFileNameWithDate,
|
|
@@ -28,20 +43,6 @@ import { WfoWorkflowTargetBadge } from '../../components/WfoBadges/WfoWorkflowTa
|
|
|
28
43
|
import { WfoDateTime } from '../../components/WfoDateTime/WfoDateTime';
|
|
29
44
|
import { mapSortableAndFilterableValuesToTableColumnConfig } from '../../components/WfoTable/utils/mapSortableAndFilterableValuesToTableColumnConfig';
|
|
30
45
|
import { GET_WORKFLOWS_GRAPHQL_QUERY } from '../../graphqlQueries/workflows/workflowsQuery';
|
|
31
|
-
import {
|
|
32
|
-
useDataDisplayParams,
|
|
33
|
-
useQueryWithGraphql,
|
|
34
|
-
useQueryWithGraphqlLazy,
|
|
35
|
-
useShowToastMessage,
|
|
36
|
-
useStoredTableConfig,
|
|
37
|
-
} from '../../hooks';
|
|
38
|
-
import type { GraphqlQueryVariables, WorkflowDefinition } from '../../types';
|
|
39
|
-
import { BadgeType, SortOrder } from '../../types';
|
|
40
|
-
import {
|
|
41
|
-
getQueryVariablesForExport,
|
|
42
|
-
parseDateToLocaleDateTimeString,
|
|
43
|
-
parseIsoString,
|
|
44
|
-
} from '../../utils';
|
|
45
46
|
import { WfoMetadataPageLayout } from './WfoMetadataPageLayout';
|
|
46
47
|
import {
|
|
47
48
|
graphQlWorkflowListMapper,
|
|
@@ -115,26 +116,30 @@ export const WfoWorkflowsPage = () => {
|
|
|
115
116
|
name: t('productTags'),
|
|
116
117
|
render: (productTags) => (
|
|
117
118
|
<>
|
|
118
|
-
{productTags
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
119
|
+
{productTags
|
|
120
|
+
?.filter(onlyUnique)
|
|
121
|
+
.map((productTag, index) => (
|
|
122
|
+
<WfoProductBlockBadge
|
|
123
|
+
key={index}
|
|
124
|
+
badgeType={BadgeType.PRODUCT_TAG}
|
|
125
|
+
>
|
|
126
|
+
{productTag}
|
|
127
|
+
</WfoProductBlockBadge>
|
|
128
|
+
))}
|
|
126
129
|
</>
|
|
127
130
|
),
|
|
128
131
|
renderDetails: (productTags) => (
|
|
129
132
|
<EuiBadgeGroup gutterSize="s">
|
|
130
|
-
{productTags
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
133
|
+
{productTags
|
|
134
|
+
?.filter(onlyUnique)
|
|
135
|
+
.map((productTag, index) => (
|
|
136
|
+
<WfoProductBlockBadge
|
|
137
|
+
key={index}
|
|
138
|
+
badgeType={BadgeType.PRODUCT_TAG}
|
|
139
|
+
>
|
|
140
|
+
{productTag}
|
|
141
|
+
</WfoProductBlockBadge>
|
|
142
|
+
))}
|
|
138
143
|
</EuiBadgeGroup>
|
|
139
144
|
),
|
|
140
145
|
},
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import { TimelineItem, WfoError, WfoLoading } from '@/components';
|
|
4
|
+
import { useGetProcessDetailQuery } from '@/rtk/endpoints/processDetail';
|
|
4
5
|
|
|
5
6
|
import {
|
|
6
7
|
WfoStepListRef,
|
|
7
8
|
WfoWorkflowStepList,
|
|
8
9
|
} from '../../components/WfoWorkflowSteps';
|
|
9
|
-
import { GET_PROCESS_DETAIL_GRAPHQL_QUERY } from '../../graphqlQueries';
|
|
10
|
-
import { useQueryWithGraphql } from '../../hooks';
|
|
11
10
|
import {
|
|
12
11
|
ProcessDetail,
|
|
13
12
|
ProcessDoneStatuses,
|
|
@@ -39,13 +38,10 @@ export const WfoProcessDetailPage = ({
|
|
|
39
38
|
const stepListRef = useRef<WfoStepListRef>(null);
|
|
40
39
|
const [fetchInterval, setFetchInterval] = useState<number | undefined>();
|
|
41
40
|
const [process, setProcess] = useState<ProcessDetail | undefined>();
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
{
|
|
45
|
-
|
|
46
|
-
},
|
|
47
|
-
'processDetail',
|
|
48
|
-
{ refetchInterval: fetchInterval, refetchOnWindowFocus: false },
|
|
41
|
+
|
|
42
|
+
const { data, isLoading, isError } = useGetProcessDetailQuery(
|
|
43
|
+
{ processId },
|
|
44
|
+
{ pollingInterval: fetchInterval },
|
|
49
45
|
);
|
|
50
46
|
|
|
51
47
|
if (isError) {
|
|
@@ -55,7 +51,7 @@ export const WfoProcessDetailPage = ({
|
|
|
55
51
|
}
|
|
56
52
|
|
|
57
53
|
useEffect(() => {
|
|
58
|
-
const process = data?.processes
|
|
54
|
+
const process = data?.processes[0];
|
|
59
55
|
// We need to cast here because the backend might return the string in upperCase and
|
|
60
56
|
// toLowerCase() will converts the value type to string
|
|
61
57
|
const lastStatus =
|
|
@@ -71,7 +67,7 @@ export const WfoProcessDetailPage = ({
|
|
|
71
67
|
}, [data, processDetailRefetchInterval]);
|
|
72
68
|
|
|
73
69
|
useEffect(() => {
|
|
74
|
-
const fetchedProcessDetails = data?.processes
|
|
70
|
+
const fetchedProcessDetails = data?.processes[0];
|
|
75
71
|
|
|
76
72
|
if (!process) {
|
|
77
73
|
setProcess(fetchedProcessDetails);
|
|
@@ -107,7 +107,7 @@ export const WfoStartPage = () => {
|
|
|
107
107
|
) ?? [],
|
|
108
108
|
button: {
|
|
109
109
|
name: t('outOfSyncSubscriptions.buttonText'),
|
|
110
|
-
url: `${PATH_SUBSCRIPTIONS}?activeTab=ALL&sortBy=field-startDate_order-ASC&queryString=status%3A%
|
|
110
|
+
url: `${PATH_SUBSCRIPTIONS}?activeTab=ALL&sortBy=field-startDate_order-ASC&queryString=status%3A%28provisioning%7Cactive%29+insync%3Afalse`,
|
|
111
111
|
},
|
|
112
112
|
isLoading: outOfSyncsubscriptionsSummaryIsFetching,
|
|
113
113
|
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { orchestratorApi } from '@/rtk';
|
|
2
|
+
import { ProcessDetail, ProcessesDetailResult } from '@/types';
|
|
3
|
+
|
|
4
|
+
export const processDetailQuery = `query ProcessDetail($processId: String!) {
|
|
5
|
+
processes(filterBy: { value: $processId, field: "processId" }) {
|
|
6
|
+
page {
|
|
7
|
+
processId
|
|
8
|
+
lastStatus
|
|
9
|
+
createdBy
|
|
10
|
+
startedAt
|
|
11
|
+
lastModifiedAt
|
|
12
|
+
lastStep
|
|
13
|
+
workflowName
|
|
14
|
+
isTask
|
|
15
|
+
form
|
|
16
|
+
steps {
|
|
17
|
+
name
|
|
18
|
+
status
|
|
19
|
+
stepId
|
|
20
|
+
executed
|
|
21
|
+
stateDelta
|
|
22
|
+
}
|
|
23
|
+
customer {
|
|
24
|
+
fullname
|
|
25
|
+
}
|
|
26
|
+
subscriptions {
|
|
27
|
+
page {
|
|
28
|
+
product {
|
|
29
|
+
name
|
|
30
|
+
}
|
|
31
|
+
description
|
|
32
|
+
subscriptionId
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}`;
|
|
38
|
+
|
|
39
|
+
export type ProcessDetailResponse = {
|
|
40
|
+
processes: ProcessDetail[];
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const processDetailApi = orchestratorApi.injectEndpoints({
|
|
44
|
+
endpoints: (builder) => ({
|
|
45
|
+
getProcessDetail: builder.query<
|
|
46
|
+
ProcessDetailResponse,
|
|
47
|
+
{ processId: string }
|
|
48
|
+
>({
|
|
49
|
+
query: (variables) => ({
|
|
50
|
+
document: processDetailQuery,
|
|
51
|
+
variables,
|
|
52
|
+
}),
|
|
53
|
+
transformResponse: (
|
|
54
|
+
response: ProcessesDetailResult,
|
|
55
|
+
): ProcessDetailResponse => {
|
|
56
|
+
const processes = response.processes.page || [];
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
processes,
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
}),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export const { useGetProcessDetailQuery } = processDetailApi;
|
|
@@ -75,7 +75,7 @@ const processApi = orchestratorApi.injectEndpoints({
|
|
|
75
75
|
transformResponse: (
|
|
76
76
|
response: ProcessListResult,
|
|
77
77
|
): ProcessListResponse => {
|
|
78
|
-
const processes = response
|
|
78
|
+
const processes = response.processes.page || [];
|
|
79
79
|
|
|
80
80
|
return {
|
|
81
81
|
processes,
|
package/src/types/index.ts
CHANGED
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { onlyUnique } from './onlyUnique';
|
|
2
|
+
|
|
3
|
+
describe('onlyUnique()', () => {
|
|
4
|
+
it('returns array as array with only unique values', () => {
|
|
5
|
+
const test1 = ['SP', 'SP', 'FW', 'SP', 'FW', 'LP'];
|
|
6
|
+
const result = test1?.filter(onlyUnique);
|
|
7
|
+
expect(result).toEqual(['SP', 'FW', 'LP']);
|
|
8
|
+
});
|
|
9
|
+
it('returns array as array with only single value', () => {
|
|
10
|
+
const test2 = ['SP', 'SP', 'SP'];
|
|
11
|
+
const result = test2?.filter(onlyUnique);
|
|
12
|
+
expect(result).toEqual(['SP']);
|
|
13
|
+
});
|
|
14
|
+
it('returns array as empty array', () => {
|
|
15
|
+
const test3 = [''];
|
|
16
|
+
const result = test3?.filter(onlyUnique);
|
|
17
|
+
expect(result).toEqual(['']);
|
|
18
|
+
});
|
|
19
|
+
});
|
package/src/utils/string.spec.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
camelToHuman,
|
|
3
|
+
removeSuffix,
|
|
4
|
+
snakeToHuman,
|
|
5
|
+
snakeToKebab,
|
|
6
|
+
upperCaseFirstChar,
|
|
7
|
+
} from './strings';
|
|
2
8
|
|
|
3
9
|
describe('upperCaseFirstChar()', () => {
|
|
4
10
|
it("Doesn't crash on an empty string but returns empty string", () => {
|
|
@@ -68,3 +74,33 @@ describe('camelToHuman()', () => {
|
|
|
68
74
|
expect(result).toEqual('A Quick Brown Fox');
|
|
69
75
|
});
|
|
70
76
|
});
|
|
77
|
+
|
|
78
|
+
describe('snakeToHuman()', () => {
|
|
79
|
+
it('Returns an empty string when input is an empty string', () => {
|
|
80
|
+
const result = snakeToHuman('');
|
|
81
|
+
expect(result).toEqual('');
|
|
82
|
+
});
|
|
83
|
+
it('Returns two words from a single underscore snake case word', () => {
|
|
84
|
+
const result = snakeToHuman('hello_world');
|
|
85
|
+
expect(result).toEqual('hello world');
|
|
86
|
+
});
|
|
87
|
+
it('Returns multiple words from a multiple underscore snake case word', () => {
|
|
88
|
+
const result = snakeToHuman('quick_brown_fox');
|
|
89
|
+
expect(result).toEqual('quick brown fox');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('snakeToKebab()', () => {
|
|
94
|
+
it('Returns an empty string when input is an empty string', () => {
|
|
95
|
+
const result = snakeToKebab('');
|
|
96
|
+
expect(result).toEqual('');
|
|
97
|
+
});
|
|
98
|
+
it('Returns kebab case word from a single underscore snake case word', () => {
|
|
99
|
+
const result = snakeToKebab('hello_world');
|
|
100
|
+
expect(result).toEqual('hello-world');
|
|
101
|
+
});
|
|
102
|
+
it('Returns kebab case word from a multiple underscore snake case word', () => {
|
|
103
|
+
const result = snakeToKebab('quick_brown_fox');
|
|
104
|
+
expect(result).toEqual('quick-brown-fox');
|
|
105
|
+
});
|
|
106
|
+
});
|
package/src/utils/strings.ts
CHANGED
|
@@ -13,3 +13,12 @@ export const camelToHuman = (value: string): string => {
|
|
|
13
13
|
const result = value.replace(/([A-Z])/g, ' $1').trimStart();
|
|
14
14
|
return result.charAt(0).toUpperCase() + result.slice(1);
|
|
15
15
|
};
|
|
16
|
+
|
|
17
|
+
export const snakeToHuman = (value: string): string => {
|
|
18
|
+
const result = value.replace(/_/g, ' ');
|
|
19
|
+
return result.charAt(0) + result.slice(1);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const snakeToKebab = (value: string): string => {
|
|
23
|
+
return value.replace(/_/g, '-');
|
|
24
|
+
};
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { parse } from 'graphql';
|
|
2
|
-
import { gql } from 'graphql-request';
|
|
3
|
-
|
|
4
|
-
import { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
5
|
-
|
|
6
|
-
import { ProcessesDetailResult } from '../types';
|
|
7
|
-
|
|
8
|
-
export const GET_PROCESS_DETAIL_GRAPHQL_QUERY: TypedDocumentNode<
|
|
9
|
-
ProcessesDetailResult,
|
|
10
|
-
{ processId: string }
|
|
11
|
-
> = parse(gql`
|
|
12
|
-
query ProcessDetail($processId: String!) {
|
|
13
|
-
processes(filterBy: { value: $processId, field: "processId" }) {
|
|
14
|
-
page {
|
|
15
|
-
processId
|
|
16
|
-
lastStatus
|
|
17
|
-
createdBy
|
|
18
|
-
startedAt
|
|
19
|
-
lastModifiedAt
|
|
20
|
-
lastStep
|
|
21
|
-
workflowName
|
|
22
|
-
isTask
|
|
23
|
-
form
|
|
24
|
-
steps {
|
|
25
|
-
name
|
|
26
|
-
status
|
|
27
|
-
stepId
|
|
28
|
-
executed
|
|
29
|
-
stateDelta
|
|
30
|
-
}
|
|
31
|
-
customer {
|
|
32
|
-
fullname
|
|
33
|
-
}
|
|
34
|
-
subscriptions {
|
|
35
|
-
page {
|
|
36
|
-
product {
|
|
37
|
-
name
|
|
38
|
-
}
|
|
39
|
-
description
|
|
40
|
-
subscriptionId
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
`);
|