@orchestrator-ui/orchestrator-ui-components 0.11.0 → 0.12.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchestrator-ui/orchestrator-ui-components",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "test": "jest",
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
- abstract class ApiClientInterface extends BaseApiClient {
109
- abstract cimStartForm: (
108
+ export class ApiClient extends BaseApiClient {
109
+ startForm = (
110
110
  formKey: string,
111
111
  userInputs: object[],
112
- ) => Promise<object>;
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
  };
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+
3
+ import { useTranslations } from 'next-intl';
4
+
5
+ export const WfoPageUnauthorized = () => {
6
+ const t = useTranslations('common');
7
+ return <p>{t('unauthorizedPage')}</p>;
8
+ };
@@ -0,0 +1,19 @@
1
+ import React, { FC } from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ import { WfoPageUnauthorized } from '@/components/WfoAuth/WfoPageUnauthorized';
5
+ import { usePolicy } from '@/hooks';
6
+
7
+ export type WfoPolicyRenderFallbackProps = {
8
+ resource?: string;
9
+ children: ReactNode;
10
+ };
11
+
12
+ export const WfoPolicyRenderPageFallback: FC<WfoPolicyRenderFallbackProps> = ({
13
+ resource,
14
+ children,
15
+ }) => {
16
+ const { isAllowed } = usePolicy();
17
+
18
+ return isAllowed(resource) ? <>{children}</> : <WfoPageUnauthorized />;
19
+ };
@@ -1,2 +1,4 @@
1
+ export * from './WfoPolicyRenderPageFallback';
1
2
  export * from './WfoAuth';
2
3
  export * from './WfoIsAllowedToRender';
4
+ export * from './WfoPageUnauthorized';
@@ -35,7 +35,7 @@ export function CreateForm(props: IProps) {
35
35
 
36
36
  const submit = useCallback(
37
37
  (userInputs: object[]) => {
38
- return apiClient.cimStartForm(formKey, userInputs).then((form) => {
38
+ return apiClient.startForm(formKey, userInputs).then((form) => {
39
39
  handleSubmit(form);
40
40
  });
41
41
  },
@@ -1,5 +1,5 @@
1
- export * from './CreateForm';
2
1
  export * from './AutoFields';
3
2
  export * from './UserInputForm';
4
3
  export * from './UserInputFormWizard';
5
4
  export * from './formFields';
5
+ export * from './CreateForm';
@@ -6,6 +6,10 @@ import { useRouter } from 'next/router';
6
6
  import { EuiSideNav, EuiSpacer } from '@elastic/eui';
7
7
  import { EuiSideNavItemType } from '@elastic/eui/src/components/side_nav/side_nav_types';
8
8
 
9
+ import { WfoIsAllowedToRender } from '@/components';
10
+ import { PolicyResource } from '@/configuration/policy-resources';
11
+ import { usePolicy } from '@/hooks';
12
+
9
13
  import {
10
14
  PATH_METADATA,
11
15
  PATH_METADATA_PRODUCTS,
@@ -21,6 +25,17 @@ import {
21
25
  import { WfoCopyright } from './WfoCopyright';
22
26
  import { WfoStartCreateWorkflowButtonComboBox } from './WfoStartCreateWorkflowButtonComboBox';
23
27
 
28
+ export const renderEmptyElementWhenNotAllowedByPolicy = (isAllowed: boolean) =>
29
+ isAllowed ? undefined : () => <></>;
30
+
31
+ export const urlPolicyMap = new Map<string, PolicyResource>([
32
+ [PATH_WORKFLOWS, PolicyResource.NAVIGATION_WORKFLOWS],
33
+ [PATH_SUBSCRIPTIONS, PolicyResource.NAVIGATION_SUBSCRIPTIONS],
34
+ [PATH_METADATA, PolicyResource.NAVIGATION_METADATA],
35
+ [PATH_TASKS, PolicyResource.NAVIGATION_TASKS],
36
+ [PATH_SETTINGS, PolicyResource.NAVIGATION_SETTINGS],
37
+ ]);
38
+
24
39
  export type WfoSidebarProps = {
25
40
  overrideMenuItems?: (
26
41
  defaultMenuItems: EuiSideNavItemType<object>[],
@@ -31,6 +46,7 @@ export const WfoSidebar: FC<WfoSidebarProps> = ({ overrideMenuItems }) => {
31
46
  const t = useTranslations('main');
32
47
  const router = useRouter();
33
48
  const [isSideNavOpenOnMobile, setIsSideNavOpenOnMobile] = useState(false);
49
+ const { isAllowed } = usePolicy();
34
50
 
35
51
  const toggleMobile = () => {
36
52
  setIsSideNavOpenOnMobile((openState) => !openState);
@@ -69,6 +85,7 @@ export const WfoSidebar: FC<WfoSidebarProps> = ({ overrideMenuItems }) => {
69
85
  {
70
86
  name: t('metadata'),
71
87
  id: '5',
88
+ href: PATH_METADATA,
72
89
  onClick: () => {
73
90
  router.push(PATH_METADATA);
74
91
  },
@@ -135,11 +152,27 @@ export const WfoSidebar: FC<WfoSidebarProps> = ({ overrideMenuItems }) => {
135
152
  },
136
153
  ];
137
154
 
155
+ const defaultMenuItemsFilteredByPolicy = defaultMenuItems.filter(
156
+ ({ href }) => {
157
+ if (!href) {
158
+ return true;
159
+ }
160
+
161
+ const policyResource = urlPolicyMap.get(href);
162
+
163
+ return policyResource ? isAllowed(policyResource) : true;
164
+ },
165
+ );
166
+
138
167
  const defaultMenu: EuiSideNavItemType<object>[] = [
139
168
  {
140
169
  renderItem: () => (
141
170
  <>
142
- <WfoStartCreateWorkflowButtonComboBox />
171
+ <WfoIsAllowedToRender
172
+ resource={PolicyResource.SUBSCRIPTION_CREATE}
173
+ >
174
+ <WfoStartCreateWorkflowButtonComboBox />
175
+ </WfoIsAllowedToRender>
143
176
  <EuiSpacer size="m" />
144
177
  <WfoCopyright />
145
178
  </>
@@ -147,8 +180,8 @@ export const WfoSidebar: FC<WfoSidebarProps> = ({ overrideMenuItems }) => {
147
180
  name: 'Menu',
148
181
  id: '1',
149
182
  items: overrideMenuItems
150
- ? overrideMenuItems(defaultMenuItems)
151
- : defaultMenuItems,
183
+ ? overrideMenuItems(defaultMenuItemsFilteredByPolicy)
184
+ : defaultMenuItemsFilteredByPolicy,
152
185
  },
153
186
  ];
154
187
 
@@ -16,10 +16,12 @@ import {
16
16
  import { ReactJSXElement } from '@emotion/react/types/jsx-namespace';
17
17
 
18
18
  import { PATH_START_NEW_TASK, PATH_START_NEW_WORKFLOW } from '@/components';
19
+ import { PolicyResource } from '@/configuration/policy-resources';
19
20
  import {
20
21
  SubscriptionAction,
21
22
  useCheckEngineStatus,
22
23
  useOrchestratorTheme,
24
+ usePolicy,
23
25
  useSubscriptionActions,
24
26
  } from '@/hooks';
25
27
  import { WfoXCircleFill } from '@/icons';
@@ -60,6 +62,7 @@ export const WfoSubscriptionActions: FC<WfoSubscriptionActionsProps> = ({
60
62
  const { data: subscriptionActions } =
61
63
  useSubscriptionActions(subscriptionId);
62
64
  const { isEngineRunningNow } = useCheckEngineStatus();
65
+ const { isAllowed } = usePolicy();
63
66
 
64
67
  const onButtonClick = () => {
65
68
  setPopover(!isPopoverOpen);
@@ -193,50 +196,68 @@ export const WfoSubscriptionActions: FC<WfoSubscriptionActionsProps> = ({
193
196
  >
194
197
  <EuiContextMenuPanel>
195
198
  <EuiPanel color="transparent" paddingSize="s">
196
- {subscriptionActions && subscriptionActions.modify && (
197
- <>
198
- <MenuBlock title={t('modify')}></MenuBlock>
199
- {subscriptionActions.modify.map((action, index) => (
200
- <MenuItem
201
- key={`m_${index}`}
202
- action={action}
203
- index={index}
204
- target={WorkflowTarget.MODIFY}
205
- />
206
- ))}
207
- </>
208
- )}
209
-
210
- {subscriptionActions && subscriptionActions.system && (
211
- <>
212
- <MenuBlock title={t('tasks')}></MenuBlock>
213
- {subscriptionActions.system.map((action, index) => (
214
- <MenuItem
215
- key={`s_${index}`}
216
- action={action}
217
- index={index}
218
- target={WorkflowTarget.SYSTEM}
219
- isTask={true}
220
- />
221
- ))}
222
- </>
223
- )}
224
-
225
- {subscriptionActions && subscriptionActions.terminate && (
226
- <>
227
- <MenuBlock title={t('terminate')}></MenuBlock>
228
- {subscriptionActions.terminate.map(
229
- (action, index) => (
230
- <MenuItem
231
- key={`t_${index}`}
232
- action={action}
233
- index={index}
234
- target={WorkflowTarget.TERMINATE}
235
- />
236
- ),
237
- )}
238
- </>
239
- )}
199
+ {subscriptionActions &&
200
+ isAllowed(
201
+ PolicyResource.SUBSCRIPTION_MODIFY + subscriptionId,
202
+ ) &&
203
+ subscriptionActions.modify && (
204
+ <>
205
+ <MenuBlock title={t('modify')}></MenuBlock>
206
+ {subscriptionActions.modify.map(
207
+ (action, index) => (
208
+ <MenuItem
209
+ key={`m_${index}`}
210
+ action={action}
211
+ index={index}
212
+ target={WorkflowTarget.MODIFY}
213
+ />
214
+ ),
215
+ )}
216
+ </>
217
+ )}
218
+
219
+ {subscriptionActions &&
220
+ isAllowed(
221
+ PolicyResource.SUBSCRIPTION_VALIDATE +
222
+ subscriptionId,
223
+ ) &&
224
+ subscriptionActions.system && (
225
+ <>
226
+ <MenuBlock title={t('tasks')}></MenuBlock>
227
+ {subscriptionActions.system.map(
228
+ (action, index) => (
229
+ <MenuItem
230
+ key={`s_${index}`}
231
+ action={action}
232
+ index={index}
233
+ target={WorkflowTarget.SYSTEM}
234
+ isTask={true}
235
+ />
236
+ ),
237
+ )}
238
+ </>
239
+ )}
240
+
241
+ {subscriptionActions &&
242
+ isAllowed(
243
+ PolicyResource.SUBSCRIPTION_TERMINATE +
244
+ subscriptionId,
245
+ ) &&
246
+ subscriptionActions.terminate && (
247
+ <>
248
+ <MenuBlock title={t('terminate')}></MenuBlock>
249
+ {subscriptionActions.terminate.map(
250
+ (action, index) => (
251
+ <MenuItem
252
+ key={`t_${index}`}
253
+ action={action}
254
+ index={index}
255
+ target={WorkflowTarget.TERMINATE}
256
+ />
257
+ ),
258
+ )}
259
+ </>
260
+ )}
240
261
  </EuiPanel>
241
262
  </EuiContextMenuPanel>
242
263
  </EuiPopover>
@@ -0,0 +1,2 @@
1
+ export * from './constants';
2
+ export * from './policy-resources';
@@ -0,0 +1,19 @@
1
+ export enum PolicyResource {
2
+ NAVIGATION_METADATA = '/orchestrator/metadata',
3
+ NAVIGATION_SETTINGS = '/orchestrator/settings',
4
+ NAVIGATION_SUBSCRIPTIONS = '/orchestrator/subscriptions',
5
+ NAVIGATION_TASKS = '/orchestrator/tasks',
6
+ NAVIGATION_WORKFLOWS = '/orchestrator/workflows',
7
+ PROCESS_ABORT = '/orchestrator/processes/abort/',
8
+ PROCESS_DELETE = '/orchestrator/processes/delete/',
9
+ PROCESS_DETAILS = '/orchestrator/processes/details/',
10
+ PROCESS_RELATED_SUBSCRIPTIONS = '/orchestrator/subscriptions/view/from-process',
11
+ PROCESS_RETRY = '/orchestrator/processes/retry/',
12
+ PROCESS_USER_INPUT = '/orchestrator/processes/user-input/',
13
+ SUBSCRIPTION_CREATE = '/orchestrator/processes/create/process/menu',
14
+ SUBSCRIPTION_MODIFY = '/orchestrator/subscriptions/modify/',
15
+ SUBSCRIPTION_TERMINATE = '/orchestrator/subscriptions/terminate/',
16
+ SUBSCRIPTION_VALIDATE = '/orchestrator/subscriptions/validate/',
17
+ TASKS_CREATE = '/orchestrator/processes/create/task',
18
+ TASKS_RETRY_ALL = '/orchestrator/processes/all-tasks/retry',
19
+ }
@@ -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([queryKey, ...Object.values(queryVars)], fetchData);
33
+ return useQuery(
34
+ [queryKey, ...Object.values(queryVars)],
35
+ fetchData,
36
+ options,
37
+ );
33
38
  };
package/src/index.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  export * from './api';
2
2
  export * from './components';
3
- export * from './icons';
4
- export * from './theme';
5
- export * from './utils';
3
+ export * from './configuration';
6
4
  export * from './contexts';
7
- export * from './types';
5
+ export * from './graphqlQueries';
8
6
  export * from './hooks';
7
+ export * from './icons';
9
8
  export * from './messages';
10
9
  export * from './pages';
11
- export * from './graphqlQueries';
12
10
  export * from './rtk';
11
+ export * from './theme';
12
+ export * from './types';
13
+ export * from './utils';
@@ -31,6 +31,7 @@
31
31
  "search": "Search",
32
32
  "errorMessage": "An error occurred",
33
33
  "export": "Export",
34
+ "unauthorizedPage": "You are not authorized to see this page",
34
35
  "insyncTrue": "in-sync",
35
36
  "insyncFalse": "out-of-sync"
36
37
  },
@@ -345,6 +346,11 @@
345
346
  "headerTitle": "Total active subscriptions",
346
347
  "listTitle": "Most recent subscriptions"
347
348
  },
349
+ "outOfSyncSubscriptions": {
350
+ "buttonText": "Show all active out-of-sync subscriptions",
351
+ "headerTitle": "Total active out-of-sync subscriptions",
352
+ "listTitle": "Most recent active out-of-sync subscriptions"
353
+ },
348
354
  "activeWorkflows": {
349
355
  "buttonText": "Show all active workflows",
350
356
  "headerTitle": "Total active workflows",
@@ -31,6 +31,7 @@
31
31
  "search": "Zoeken",
32
32
  "errorMessage": "Er is een fout opgetreden",
33
33
  "export": "Exporteren",
34
+ "unauthorizedPage": "Niet geautoriseerd om deze pagina te bekijken",
34
35
  "insyncTrue": "in-sync",
35
36
  "insyncFalse": "out-of-sync"
36
37
  },
@@ -344,6 +345,11 @@
344
345
  "headerTitle": "Totaal aantal actieve subscriptions",
345
346
  "listTitle": "Meest recente subscriptions"
346
347
  },
348
+ "outOfSyncSubscriptions": {
349
+ "buttonText": "Toon alle actieve out-of-sync subscriptions",
350
+ "headerTitle": "Totaal aantal actieve out-of-sync subscriptions",
351
+ "listTitle": "Meest recente actieve out-of-sync subscriptions"
352
+ },
347
353
  "activeWorkflows": {
348
354
  "buttonText": "Toon alle actieve workflows",
349
355
  "headerTitle": "Totaal aantal actieve workflows",