Merge branch '21815-trigrams-exclude-ids'
[arvados.git] / services / workbench2 / src / store / advanced-tab / advanced-tab.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { Dispatch } from 'redux';
6 import { dialogActions } from 'store/dialog/dialog-actions';
7 import { RootState } from 'store/store';
8 import { ResourceKind, extractUuidKind } from 'models/resource';
9 import { getResource } from 'store/resources/resources';
10 import { GroupContentsResourcePrefix } from 'services/groups-service/groups-service';
11 import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
12 import { ContainerRequestResource } from 'models/container-request';
13 import { CollectionResource } from 'models/collection';
14 import { ProjectResource } from 'models/project';
15 import { ServiceRepository } from 'services/services';
16 import { FilterBuilder } from 'services/api/filter-builder';
17 import { ListResults } from 'services/common-service/common-service';
18 import { RepositoryResource } from 'models/repositories';
19 import { SshKeyResource } from 'models/ssh-key';
20 import { VirtualMachinesResource } from 'models/virtual-machines';
21 import { UserResource } from 'models/user';
22 import { LinkResource } from 'models/link';
23 import { WorkflowResource } from 'models/workflow';
24 import { KeepServiceResource } from 'models/keep-services';
25 import { ApiClientAuthorization } from 'models/api-client-authorization';
26 import React from 'react';
27
28 export const ADVANCED_TAB_DIALOG = 'advancedTabDialog';
29
30 export interface AdvancedTabDialogData {
31     uuid: string;
32     apiResponse: JSX.Element;
33     metadata: ListResults<LinkResource> | string;
34     user: UserResource | string;
35     pythonHeader: string;
36     pythonExample: string;
37     cliGetHeader: string;
38     cliGetExample: string;
39     cliUpdateHeader: string;
40     cliUpdateExample: string;
41     curlHeader: string;
42     curlExample: string;
43 }
44
45 enum CollectionData {
46     COLLECTION = 'collection',
47     STORAGE_CLASSES_CONFIRMED = 'storage_classes_confirmed'
48 }
49
50 enum ProcessData {
51     CONTAINER_REQUEST = 'container_request',
52     OUTPUT_NAME = 'output_name'
53 }
54
55 enum ProjectData {
56     GROUP = 'group',
57     DELETE_AT = 'delete_at'
58 }
59
60 enum RepositoryData {
61     REPOSITORY = 'repository',
62     CREATED_AT = 'created_at'
63 }
64
65 enum SshKeyData {
66     SSH_KEY = 'authorized_key',
67     CREATED_AT = 'created_at'
68 }
69
70 enum VirtualMachineData {
71     VIRTUAL_MACHINE = 'virtual_machine',
72     CREATED_AT = 'created_at'
73 }
74
75 enum ResourcePrefix {
76     REPOSITORIES = 'repositories',
77     AUTORIZED_KEYS = 'authorized_keys',
78     VIRTUAL_MACHINES = 'virtual_machines',
79     KEEP_SERVICES = 'keep_services',
80     USERS = 'users',
81     API_CLIENT_AUTHORIZATIONS = 'api_client_authorizations',
82     LINKS = 'links'
83 }
84
85 enum KeepServiceData {
86     KEEP_SERVICE = 'keep_services',
87     CREATED_AT = 'created_at'
88 }
89
90 enum UserData {
91     USER = 'user',
92     USERNAME = 'username'
93 }
94
95 enum ApiClientAuthorizationsData {
96     API_CLIENT_AUTHORIZATION = 'api_client_authorization',
97     EXPIRES_AT = 'expires_at'
98 }
99
100 enum LinkData {
101     LINK = 'link',
102     PROPERTIES = 'properties'
103 }
104
105 enum WorkflowData {
106     WORKFLOW = 'workflow',
107     CREATED_AT = 'created_at'
108 }
109
110 type AdvanceResourceKind = CollectionData | ProcessData | ProjectData | RepositoryData | SshKeyData | VirtualMachineData | KeepServiceData | ApiClientAuthorizationsData | UserData | LinkData | WorkflowData;
111 type AdvanceResourcePrefix = GroupContentsResourcePrefix | ResourcePrefix;
112 type AdvanceResponseData = ContainerRequestResource | ProjectResource | CollectionResource | RepositoryResource | SshKeyResource | VirtualMachinesResource | KeepServiceResource | ApiClientAuthorization | UserResource | LinkResource | WorkflowResource | undefined;
113
114 export const openAdvancedTabDialog = (uuid: string) =>
115     async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
116         const kind = extractUuidKind(uuid);
117         switch (kind) {
118             case ResourceKind.COLLECTION:
119                 const { data: dataCollection, metadata: metaCollection, user: userCollection } = await dispatch<any>(getDataForAdvancedTab(uuid));
120                 const advanceDataCollection = advancedTabData({
121                     uuid,
122                     metadata: metaCollection,
123                     user: userCollection,
124                     apiResponseKind: collectionApiResponse,
125                     data: dataCollection,
126                     resourceKind: CollectionData.COLLECTION,
127                     resourcePrefix: GroupContentsResourcePrefix.COLLECTION,
128                     resourceKindProperty: CollectionData.STORAGE_CLASSES_CONFIRMED,
129                     property: dataCollection.storageClassesConfirmed
130                 });
131                 dispatch<any>(initAdvancedTabDialog(advanceDataCollection));
132                 break;
133             case ResourceKind.PROCESS:
134                 const { data: dataProcess, metadata: metaProcess, user: userProcess } = await dispatch<any>(getDataForAdvancedTab(uuid));
135                 const advancedDataProcess = advancedTabData({
136                     uuid,
137                     metadata: metaProcess,
138                     user: userProcess,
139                     apiResponseKind: containerRequestApiResponse,
140                     data: dataProcess,
141                     resourceKind: ProcessData.CONTAINER_REQUEST,
142                     resourcePrefix: GroupContentsResourcePrefix.PROCESS,
143                     resourceKindProperty: ProcessData.OUTPUT_NAME,
144                     property: dataProcess.outputName
145                 });
146                 dispatch<any>(initAdvancedTabDialog(advancedDataProcess));
147                 break;
148             case ResourceKind.PROJECT:
149                 const { data: dataProject, metadata: metaProject, user: userProject } = await dispatch<any>(getDataForAdvancedTab(uuid));
150                 const advanceDataProject = advancedTabData({
151                     uuid,
152                     metadata: metaProject,
153                     user: userProject,
154                     apiResponseKind: groupRequestApiResponse,
155                     data: dataProject,
156                     resourceKind: ProjectData.GROUP,
157                     resourcePrefix: GroupContentsResourcePrefix.PROJECT,
158                     resourceKindProperty: ProjectData.DELETE_AT,
159                     property: dataProject.deleteAt
160                 });
161                 dispatch<any>(initAdvancedTabDialog(advanceDataProject));
162                 break;
163             case ResourceKind.REPOSITORY:
164                 const dataRepository = getState().repositories.items.find(it => it.uuid === uuid);
165                 const advanceDataRepository = advancedTabData({
166                     uuid,
167                     metadata: '',
168                     user: '',
169                     apiResponseKind: repositoryApiResponse,
170                     data: dataRepository,
171                     resourceKind: RepositoryData.REPOSITORY,
172                     resourcePrefix: ResourcePrefix.REPOSITORIES,
173                     resourceKindProperty: RepositoryData.CREATED_AT,
174                     property: dataRepository!.createdAt
175                 });
176                 dispatch<any>(initAdvancedTabDialog(advanceDataRepository));
177                 break;
178             case ResourceKind.SSH_KEY:
179                 const dataSshKey = getState().auth.sshKeys.find(it => it.uuid === uuid);
180                 const advanceDataSshKey = advancedTabData({
181                     uuid,
182                     metadata: '',
183                     user: '',
184                     apiResponseKind: sshKeyApiResponse,
185                     data: dataSshKey,
186                     resourceKind: SshKeyData.SSH_KEY,
187                     resourcePrefix: ResourcePrefix.AUTORIZED_KEYS,
188                     resourceKindProperty: SshKeyData.CREATED_AT,
189                     property: dataSshKey!.createdAt
190                 });
191                 dispatch<any>(initAdvancedTabDialog(advanceDataSshKey));
192                 break;
193             case ResourceKind.VIRTUAL_MACHINE:
194                 const dataVirtualMachine = getState().virtualMachines.virtualMachines.items.find(it => it.uuid === uuid);
195                 const advanceDataVirtualMachine = advancedTabData({
196                     uuid,
197                     metadata: '',
198                     user: '',
199                     apiResponseKind: virtualMachineApiResponse,
200                     data: dataVirtualMachine,
201                     resourceKind: VirtualMachineData.VIRTUAL_MACHINE,
202                     resourcePrefix: ResourcePrefix.VIRTUAL_MACHINES,
203                     resourceKindProperty: VirtualMachineData.CREATED_AT,
204                     property: dataVirtualMachine.createdAt
205                 });
206                 dispatch<any>(initAdvancedTabDialog(advanceDataVirtualMachine));
207                 break;
208             case ResourceKind.KEEP_SERVICE:
209                 const dataKeepService = getState().keepServices.find(it => it.uuid === uuid);
210                 const advanceDataKeepService = advancedTabData({
211                     uuid,
212                     metadata: '',
213                     user: '',
214                     apiResponseKind: keepServiceApiResponse,
215                     data: dataKeepService,
216                     resourceKind: KeepServiceData.KEEP_SERVICE,
217                     resourcePrefix: ResourcePrefix.KEEP_SERVICES,
218                     resourceKindProperty: KeepServiceData.CREATED_AT,
219                     property: dataKeepService!.createdAt
220                 });
221                 dispatch<any>(initAdvancedTabDialog(advanceDataKeepService));
222                 break;
223             case ResourceKind.USER:
224                 const { resources } = getState();
225                 const data = getResource<UserResource>(uuid)(resources);
226                 const metadata = await services.linkService.list({
227                     filters: new FilterBuilder()
228                         .addEqual('head_uuid', uuid)
229                         .getFilters()
230                 });
231                 const advanceDataUser = advancedTabData({
232                     uuid,
233                     metadata,
234                     user: '',
235                     apiResponseKind: userApiResponse,
236                     data,
237                     resourceKind: UserData.USER,
238                     resourcePrefix: ResourcePrefix.USERS,
239                     resourceKindProperty: UserData.USERNAME,
240                     property: data!.username
241                 });
242                 dispatch<any>(initAdvancedTabDialog(advanceDataUser));
243                 break;
244             case ResourceKind.API_CLIENT_AUTHORIZATION:
245                 const apiClientAuthorizationResources = getState().resources;
246                 const dataApiClientAuthorization = getResource<ApiClientAuthorization>(uuid)(apiClientAuthorizationResources);
247                 const advanceDataApiClientAuthorization = advancedTabData({
248                     uuid,
249                     metadata: '',
250                     user: '',
251                     apiResponseKind: apiClientAuthorizationApiResponse,
252                     data: dataApiClientAuthorization,
253                     resourceKind: ApiClientAuthorizationsData.API_CLIENT_AUTHORIZATION,
254                     resourcePrefix: ResourcePrefix.API_CLIENT_AUTHORIZATIONS,
255                     resourceKindProperty: ApiClientAuthorizationsData.EXPIRES_AT,
256                     property: dataApiClientAuthorization!.createdAt
257                 });
258                 dispatch<any>(initAdvancedTabDialog(advanceDataApiClientAuthorization));
259                 break;
260             case ResourceKind.LINK:
261                 const linkResources = getState().resources;
262                 const dataLink = getResource<LinkResource>(uuid)(linkResources);
263                 const advanceDataLink = advancedTabData({
264                     uuid,
265                     metadata: '',
266                     user: '',
267                     apiResponseKind: linkApiResponse,
268                     data: dataLink,
269                     resourceKind: LinkData.LINK,
270                     resourcePrefix: ResourcePrefix.LINKS,
271                     resourceKindProperty: LinkData.PROPERTIES,
272                     property: dataLink!.properties
273                 });
274                 dispatch<any>(initAdvancedTabDialog(advanceDataLink));
275                 break;
276             case ResourceKind.WORKFLOW:
277                 const wfResources = getState().resources;
278                 const dataWf = getResource<WorkflowResource>(uuid)(wfResources);
279                 const advanceDataWf = advancedTabData({
280                     uuid,
281                     metadata: '',
282                     user: '',
283                     apiResponseKind: wfApiResponse,
284                     data: dataWf,
285                     resourceKind: WorkflowData.WORKFLOW,
286                     resourcePrefix: GroupContentsResourcePrefix.WORKFLOW,
287                     resourceKindProperty: WorkflowData.CREATED_AT,
288                     property: dataWf!.createdAt
289                 });
290                 dispatch<any>(initAdvancedTabDialog(advanceDataWf));
291                 break;
292
293             default:
294                 dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Could not open advanced tab for this resource.", hideDuration: 2000, kind: SnackbarKind.ERROR }));
295         }
296     };
297
298 const getDataForAdvancedTab = (uuid: string) =>
299     async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
300         const { resources } = getState();
301         const data = getResource<any>(uuid)(resources);
302         const metadata = await services.linkService.list({
303             filters: new FilterBuilder()
304                 .addEqual('head_uuid', uuid)
305                 .getFilters()
306         });
307
308         return { data, metadata };
309     };
310
311 const initAdvancedTabDialog = (data: AdvancedTabDialogData) => dialogActions.OPEN_DIALOG({ id: ADVANCED_TAB_DIALOG, data });
312
313 interface AdvancedTabData {
314     uuid: string;
315     metadata: ListResults<LinkResource> | string;
316     user: UserResource | string;
317     apiResponseKind: (apiResponse) => JSX.Element;
318     data: AdvanceResponseData;
319     resourceKind: AdvanceResourceKind;
320     resourcePrefix: AdvanceResourcePrefix;
321     resourceKindProperty: AdvanceResourceKind;
322     property: any;
323 }
324
325 const advancedTabData = ({ uuid, user, metadata, apiResponseKind, data, resourceKind, resourcePrefix, resourceKindProperty, property }: AdvancedTabData) => {
326     return {
327         uuid,
328         user,
329         metadata,
330         apiResponse: apiResponseKind(data),
331         pythonHeader: pythonHeader(resourceKind),
332         pythonExample: pythonExample(uuid, resourcePrefix),
333         cliGetHeader: cliGetHeader(resourceKind),
334         cliGetExample: cliGetExample(uuid, resourceKind),
335         cliUpdateHeader: cliUpdateHeader(resourceKind, resourceKindProperty),
336         cliUpdateExample: cliUpdateExample(uuid, resourceKind, property, resourceKindProperty),
337         curlHeader: curlHeader(resourceKind, resourceKindProperty),
338         curlExample: curlExample(uuid, resourcePrefix, property, resourceKind, resourceKindProperty),
339     };
340 };
341
342 const pythonHeader = (resourceKind: string) =>
343     `An example python command to get a ${resourceKind} using its uuid:`;
344
345 const pythonExample = (uuid: string, resourcePrefix: string) => {
346     const pythonExample = `import arvados
347
348 x = arvados.api().${resourcePrefix}().get(uuid='${uuid}').execute()`;
349
350     return pythonExample;
351 };
352
353 const cliGetHeader = (resourceKind: string) =>
354     `An example arv command to get a ${resourceKind} using its uuid:`;
355
356 const cliGetExample = (uuid: string, resourceKind: string) => {
357     const cliGetExample = `arv ${resourceKind} get \\
358   --uuid ${uuid}`;
359
360     return cliGetExample;
361 };
362
363 const cliUpdateHeader = (resourceKind: string, resourceName: string) =>
364     `An example arv command to update the "${resourceName}" attribute for the current ${resourceKind}:`;
365
366 const cliUpdateExample = (uuid: string, resourceKind: string, resource: string | string[], resourceName: string) => {
367     const CLIUpdateCollectionExample = `arv ${resourceKind} update \\
368   --uuid ${uuid} \\
369   --${resourceKind} '{"${resourceName}":${JSON.stringify(resource)}}'`;
370
371     return CLIUpdateCollectionExample;
372 };
373
374 const curlHeader = (resourceKind: string, resource: string) =>
375     `An example curl command to update the "${resource}" attribute for the current ${resourceKind}:`;
376
377 const curlExample = (uuid: string, resourcePrefix: string, resource: string | string[], resourceKind: string, resourceName: string) => {
378     const curlExample = `curl -X PUT \\
379   -H "Authorization: OAuth2 $ARVADOS_API_TOKEN" \\
380   --data-urlencode ${resourceKind}@/dev/stdin \\
381   https://$ARVADOS_API_HOST/arvados/v1/${resourcePrefix}/${uuid} \\
382   <<EOF
383 {
384   "${resourceName}": ${JSON.stringify(resource, null, 4)}
385 }
386 EOF`;
387
388     return curlExample;
389 };
390
391 const stringify = (item: string | null | number | boolean) =>
392     JSON.stringify(item) || 'null';
393
394 const stringifyObject = (item: any) =>
395     JSON.stringify(item, null, 2) || 'null';
396
397 const containerRequestApiResponse = (apiResponse: ContainerRequestResource): JSX.Element => {
398     const { uuid, ownerUuid, createdAt, modifiedAt, modifiedByClientUuid, modifiedByUserUuid, name, description, properties, state, requestingContainerUuid, containerUuid,
399         containerCountMax, mounts, runtimeConstraints, containerImage, environment, cwd, command, outputPath, priority, expiresAt, filters, containerCount,
400         useExisting, schedulingParameters, outputUuid, logUuid, outputName, outputTtl } = apiResponse;
401     const response = `
402 "uuid": "${uuid}",
403 "owner_uuid": "${ownerUuid}",
404 "created_at": "${createdAt}",
405 "modified_at": ${stringify(modifiedAt)},
406 "modified_by_client_uuid": ${stringify(modifiedByClientUuid)},
407 "modified_by_user_uuid": ${stringify(modifiedByUserUuid)},
408 "name": ${stringify(name)},
409 "description": ${stringify(description)},
410 "properties": ${stringifyObject(properties)},
411 "state": ${stringify(state)},
412 "requesting_container_uuid": ${stringify(requestingContainerUuid)},
413 "container_uuid": ${stringify(containerUuid)},
414 "container_count_max": ${stringify(containerCountMax)},
415 "mounts": ${stringifyObject(mounts)},
416 "runtime_constraints": ${stringifyObject(runtimeConstraints)},
417 "container_image": ${stringify(containerImage)},
418 "environment": ${stringifyObject(environment)},
419 "cwd": ${stringify(cwd)},
420 "command": ${stringifyObject(command)},
421 "output_path": ${stringify(outputPath)},
422 "priority": ${stringify(priority)},
423 "expires_at": ${stringify(expiresAt)},
424 "filters": ${stringify(filters)},
425 "container_count": ${stringify(containerCount)},
426 "use_existing": ${stringify(useExisting)},
427 "scheduling_parameters": ${stringifyObject(schedulingParameters)},
428 "output_uuid": ${stringify(outputUuid)},
429 "log_uuid": ${stringify(logUuid)},
430 "output_name": ${stringify(outputName)},
431 "output_ttl": ${stringify(outputTtl)}`;
432
433     return <span style={{ marginLeft: '-15px' }}>{'{'} {response} {'\n'} <span style={{ marginLeft: '-15px' }}>{'}'}</span></span>;
434 };
435
436 const collectionApiResponse = (apiResponse: CollectionResource): JSX.Element => {
437     const { uuid, ownerUuid, createdAt, modifiedAt, modifiedByClientUuid, modifiedByUserUuid, name, description, properties, portableDataHash, replicationDesired,
438         replicationConfirmedAt, replicationConfirmed, deleteAt, trashAt, isTrashed, storageClassesDesired,
439         storageClassesConfirmed, storageClassesConfirmedAt, currentVersionUuid, version, preserveVersion, fileCount, fileSizeTotal } = apiResponse;
440     const response = `
441 "uuid": "${uuid}",
442 "owner_uuid": "${ownerUuid}",
443 "created_at": "${createdAt}",
444 "modified_by_client_uuid": ${stringify(modifiedByClientUuid)},
445 "modified_by_user_uuid": ${stringify(modifiedByUserUuid)},
446 "modified_at": ${stringify(modifiedAt)},
447 "portable_data_hash": ${stringify(portableDataHash)},
448 "replication_desired": ${stringify(replicationDesired)},
449 "replication_confirmed_at": ${stringify(replicationConfirmedAt)},
450 "replication_confirmed": ${stringify(replicationConfirmed)},
451 "name": ${stringify(name)},
452 "description": ${stringify(description)},
453 "properties": ${stringifyObject(properties)},
454 "delete_at": ${stringify(deleteAt)},
455 "trash_at": ${stringify(trashAt)},
456 "is_trashed": ${stringify(isTrashed)},
457 "storage_classes_desired": ${JSON.stringify(storageClassesDesired, null, 2)},
458 "storage_classes_confirmed": ${JSON.stringify(storageClassesConfirmed, null, 2)},
459 "storage_classes_confirmed_at": ${stringify(storageClassesConfirmedAt)},
460 "current_version_uuid": ${stringify(currentVersionUuid)},
461 "version": ${version},
462 "preserve_version": ${preserveVersion},
463 "file_count": ${fileCount},
464 "file_size_total": ${fileSizeTotal}`;
465
466     return <span style={{ marginLeft: '-15px' }}>{'{'} {response} {'\n'} <span style={{ marginLeft: '-15px' }}>{'}'}</span></span>;
467 };
468
469 const groupRequestApiResponse = (apiResponse: ProjectResource): JSX.Element => {
470     const { uuid, ownerUuid, createdAt, modifiedAt, modifiedByClientUuid, modifiedByUserUuid, name,
471         description, groupClass, trashAt, isTrashed, deleteAt, properties,
472         canWrite, canManage } = apiResponse;
473     const response = `
474 "uuid": "${uuid}",
475 "owner_uuid": "${ownerUuid}",
476 "created_at": "${createdAt}",
477 "modified_by_client_uuid": ${stringify(modifiedByClientUuid)},
478 "modified_by_user_uuid": ${stringify(modifiedByUserUuid)},
479 "modified_at": ${stringify(modifiedAt)},
480 "name": ${stringify(name)},
481 "description": ${stringify(description)},
482 "group_class": ${stringify(groupClass)},
483 "trash_at": ${stringify(trashAt)},
484 "is_trashed": ${stringify(isTrashed)},
485 "delete_at": ${stringify(deleteAt)},
486 "properties": ${stringifyObject(properties)},
487 "can_write": ${stringify(canWrite)},
488 "can_manage": ${stringify(canManage)}`;
489
490     return <span style={{ marginLeft: '-15px' }}>{'{'} {response} {'\n'} <span style={{ marginLeft: '-15px' }}>{'}'}</span></span>;
491 };
492
493 const repositoryApiResponse = (apiResponse: RepositoryResource): JSX.Element => {
494     const { uuid, ownerUuid, createdAt, modifiedAt, modifiedByClientUuid, modifiedByUserUuid, name, cloneUrls } = apiResponse;
495     const response = `
496 "uuid": "${uuid}",
497 "owner_uuid": "${ownerUuid}",
498 "modified_by_client_uuid": ${stringify(modifiedByClientUuid)},
499 "modified_by_user_uuid": ${stringify(modifiedByUserUuid)},
500 "modified_at": ${stringify(modifiedAt)},
501 "name": ${stringify(name)},
502 "created_at": "${createdAt}",
503 "clone_urls": ${stringifyObject(cloneUrls)}`;
504
505     return <span style={{ marginLeft: '-15px' }}>{'{'} {response} {'\n'} <span style={{ marginLeft: '-15px' }}>{'}'}</span></span>;
506 };
507
508 const sshKeyApiResponse = (apiResponse: SshKeyResource): JSX.Element => {
509     const { uuid, ownerUuid, createdAt, modifiedAt, modifiedByClientUuid, modifiedByUserUuid, name, authorizedUserUuid, expiresAt } = apiResponse;
510     const response = `
511 "uuid": "${uuid}",
512 "owner_uuid": "${ownerUuid}",
513 "authorized_user_uuid": "${authorizedUserUuid}",
514 "modified_by_client_uuid": ${stringify(modifiedByClientUuid)},
515 "modified_by_user_uuid": ${stringify(modifiedByUserUuid)},
516 "modified_at": ${stringify(modifiedAt)},
517 "name": ${stringify(name)},
518 "created_at": "${createdAt}",
519 "expires_at": "${expiresAt}"`;
520     return <span style={{ marginLeft: '-15px' }}>{'{'} {response} {'\n'} <span style={{ marginLeft: '-15px' }}>{'}'}</span></span>;
521 };
522
523 const virtualMachineApiResponse = (apiResponse: VirtualMachinesResource): JSX.Element => {
524     const { uuid, ownerUuid, createdAt, modifiedAt, modifiedByClientUuid, modifiedByUserUuid, hostname } = apiResponse;
525     const response = `
526 "hostname": ${stringify(hostname)},
527 "uuid": "${uuid}",
528 "owner_uuid": "${ownerUuid}",
529 "modified_by_client_uuid": ${stringify(modifiedByClientUuid)},
530 "modified_by_user_uuid": ${stringify(modifiedByUserUuid)},
531 "modified_at": ${stringify(modifiedAt)},
532 "modified_at": ${stringify(modifiedAt)},
533 "created_at": "${createdAt}"`;
534
535     return <span style={{ marginLeft: '-15px' }}>{'{'} {response} {'\n'} <span style={{ marginLeft: '-15px' }}>{'}'}</span></span>;
536 };
537
538 const keepServiceApiResponse = (apiResponse: KeepServiceResource): JSX.Element => {
539     const {
540         uuid, readOnly, serviceHost, servicePort, serviceSslFlag, serviceType,
541         ownerUuid, createdAt, modifiedAt, modifiedByClientUuid, modifiedByUserUuid
542     } = apiResponse;
543     const response = `
544 "uuid": "${uuid}",
545 "owner_uuid": "${ownerUuid}",
546 "modified_by_client_uuid": ${stringify(modifiedByClientUuid)},
547 "modified_by_user_uuid": ${stringify(modifiedByUserUuid)},
548 "modified_at": ${stringify(modifiedAt)},
549 "service_host": "${serviceHost}",
550 "service_port": "${servicePort}",
551 "service_ssl_flag": "${stringify(serviceSslFlag)}",
552 "service_type": "${serviceType}",
553 "created_at": "${createdAt}",
554 "read_only": "${stringify(readOnly)}"`;
555
556     return <span style={{ marginLeft: '-15px' }}>{'{'} {response} {'\n'} <span style={{ marginLeft: '-15px' }}>{'}'}</span></span>;
557 };
558
559 const userApiResponse = (apiResponse: UserResource): JSX.Element => {
560     const {
561         uuid, ownerUuid, createdAt, modifiedAt, modifiedByClientUuid, modifiedByUserUuid,
562         email, firstName, lastName, username, isActive, isAdmin, prefs,
563     } = apiResponse;
564     const response = `
565 "uuid": "${uuid}",
566 "owner_uuid": "${ownerUuid}",
567 "created_at": "${createdAt}",
568 "modified_by_client_uuid": ${stringify(modifiedByClientUuid)},
569 "modified_by_user_uuid": ${stringify(modifiedByUserUuid)},
570 "modified_at": ${stringify(modifiedAt)},
571 "email": "${email}",
572 "first_name": "${firstName}",
573 "last_name": "${stringify(lastName)}",
574 "username": "${username}",
575 "is_active": "${isActive},
576 "is_admin": "${isAdmin},
577 "prefs": "${stringifyObject(prefs)},
578 "username": "${username}"`;
579
580     return <span style={{ marginLeft: '-15px' }}>{'{'} {response} {'\n'} <span style={{ marginLeft: '-15px' }}>{'}'}</span></span>;
581 };
582
583 const apiClientAuthorizationApiResponse = (apiResponse: ApiClientAuthorization): JSX.Element => {
584     const {
585         uuid, ownerUuid, apiToken, apiClientId, userId, createdByIpAddress, lastUsedByIpAddress,
586         lastUsedAt, expiresAt, scopes, updatedAt, createdAt
587     } = apiResponse;
588     const response = `
589 "uuid": "${uuid}",
590 "owner_uuid": "${ownerUuid}",
591 "api_token": "${stringify(apiToken)}",
592 "api_client_id": "${stringify(apiClientId)}",
593 "created_by_ip_address": "${stringify(createdByIpAddress)}",
594 "last_used_by_ip_address": "${stringify(lastUsedByIpAddress)}",
595 "last_used_at": "${stringify(lastUsedAt)}",
596 "expires_at": "${stringify(expiresAt)}",
597 "created_at": "${stringify(createdAt)}",
598 "updated_at": "${stringify(updatedAt)}",
599 "scopes": "${JSON.stringify(scopes, null, 2)}"`;
600
601     return <span style={{ marginLeft: '-15px' }}>{'{'} {response} {'\n'} <span style={{ marginLeft: '-15px' }}>{'}'}</span></span>;
602 };
603
604 const linkApiResponse = (apiResponse: LinkResource): JSX.Element => {
605     const {
606         uuid, name, headUuid, properties, headKind, tailUuid, tailKind, linkClass,
607         ownerUuid, createdAt, modifiedAt, modifiedByClientUuid, modifiedByUserUuid
608     } = apiResponse;
609     const response = `
610 "uuid": "${uuid}",
611 "name": "${name}",
612 "head_uuid": "${headUuid}",
613 "head_kind": "${headKind}",
614 "tail_uuid": "${tailUuid}",
615 "tail_kind": "${tailKind}",
616 "link_class": "${linkClass}",
617 "owner_uuid": "${ownerUuid}",
618 "created_at": "${stringify(createdAt)}",
619 "modified_at": ${stringify(modifiedAt)},
620 "modified_by_client_uuid": ${stringify(modifiedByClientUuid)},
621 "modified_by_user_uuid": ${stringify(modifiedByUserUuid)},
622 "properties": "${JSON.stringify(properties, null, 2)}"`;
623
624     return <span style={{ marginLeft: '-15px' }}>{'{'} {response} {'\n'} <span style={{ marginLeft: '-15px' }}>{'}'}</span></span>;
625 };
626
627
628 const wfApiResponse = (apiResponse: WorkflowResource): JSX.Element => {
629     const {
630         uuid, name,
631         ownerUuid, createdAt, modifiedAt, modifiedByClientUuid, modifiedByUserUuid, description
632     } = apiResponse;
633     const response = `
634 "uuid": "${uuid}",
635 "name": "${name}",
636 "owner_uuid": "${ownerUuid}",
637 "created_at": "${stringify(createdAt)}",
638 "modified_at": ${stringify(modifiedAt)},
639 "modified_by_client_uuid": ${stringify(modifiedByClientUuid)},
640 "modified_by_user_uuid": ${stringify(modifiedByUserUuid)}
641 "description": ${stringify(description)}`;
642
643     return <span style={{ marginLeft: '-15px' }}>{'{'} {response} {'\n'} <span style={{ marginLeft: '-15px' }}>{'}'}</span></span>;
644 };