[ARVADOS-WORKBENCH2] updated: 1.4.0-25-g0aea0fe4

Git user git at public.curoverse.com
Thu Aug 22 15:35:22 UTC 2019


Summary of changes:
 .gitignore                                         |   1 +
 README.md                                          |  10 +
 src/common/config.ts                               | 238 +++++++++++----------
 src/models/mount-types.ts                          |   2 +-
 src/models/node.ts                                 |  16 +-
 src/models/process.ts                              |   3 +
 src/models/runtime-constraints.ts                  |   2 +-
 src/models/scheduling-parameters.ts                |   2 +-
 src/models/workflow.ts                             |  23 +-
 src/services/api/filter-builder.test.ts            |   2 +-
 src/services/api/filter-builder.ts                 |  11 +-
 .../common-service/common-resource-service.test.ts |  26 ++-
 src/services/common-service/common-service.ts      |   2 +-
 src/store/advanced-tab/advanced-tab.tsx            |   6 +-
 src/store/auth/auth-action-session.ts              |   6 +-
 src/store/auth/auth-action.ts                      |  12 +-
 .../collection-panel/collection-panel-action.ts    |  18 +-
 src/store/processes/process-input-actions.ts       |  16 +-
 src/store/processes/processes-actions.ts           |  32 ++-
 .../run-process-panel/run-process-panel-actions.ts |   2 +-
 .../compute-nodes-dialog/attributes-dialog.tsx     |  24 +--
 .../process-input-dialog/process-input-dialog.tsx  |  29 +--
 .../run-process-advanced-form.tsx                  |   6 +-
 src/websocket/websocket.ts                         |  11 +-
 24 files changed, 296 insertions(+), 204 deletions(-)

       via  0aea0fe49faebf703226d662012aae71904a5a30 (commit)
       via  79d59f2396e2a2868388c5b566d10c4b45a3d759 (commit)
       via  c5fa0c4eed19a9d0c163ea1727189970533f0203 (commit)
       via  f159c2bb8e32579651942e8ba7a53c4d90a9b890 (commit)
       via  e5b6b618ea041395c10568d7245d7733b4cb2a1e (commit)
       via  59a7af19f6889e79b1b9a4348e9253f3fa0f50c3 (commit)
       via  1afdf6484780b11ec66e6048ded1789b92701a0e (commit)
       via  d01ef49ba6d88c0a00b0e784e73a1170436faa9f (commit)
       via  69e971b0d44effcf900cf830edf8592731aff2b1 (commit)
       via  50ab8cb746632cb442c064449503e527c4cdddbe (commit)
       via  692582f3b80fa8f886d4ddb37055d71b3c3db14d (commit)
       via  9cc1b7cb37e9f70103b7fce3a8548f699724c18d (commit)
       via  f0499df0c096a1fa4c4beef3a8572dce51b1cb94 (commit)
       via  e1c5bef353f132b92a1258bf4933d77fd86c3e4e (commit)
       via  6ead1b122e52a587963383f6a15edf6dfc166ef6 (commit)
       via  61769345c78e04b0f756dcd15e39fe57ddb75c80 (commit)
       via  c8c5fc13ef57812800fb6ba4553d009ba8c5c7be (commit)
       via  de9713360b5bc04dc3586b332bc98db5d97650a5 (commit)
       via  9dba704e6e39056af0ec49a64cb2151b2f4321d0 (commit)
       via  76c84f182253b2b10b570d669589fa576dd8ba81 (commit)
       via  fea9aae78f6033ba5cc5b18f9350eecb7dabe9b1 (commit)
       via  66eb2fa83ea055c5214e6e3d2d5d66b9c1173dbe (commit)
       via  24e864bc823b27ac27c39e9e5879987924330007 (commit)
       via  9f12beba0400015a833e2719721bf3369f4f2d94 (commit)
       via  b545b17633d2d37242a39a2d1b474e3206d44e41 (commit)
      from  06fc8f6a97afa24144cb435f5af228fec6162a6b (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 0aea0fe49faebf703226d662012aae71904a5a30
Merge: f0499df0 79d59f23
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Tue Aug 20 16:42:04 2019 -0300

    Merge branch '15407-camel-casing-fix'
    Closes #15407
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima at veritasgenetics.com>


commit 79d59f2396e2a2868388c5b566d10c4b45a3d759
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Tue Aug 20 16:08:17 2019 -0300

    15407: Removes snackbar notification when websocket is disabled.
    
    This will be implemented as a notification on the "bell" notifications menu.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima at veritasgenetics.com>

diff --git a/src/websocket/websocket.ts b/src/websocket/websocket.ts
index e7ea3016..8161f193 100644
--- a/src/websocket/websocket.ts
+++ b/src/websocket/websocket.ts
@@ -21,8 +21,7 @@ export const initWebSocket = (config: Config, authService: AuthService, store: R
         webSocketService.setMessageListener(messageListener(store));
         webSocketService.connect();
     } else {
-        console.warn("WARNING: Websocket ExternalURL is not set on the API Server");
-        store.dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Websocket URL missing on cluster config", kind: SnackbarKind.WARNING }));
+        console.warn("WARNING: Websocket ExternalURL is not set on the cluster config.");
     }
 };
 

commit c5fa0c4eed19a9d0c163ea1727189970533f0203
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Tue Aug 20 14:51:58 2019 -0300

    15407: Simplifies code handling workflow json mounts.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima at veritasgenetics.com>

diff --git a/src/models/process.ts b/src/models/process.ts
index 9762d504..b89e3bef 100644
--- a/src/models/process.ts
+++ b/src/models/process.ts
@@ -9,6 +9,9 @@ import { WorkflowInputsData } from './workflow';
 
 export type ProcessResource = ContainerRequestResource;
 
+export const MOUNT_PATH_CWL_WORKFLOW = '/var/lib/cwl/workflow.json';
+export const MOUNT_PATH_CWL_INPUT = '/var/lib/cwl/cwl.input.json';
+
 export const createWorkflowMounts = (workflow: WorkflowResource, inputs: WorkflowInputsData): { [path: string]: MountType } => {
     return {
         '/var/spool/cwl': {
diff --git a/src/models/workflow.ts b/src/models/workflow.ts
index 4fc70419..8d0b37de 100644
--- a/src/models/workflow.ts
+++ b/src/models/workflow.ts
@@ -122,7 +122,8 @@ export const parseWorkflowDefinition = (workflow: WorkflowResource): WorkflowRes
 };
 
 export const getWorkflowInputs = (workflowDefinition: WorkflowResourceDefinition) => {
-    const mainWorkflow = workflowDefinition.$graph!.find(item => item.class === 'Workflow' && item.id === '#main');
+    if (!workflowDefinition.$graph) { return undefined; }
+    const mainWorkflow = workflowDefinition.$graph.find(item => item.class === 'Workflow' && item.id === '#main');
     return mainWorkflow
         ? mainWorkflow.inputs
         : undefined;
diff --git a/src/store/processes/process-input-actions.ts b/src/store/processes/process-input-actions.ts
index 88624d08..7e22b53f 100644
--- a/src/store/processes/process-input-actions.ts
+++ b/src/store/processes/process-input-actions.ts
@@ -5,8 +5,11 @@
 import { dialogActions } from '~/store/dialog/dialog-actions';
 import { RootState } from '~/store/store';
 import { Dispatch } from 'redux';
-import { getProcess } from '~/store/processes/process';
+import { getProcess, Process } from '~/store/processes/process';
 import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
+import { getWorkflowInputs } from '~/models/workflow';
+import { JSONMount } from '~/models/mount-types';
+import { MOUNT_PATH_CWL_WORKFLOW } from '~/models/process';
 
 export const PROCESS_INPUT_DIALOG_NAME = 'processInputDialog';
 
@@ -15,10 +18,17 @@ export const openProcessInputDialog = (processUuid: string) =>
         const process = getProcess(processUuid)(getState().resources);
         if (process) {
             const data: any = process;
-            if (data && data.containerRequest.mounts["/var/lib/cwl/workflow.json"] && data.containerRequest.mounts["/var/lib/cwl/workflow.json"].content.$graph.find((a: any) => a.class === 'Workflow' && a.id === '#main') && data.containerRequest.mounts["/var/lib/cwl/workflow.json"].content.$graph.find((a: any) => a.class === 'Workflow' && a.id === '#main').inputs.length > 0) {
+            const inputs = getInputsFromWFMount(process);
+            if (inputs && inputs.length > 0) {
                 dispatch(dialogActions.OPEN_DIALOG({ id: PROCESS_INPUT_DIALOG_NAME, data }));
             } else {
                 dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'There are no inputs in this process!', kind: SnackbarKind.ERROR }));
             }
         }
-    };
\ No newline at end of file
+    };
+
+const getInputsFromWFMount = (process: Process) => {
+    if (!process || !process.containerRequest[MOUNT_PATH_CWL_WORKFLOW] ) { return undefined; }
+    const mnt = process.containerRequest.mounts[MOUNT_PATH_CWL_WORKFLOW] as JSONMount;
+    return getWorkflowInputs(mnt.content);
+};
\ No newline at end of file
diff --git a/src/store/processes/processes-actions.ts b/src/store/processes/processes-actions.ts
index 91996dde..b65a2202 100644
--- a/src/store/processes/processes-actions.ts
+++ b/src/store/processes/processes-actions.ts
@@ -18,6 +18,8 @@ import { getResource } from '~/store/resources/resources';
 import { initialize } from "redux-form";
 import { RUN_PROCESS_BASIC_FORM, RunProcessBasicFormData } from "~/views/run-process-panel/run-process-basic-form";
 import { RunProcessAdvancedFormData, RUN_PROCESS_ADVANCED_FORM } from "~/views/run-process-panel/run-process-advanced-form";
+import { MOUNT_PATH_CWL_WORKFLOW, MOUNT_PATH_CWL_INPUT } from '~/models/process';
+import { getWorkflowInputs } from "~/models/workflow";
 
 export const loadProcess = (containerRequestUuid: string) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise<Process> => {
@@ -84,10 +86,9 @@ export const reRunProcess = (processUuid: string, workflowUuid: string) =>
         const workflows = getState().runProcessPanel.searchWorkflows;
         const workflow = workflows.find(workflow => workflow.uuid === workflowUuid);
         if (workflow && process) {
-            const newValues = getInputs(process);
-            process.mounts["/var/lib/cwl/workflow.json"].content.$graph.find(
-                (a: any) => a.id === '#main').inputs = newValues;
-            const stringifiedDefinition = JSON.stringify(process.mounts["/var/lib/cwl/workflow.json"].content);
+            let inputs = getWorkflowInputs(process.mounts[MOUNT_PATH_CWL_WORKFLOW]);
+            inputs = getInputs(process);
+            const stringifiedDefinition = JSON.stringify(process.mounts[MOUNT_PATH_CWL_WORKFLOW].content);
             const newWorkflow = { ...workflow, definition: stringifiedDefinition };
 
             const basicInitialData: RunProcessBasicFormData = { name: `Copy of: ${process.name}`, description: process.description };
@@ -112,19 +113,21 @@ export const reRunProcess = (processUuid: string, workflowUuid: string) =>
         }
     };
 
-const getInputs = (data: any) =>
-    data && data.mounts["/var/lib/cwl/workflow.json"] ? data.mounts["/var/lib/cwl/workflow.json"].content.$graph.find(
-        (a: any) => a.id === '#main').inputs.map(
-            (it: any) => (
-                {
-                    type: it.type,
-                    id: it.id,
-                    label: it.label,
-                    default: data.mounts["/var/lib/cwl/cwl.input.json"].content[it.id],
-                    doc: it.doc
-                }
-            )
-        ) : [];
+const getInputs = (data: any) => {
+    if (!data || !data.mounts || !data.mounts[MOUNT_PATH_CWL_WORKFLOW]) { return []; }
+    const inputs = getWorkflowInputs(data.mounts[MOUNT_PATH_CWL_WORKFLOW].content);
+    return inputs ? inputs.map(
+        (it: any) => (
+            {
+                type: it.type,
+                id: it.id,
+                label: it.label,
+                default: data.mounts[MOUNT_PATH_CWL_INPUT].content[it.id],
+                doc: it.doc
+            }
+    )
+    ) : [];
+};
 
 export const openRemoveProcessDialog = (uuid: string) =>
     (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
diff --git a/src/views-components/process-input-dialog/process-input-dialog.tsx b/src/views-components/process-input-dialog/process-input-dialog.tsx
index 1650c902..a2d59407 100644
--- a/src/views-components/process-input-dialog/process-input-dialog.tsx
+++ b/src/views-components/process-input-dialog/process-input-dialog.tsx
@@ -8,6 +8,8 @@ import { WithDialogProps } from '~/store/dialog/with-dialog';
 import { withDialog } from "~/store/dialog/with-dialog";
 import { PROCESS_INPUT_DIALOG_NAME } from '~/store/processes/process-input-actions';
 import { RunProcessInputsForm } from "~/views/run-process-panel/run-process-inputs-form";
+import { MOUNT_PATH_CWL_WORKFLOW, MOUNT_PATH_CWL_INPUT } from "~/models/process";
+import { getWorkflowInputs } from "~/models/workflow";
 
 export const ProcessInputDialog = withDialog(PROCESS_INPUT_DIALOG_NAME)(
     (props: WithDialogProps<any>) =>
@@ -31,16 +33,18 @@ export const ProcessInputDialog = withDialog(PROCESS_INPUT_DIALOG_NAME)(
         </Dialog>
 );
 
-const getInputs = (data: any) =>
-    data && data.mounts["/var/lib/cwl/workflow.json"] ? data.mounts["/var/lib/cwl/workflow.json"].content.$graph.find(
-        (a: any) => a.id === '#main').inputs.map(
-            (it: any) => (
-                {
-                    type: it.type,
-                    id: it.id,
-                    label: it.label,
-                    value: data.mounts["/var/lib/cwl/cwl.input.json"].content[it.id],
-                    disabled: true
-                }
-            )
-        ) : [];
+const getInputs = (data: any) => {
+    if (!data || !data.mounts || !data.mounts[MOUNT_PATH_CWL_WORKFLOW]) { return []; }
+    const inputs = getWorkflowInputs(data.mounts[MOUNT_PATH_CWL_WORKFLOW].content);
+    return inputs ? inputs.map(
+        (it: any) => (
+            {
+                type: it.type,
+                id: it.id,
+                label: it.label,
+                value: data.mounts[MOUNT_PATH_CWL_INPUT].content[it.id],
+                disabled: true
+            }
+        )
+    ) : [];
+};

commit f159c2bb8e32579651942e8ba7a53c4d90a9b890
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Tue Aug 20 11:44:17 2019 -0300

    15407: Adds user visible warning when websocket url is not set.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima at veritasgenetics.com>

diff --git a/.gitignore b/.gitignore
index c18f27f6..45df030d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,6 +22,7 @@
 .env.development.local
 .env.test.local
 .env.production.local
+.npm.local
 
 npm-debug.log*
 yarn-debug.log*
diff --git a/src/websocket/websocket.ts b/src/websocket/websocket.ts
index 4fd7cc2c..e7ea3016 100644
--- a/src/websocket/websocket.ts
+++ b/src/websocket/websocket.ts
@@ -13,6 +13,7 @@ import { loadContainers } from '~/store/processes/processes-actions';
 import { LogEventType } from '~/models/log';
 import { addProcessLogsPanelItem } from '../store/process-logs-panel/process-logs-panel-actions';
 import { FilterBuilder } from "~/services/api/filter-builder";
+import { snackbarActions, SnackbarKind } from "~/store/snackbar/snackbar-actions";
 
 export const initWebSocket = (config: Config, authService: AuthService, store: RootStore) => {
     if (config.websocketUrl) {
@@ -20,7 +21,8 @@ export const initWebSocket = (config: Config, authService: AuthService, store: R
         webSocketService.setMessageListener(messageListener(store));
         webSocketService.connect();
     } else {
-        console.warn("WARNING: webSocketUrl is not configured on the API Server");
+        console.warn("WARNING: Websocket ExternalURL is not set on the API Server");
+        store.dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Websocket URL missing on cluster config", kind: SnackbarKind.WARNING }));
     }
 };
 

commit e5b6b618ea041395c10568d7245d7733b4cb2a1e
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Tue Aug 20 10:27:35 2019 -0300

    15407: Fixes app crash when webSocketUrl is not set on the API Server.
    
    Instead, show a warning message on the JS console.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima at veritasgenetics.com>

diff --git a/src/websocket/websocket.ts b/src/websocket/websocket.ts
index e3f1e192..4fd7cc2c 100644
--- a/src/websocket/websocket.ts
+++ b/src/websocket/websocket.ts
@@ -15,9 +15,13 @@ import { addProcessLogsPanelItem } from '../store/process-logs-panel/process-log
 import { FilterBuilder } from "~/services/api/filter-builder";
 
 export const initWebSocket = (config: Config, authService: AuthService, store: RootStore) => {
-    const webSocketService = new WebSocketService(config.websocketUrl, authService);
-    webSocketService.setMessageListener(messageListener(store));
-    webSocketService.connect();
+    if (config.websocketUrl) {
+        const webSocketService = new WebSocketService(config.websocketUrl, authService);
+        webSocketService.setMessageListener(messageListener(store));
+        webSocketService.connect();
+    } else {
+        console.warn("WARNING: webSocketUrl is not configured on the API Server");
+    }
 };
 
 const messageListener = (store: RootStore) => (message: ResourceEventMessage) => {

commit 59a7af19f6889e79b1b9a4348e9253f3fa0f50c3
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Mon Aug 19 14:39:22 2019 -0300

    15407: Fixes mount handling. Re-run process now shows dialog without erroring.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima at veritasgenetics.com>

diff --git a/src/models/workflow.ts b/src/models/workflow.ts
index a858c0d7..4fc70419 100644
--- a/src/models/workflow.ts
+++ b/src/models/workflow.ts
@@ -11,9 +11,8 @@ export interface WorkflowResource extends Resource {
     description: string;
     definition: string;
 }
-export interface WorkflowResoruceDefinition {
+export interface WorkflowResourceDefinition {
     cwlVersion: string;
-    graph?: Array<Workflow | CommandLineTool>;
     $graph?: Array<Workflow | CommandLineTool>;
 }
 export interface Workflow {
@@ -117,23 +116,16 @@ export type DirectoryArrayCommandInputParameter = GenericArrayCommandInputParame
 export type WorkflowInputsData = {
     [key: string]: boolean | number | string | File | Directory;
 };
-export const parseWorkflowDefinition = (workflow: WorkflowResource): WorkflowResoruceDefinition => {
+export const parseWorkflowDefinition = (workflow: WorkflowResource): WorkflowResourceDefinition => {
     const definition = safeLoad(workflow.definition);
     return definition;
 };
 
-export const getWorkflowInputs = (workflowDefinition: WorkflowResoruceDefinition) => {
-    if (workflowDefinition.graph) {
-        const mainWorkflow = workflowDefinition.graph.find(item => item.class === 'Workflow' && item.id === '#main');
-        return mainWorkflow
-            ? mainWorkflow.inputs
-            : undefined;
-    } else {
-        const mainWorkflow = workflowDefinition.$graph!.find(item => item.class === 'Workflow' && item.id === '#main');
-        return mainWorkflow
-            ? mainWorkflow.inputs
-            : undefined;
-    }
+export const getWorkflowInputs = (workflowDefinition: WorkflowResourceDefinition) => {
+    const mainWorkflow = workflowDefinition.$graph!.find(item => item.class === 'Workflow' && item.id === '#main');
+    return mainWorkflow
+        ? mainWorkflow.inputs
+        : undefined;
 };
 
 export const getInputLabel = (input: CommandInputParameter) => {
diff --git a/src/store/processes/process-input-actions.ts b/src/store/processes/process-input-actions.ts
index 7ce2749c..88624d08 100644
--- a/src/store/processes/process-input-actions.ts
+++ b/src/store/processes/process-input-actions.ts
@@ -15,10 +15,10 @@ export const openProcessInputDialog = (processUuid: string) =>
         const process = getProcess(processUuid)(getState().resources);
         if (process) {
             const data: any = process;
-            if (data && data.containerRequest.mounts.varLibCwlWorkflowJson && data.containerRequest.mounts.varLibCwlWorkflowJson.content.graph.filter((a: any) => a.class === 'Workflow')[0] && data.containerRequest.mounts.varLibCwlWorkflowJson.content.graph.filter((a: any) => a.class === 'Workflow')[0].inputs.length > 0) {
+            if (data && data.containerRequest.mounts["/var/lib/cwl/workflow.json"] && data.containerRequest.mounts["/var/lib/cwl/workflow.json"].content.$graph.find((a: any) => a.class === 'Workflow' && a.id === '#main') && data.containerRequest.mounts["/var/lib/cwl/workflow.json"].content.$graph.find((a: any) => a.class === 'Workflow' && a.id === '#main').inputs.length > 0) {
                 dispatch(dialogActions.OPEN_DIALOG({ id: PROCESS_INPUT_DIALOG_NAME, data }));
             } else {
                 dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'There are no inputs in this process!', kind: SnackbarKind.ERROR }));
             }
         }
-    }; 
\ No newline at end of file
+    };
\ No newline at end of file
diff --git a/src/store/processes/processes-actions.ts b/src/store/processes/processes-actions.ts
index c6bc1418..91996dde 100644
--- a/src/store/processes/processes-actions.ts
+++ b/src/store/processes/processes-actions.ts
@@ -15,7 +15,6 @@ import { projectPanelActions } from '~/store/project-panel/project-panel-action'
 import { navigateToRunProcess } from '~/store/navigation/navigation-action';
 import { goToStep, runProcessPanelActions } from '~/store/run-process-panel/run-process-panel-actions';
 import { getResource } from '~/store/resources/resources';
-import { getInputValue } from "~/views-components/process-input-dialog/process-input-dialog";
 import { initialize } from "redux-form";
 import { RUN_PROCESS_BASIC_FORM, RunProcessBasicFormData } from "~/views/run-process-panel/run-process-basic-form";
 import { RunProcessAdvancedFormData, RUN_PROCESS_ADVANCED_FORM } from "~/views/run-process-panel/run-process-advanced-form";
@@ -86,8 +85,9 @@ export const reRunProcess = (processUuid: string, workflowUuid: string) =>
         const workflow = workflows.find(workflow => workflow.uuid === workflowUuid);
         if (workflow && process) {
             const newValues = getInputs(process);
-            process.mounts.varLibCwlWorkflowJson.content.graph[1].inputs = newValues;
-            const stringifiedDefinition = JSON.stringify(process.mounts.varLibCwlWorkflowJson.content);
+            process.mounts["/var/lib/cwl/workflow.json"].content.$graph.find(
+                (a: any) => a.id === '#main').inputs = newValues;
+            const stringifiedDefinition = JSON.stringify(process.mounts["/var/lib/cwl/workflow.json"].content);
             const newWorkflow = { ...workflow, definition: stringifiedDefinition };
 
             const basicInitialData: RunProcessBasicFormData = { name: `Copy of: ${process.name}`, description: process.description };
@@ -113,9 +113,18 @@ export const reRunProcess = (processUuid: string, workflowUuid: string) =>
     };
 
 const getInputs = (data: any) =>
-    data && data.mounts.varLibCwlWorkflowJson ? data.mounts.varLibCwlWorkflowJson.content.graph[1].inputs.map((it: any) => (
-        { type: it.type, id: it.id, label: it.label, default: getInputValue(it.id, data.mounts.varLibCwlCwlInputJson.content), doc: it.doc }
-    )) : [];
+    data && data.mounts["/var/lib/cwl/workflow.json"] ? data.mounts["/var/lib/cwl/workflow.json"].content.$graph.find(
+        (a: any) => a.id === '#main').inputs.map(
+            (it: any) => (
+                {
+                    type: it.type,
+                    id: it.id,
+                    label: it.label,
+                    default: data.mounts["/var/lib/cwl/cwl.input.json"].content[it.id],
+                    doc: it.doc
+                }
+            )
+        ) : [];
 
 export const openRemoveProcessDialog = (uuid: string) =>
     (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
diff --git a/src/views-components/process-input-dialog/process-input-dialog.tsx b/src/views-components/process-input-dialog/process-input-dialog.tsx
index edb4bc68..1650c902 100644
--- a/src/views-components/process-input-dialog/process-input-dialog.tsx
+++ b/src/views-components/process-input-dialog/process-input-dialog.tsx
@@ -32,16 +32,15 @@ export const ProcessInputDialog = withDialog(PROCESS_INPUT_DIALOG_NAME)(
 );
 
 const getInputs = (data: any) =>
-    data && data.mounts.varLibCwlWorkflowJson ? data.mounts.varLibCwlWorkflowJson.content.graph.filter((a: any) => a.class === 'Workflow')[0].inputs.map((it: any) => (
-        { type: it.type, id: it.id, label: it.label, value: getInputValue(it.id, data.mounts.varLibCwlCwlInputJson.content), disabled: true }
-    )) : [];
-
-const snakeToCamel = (s: string) => {
-    const a = s.split('/');
-    return a[1].replace(/(\_\w)/g, (m: string) => m[1].toUpperCase());
-};
-
-export const getInputValue = (id: string, data: any) => {
-    const a = snakeToCamel(id);
-    return data[a];
-};
\ No newline at end of file
+    data && data.mounts["/var/lib/cwl/workflow.json"] ? data.mounts["/var/lib/cwl/workflow.json"].content.$graph.find(
+        (a: any) => a.id === '#main').inputs.map(
+            (it: any) => (
+                {
+                    type: it.type,
+                    id: it.id,
+                    label: it.label,
+                    value: data.mounts["/var/lib/cwl/cwl.input.json"].content[it.id],
+                    disabled: true
+                }
+            )
+        ) : [];

commit 1afdf6484780b11ec66e6048ded1789b92701a0e
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Mon Aug 19 14:38:28 2019 -0300

    15407: Fixes pending model.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima at veritasgenetics.com>

diff --git a/src/models/mount-types.ts b/src/models/mount-types.ts
index 52b29499..db87db18 100644
--- a/src/models/mount-types.ts
+++ b/src/models/mount-types.ts
@@ -22,7 +22,7 @@ export type MountType =
 export interface CollectionMount {
     kind: MountKind.COLLECTION;
     uuid?: string;
-    portableDataHash?: string;
+    portable_data_hash?: string;
     path?: string;
     writable?: boolean;
 }
diff --git a/src/store/advanced-tab/advanced-tab.tsx b/src/store/advanced-tab/advanced-tab.tsx
index 3cc1dc3a..fc24ace0 100644
--- a/src/store/advanced-tab/advanced-tab.tsx
+++ b/src/store/advanced-tab/advanced-tab.tsx
@@ -413,7 +413,7 @@ const containerRequestApiResponse = (apiResponse: ContainerRequestResource) => {
 "container_count_max": ${stringify(containerCountMax)},
 "mounts": ${stringifyObject(mounts)},
 "runtime_constraints": ${stringifyObject(runtimeConstraints)},
-"container_image": "${stringify(containerImage)}",
+"container_image": ${stringify(containerImage)},
 "environment": ${stringifyObject(environment)},
 "cwd": ${stringify(cwd)},
 "command": ${stringifyObject(command)},

commit d01ef49ba6d88c0a00b0e784e73a1170436faa9f
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Fri Aug 16 15:18:20 2019 -0300

    15407: Fixes node's properties & info handling.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima at veritasgenetics.com>

diff --git a/src/models/node.ts b/src/models/node.ts
index 87238115..d0232b83 100644
--- a/src/models/node.ts
+++ b/src/models/node.ts
@@ -18,17 +18,17 @@ export interface NodeResource extends Resource {
 }
 
 export interface NodeInfo {
-    lastAction: string;
-    pingSecret: string;
-    ec2InstanceId: string;
-    slurmState?: string;
+    last_action: string;
+    ping_secret: string;
+    ec2_instance_id: string;
+    slurm_state?: string;
 }
 
 export interface NodeProperties {
-    cloudNode: CloudNode;
-    totalRamMb: number;
-    totalCpuCores: number;
-    totalScratchMb: number;
+    cloud_node: CloudNode;
+    total_ram_mb: number;
+    total_cpu_cores: number;
+    total_scratch_mb: number;
 }
 
 interface CloudNode {
diff --git a/src/views-components/compute-nodes-dialog/attributes-dialog.tsx b/src/views-components/compute-nodes-dialog/attributes-dialog.tsx
index 41ca6395..5d9946b4 100644
--- a/src/views-components/compute-nodes-dialog/attributes-dialog.tsx
+++ b/src/views-components/compute-nodes-dialog/attributes-dialog.tsx
@@ -29,7 +29,7 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     },
     grid: {
         padding: '8px 0 0 0'
-    } 
+    }
 });
 
 interface AttributesComputeNodeDialogDataProps {
@@ -81,35 +81,35 @@ const renderPrimaryInfo = (computeNode: NodeResource, classes: any) => {
 };
 
 const renderInfo = (info: NodeInfo, classes: any) => {
-    const { lastAction, pingSecret, ec2InstanceId, slurmState } = info;
+    const { last_action, ping_secret, ec2_instance_id, slurm_state } = info;
     return (
         <Grid container direction="row" spacing={16} className={classnames([classes.root, classes.grid])}>
             <Grid item xs={5}>Info - Last action</Grid>
-            <Grid item xs={7}>{lastAction || '(none)'}</Grid>
+            <Grid item xs={7}>{last_action || '(none)'}</Grid>
             <Grid item xs={5}>Info - Ping secret</Grid>
-            <Grid item xs={7}>{pingSecret || '(none)'}</Grid>
+            <Grid item xs={7}>{ping_secret || '(none)'}</Grid>
             <Grid item xs={5}>Info - ec2 instance id</Grid>
-            <Grid item xs={7}>{ec2InstanceId || '(none)'}</Grid>
+            <Grid item xs={7}>{ec2_instance_id || '(none)'}</Grid>
             <Grid item xs={5}>Info - Slurm state</Grid>
-            <Grid item xs={7}>{slurmState || '(none)'}</Grid>
+            <Grid item xs={7}>{slurm_state || '(none)'}</Grid>
         </Grid>
     );
 };
 
 const renderProperties = (properties: NodeProperties, classes: any) => {
-    const { totalRamMb, totalCpuCores, totalScratchMb, cloudNode } = properties;
+    const { total_ram_mb, total_cpu_cores, total_scratch_mb, cloud_node } = properties;
     return (
         <Grid container direction="row" spacing={16} className={classnames([classes.root, classes.grid])}>
             <Grid item xs={5}>Properties - Total ram mb</Grid>
-            <Grid item xs={7}>{totalRamMb || '(none)'}</Grid>
+            <Grid item xs={7}>{total_ram_mb || '(none)'}</Grid>
             <Grid item xs={5}>Properties - Total scratch mb</Grid>
-            <Grid item xs={7}>{totalScratchMb || '(none)'}</Grid>
+            <Grid item xs={7}>{total_scratch_mb || '(none)'}</Grid>
             <Grid item xs={5}>Properties - Total cpu cores</Grid>
-            <Grid item xs={7}>{totalCpuCores || '(none)'}</Grid>
+            <Grid item xs={7}>{total_cpu_cores || '(none)'}</Grid>
             <Grid item xs={5}>Properties - Cloud node size </Grid>
-            <Grid item xs={7}>{cloudNode ? cloudNode.size : '(none)'}</Grid>
+            <Grid item xs={7}>{cloud_node ? cloud_node.size : '(none)'}</Grid>
             <Grid item xs={5}>Properties - Cloud node price</Grid>
-            <Grid item xs={7}>{cloudNode ? cloudNode.price : '(none)'}</Grid>
+            <Grid item xs={7}>{cloud_node ? cloud_node.price : '(none)'}</Grid>
         </Grid>
     );
 };
\ No newline at end of file

commit 69e971b0d44effcf900cf830edf8592731aff2b1
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Fri Aug 16 15:07:36 2019 -0300

    15407: Fixes handling of runtime constraints and scheduling params.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima at veritasgenetics.com>

diff --git a/src/models/runtime-constraints.ts b/src/models/runtime-constraints.ts
index a780fd35..89101c6e 100644
--- a/src/models/runtime-constraints.ts
+++ b/src/models/runtime-constraints.ts
@@ -5,6 +5,6 @@
 export interface RuntimeConstraints {
     ram: number;
     vcpus: number;
-    keepCacheRam?: number;
+    keep_cache_ram?: number;
     API: boolean;
 }
diff --git a/src/models/scheduling-parameters.ts b/src/models/scheduling-parameters.ts
index 50ce4156..f2167c97 100644
--- a/src/models/scheduling-parameters.ts
+++ b/src/models/scheduling-parameters.ts
@@ -5,5 +5,5 @@
 export interface SchedulingParameters {
     partitions?: string[];
     preemptible?: boolean;
-    maxRunTime?: number;
+    max_run_time?: number;
 }
diff --git a/src/store/processes/processes-actions.ts b/src/store/processes/processes-actions.ts
index 6f1d23b0..c6bc1418 100644
--- a/src/store/processes/processes-actions.ts
+++ b/src/store/processes/processes-actions.ts
@@ -95,10 +95,10 @@ export const reRunProcess = (processUuid: string, workflowUuid: string) =>
 
             const advancedInitialData: RunProcessAdvancedFormData = {
                 output: process.outputName,
-                runtime: process.schedulingParameters.maxRunTime,
+                runtime: process.schedulingParameters.max_run_time,
                 ram: process.runtimeConstraints.ram,
                 vcpus: process.runtimeConstraints.vcpus,
-                keepCacheRam: process.runtimeConstraints.keepCacheRam,
+                keep_cache_ram: process.runtimeConstraints.keep_cache_ram,
                 api: process.runtimeConstraints.API
             };
             dispatch<any>(initialize(RUN_PROCESS_ADVANCED_FORM, advancedInitialData));
diff --git a/src/store/run-process-panel/run-process-panel-actions.ts b/src/store/run-process-panel/run-process-panel-actions.ts
index 5b082b8c..b543b160 100644
--- a/src/store/run-process-panel/run-process-panel-actions.ts
+++ b/src/store/run-process-panel/run-process-panel-actions.ts
@@ -135,7 +135,7 @@ export const runProcess = async (dispatch: Dispatch<any>, getState: () => RootSt
                 api: advancedForm[API_FIELD],
             },
             schedulingParameters: {
-                maxRunTime: advancedForm[RUNTIME_FIELD]
+                max_run_time: advancedForm[RUNTIME_FIELD]
             },
             containerImage: 'arvados/jobs',
             cwd: '/var/spool/cwl',
diff --git a/src/views/run-process-panel/run-process-advanced-form.tsx b/src/views/run-process-panel/run-process-advanced-form.tsx
index 30ff494c..d63ff30f 100644
--- a/src/views/run-process-panel/run-process-advanced-form.tsx
+++ b/src/views/run-process-panel/run-process-advanced-form.tsx
@@ -20,7 +20,7 @@ export const OUTPUT_FIELD = 'output';
 export const RUNTIME_FIELD = 'runtime';
 export const RAM_FIELD = 'ram';
 export const VCPUS_FIELD = 'vcpus';
-export const KEEP_CACHE_RAM_FIELD = 'keepCacheRam';
+export const KEEP_CACHE_RAM_FIELD = 'keep_cache_ram';
 export const API_FIELD = 'api';
 
 export interface RunProcessAdvancedFormData {
@@ -93,7 +93,7 @@ export const RunProcessAdvancedForm =
                                 parse={IntInput.parse}
                                 format={IntInput.format}
                                 type='number'
-                                validate={keepCacheRamValdation} />
+                                validate={keepCacheRamValidation} />
                         </Grid>
                         <Grid item xs={12} md={6}>
                             <Field
@@ -112,5 +112,5 @@ export const RunProcessAdvancedForm =
 
 const ramValidation = [min(0)];
 const vcpusValidation = [min(1)];
-const keepCacheRamValdation = [optional(min(0))];
+const keepCacheRamValidation = [optional(min(0))];
 const runtimeValidation = [optional(min(1))];

commit 50ab8cb746632cb442c064449503e527c4cdddbe
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Fri Aug 16 14:07:40 2019 -0300

    15407: Fixes collection versioning keys on advanced tab.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima at veritasgenetics.com>

diff --git a/src/store/advanced-tab/advanced-tab.tsx b/src/store/advanced-tab/advanced-tab.tsx
index b1f0f983..3cc1dc3a 100644
--- a/src/store/advanced-tab/advanced-tab.tsx
+++ b/src/store/advanced-tab/advanced-tab.tsx
@@ -457,9 +457,9 @@ const collectionApiResponse = (apiResponse: CollectionResource) => {
 "storage_classes_desired": ${JSON.stringify(storageClassesDesired, null, 2)},
 "storage_classes_confirmed": ${JSON.stringify(storageClassesConfirmed, null, 2)},
 "storage_classes_confirmed_at": ${stringify(storageClassesConfirmedAt)},
-"currentVersionUuid": ${stringify(currentVersionUuid)},
+"current_version_uuid": ${stringify(currentVersionUuid)},
 "version": ${version},
-"preserveVersion": ${preserveVersion}`;
+"preserve_version": ${preserveVersion}`;
 
     return <span style={{ marginLeft: '-15px' }}>{'{'} {response} {'\n'} <span style={{ marginLeft: '-15px' }}>{'}'}</span></span>;
 };

commit 692582f3b80fa8f886d4ddb37055d71b3c3db14d
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Fri Aug 16 11:24:45 2019 -0300

    15407: Restricts mapKeys' recursion to only "items" keys, to support listing.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima at veritasgenetics.com>

diff --git a/src/services/common-service/common-service.ts b/src/services/common-service/common-service.ts
index 07ff398a..212f2f4d 100644
--- a/src/services/common-service/common-service.ts
+++ b/src/services/common-service/common-service.ts
@@ -54,7 +54,7 @@ export class CommonService<T> {
                         .map(key => [key, mapFn(key)])
                         .reduce((newValue, [key, newKey]) => ({
                             ...newValue,
-                            [newKey]: CommonService.mapKeys(mapFn)(value[key])
+                            [newKey]: (key === 'items') ? CommonService.mapKeys(mapFn)(value[key]) : value[key]
                         }), {});
                 case _.isArray(value):
                     return value.map(CommonService.mapKeys(mapFn));

commit 9cc1b7cb37e9f70103b7fce3a8548f699724c18d
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Fri Aug 16 11:22:38 2019 -0300

    15407: Tests that only first level keys are mapped on resource objects.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima at veritasgenetics.com>

diff --git a/src/services/common-service/common-resource-service.test.ts b/src/services/common-service/common-resource-service.test.ts
index 5a3bae25..a6a8b9d0 100644
--- a/src/services/common-service/common-resource-service.test.ts
+++ b/src/services/common-service/common-resource-service.test.ts
@@ -60,11 +60,22 @@ describe("CommonResourceService", () => {
     it("#get", async () => {
         axiosMock
             .onGet("/resource/uuid")
-            .reply(200, { modified_at: "now" });
+            .reply(200, {
+                modified_at: "now",
+                properties: {
+                    responsible_owner_uuid: "another_owner"
+                }
+            });
 
         const commonResourceService = new CommonResourceService(axiosInstance, "resource", actions);
         const resource = await commonResourceService.get("uuid");
-        expect(resource).toEqual({ modifiedAt: "now" });
+        // Only first level keys are mapped to camel case
+        expect(resource).toEqual({
+            modifiedAt: "now",
+            properties: {
+                responsible_owner_uuid: "another_owner"
+            }
+        });
     });
 
     it("#list", async () => {
@@ -75,19 +86,26 @@ describe("CommonResourceService", () => {
                 offset: 2,
                 limit: 10,
                 items: [{
-                    modified_at: "now"
+                    modified_at: "now",
+                    properties: {
+                        is_active: true
+                    }
                 }],
                 items_available: 20
             });
 
         const commonResourceService = new CommonResourceService(axiosInstance, "resource", actions);
         const resource = await commonResourceService.list({ limit: 10, offset: 1 });
+        // First level keys are mapped to camel case inside "items" arrays
         expect(resource).toEqual({
             kind: "kind",
             offset: 2,
             limit: 10,
             items: [{
-                modifiedAt: "now"
+                modifiedAt: "now",
+                properties: {
+                    is_active: true
+                }
             }],
             itemsAvailable: 20
         });

commit f0499df0c096a1fa4c4beef3a8572dce51b1cb94
Merge: e1c5bef3 61769345
Author: Eric Biagiotti <ebiagiotti at veritasgenetics.com>
Date:   Thu Aug 1 15:11:51 2019 -0400

    Merge branch '14813-cluster-config'
    
    refs #14813
    
    Arvados-DCO-1.1-Signed-off-by: Eric Biagiotti <ebiagiotti at veritasgenetics.com>


commit e1c5bef353f132b92a1258bf4933d77fd86c3e4e
Author: Eric Biagiotti <ebiagiotti at veritasgenetics.com>
Date:   Wed Jun 12 14:41:51 2019 -0400

    15106: Fixes full text search test
    
    Arvados-DCO-1.1-Signed-off-by: Eric Biagiotti <ebiagiotti at veritasgenetics.com>

diff --git a/src/services/api/filter-builder.test.ts b/src/services/api/filter-builder.test.ts
index d9b76b1e..d9656934 100644
--- a/src/services/api/filter-builder.test.ts
+++ b/src/services/api/filter-builder.test.ts
@@ -87,6 +87,6 @@ describe("FilterBuilder", () => {
             new FilterBuilder()
                 .addFullTextSearch('my custom search')
                 .getFilters()
-        ).toEqual(`["any","@@","my:*&custom:*&search"]`);
+        ).toEqual(`["any","ilike","%my%"],["any","ilike","%custom%"],["any","ilike","%search%"]`);
     });
 });

commit 6ead1b122e52a587963383f6a15edf6dfc166ef6
Author: Eric Biagiotti <ebiagiotti at veritasgenetics.com>
Date:   Wed Jun 12 14:13:55 2019 -0400

    15106: Changes full text search to use trigram indexing
    
    Arvados-DCO-1.1-Signed-off-by: Eric Biagiotti <ebiagiotti at veritasgenetics.com>

diff --git a/src/services/api/filter-builder.ts b/src/services/api/filter-builder.ts
index e8992341..77fcef6f 100644
--- a/src/services/api/filter-builder.ts
+++ b/src/services/api/filter-builder.ts
@@ -56,10 +56,13 @@ export class FilterBuilder {
     }
 
     public addFullTextSearch(value: string) {
-        // Filter construction implementation taken from 
-        // https://dev.arvados.org/projects/arvados/repository/entry/apps/workbench/app/assets/javascripts/filterable.js
-        // https://dev.arvados.org/projects/arvados/repository/entry/apps/workbench/app/assets/javascripts/to_tsquery.js
-        return this.addCondition('any', '@@', value.replace(/[^-\w\.\/]+/g, ' ').trim().replace(/ /g, ':*&'));
+        const terms = value.trim().split(/(\s+)/);
+        terms.forEach(term => {
+            if (term !== " ") {
+                this.addCondition("any", "ilike", term, "%", "%");
+            }
+        });
+        return this;
     }
 
     public getFilters() {

commit 61769345c78e04b0f756dcd15e39fe57ddb75c80
Author: Eric Biagiotti <ebiagiotti at veritasgenetics.com>
Date:   Thu Aug 1 14:45:41 2019 -0400

    14813: Updates misleading error message
    
    The API_HOST could be undefined from config.json or the default set in the environment.
    
    Arvados-DCO-1.1-Signed-off-by: Eric Biagiotti <ebiagiotti at veritasgenetics.com>

diff --git a/src/common/config.ts b/src/common/config.ts
index 7363b95d..496b0b78 100644
--- a/src/common/config.ts
+++ b/src/common/config.ts
@@ -72,7 +72,7 @@ export const fetchConfig = () => {
         })
         .then(workbenchConfig => {
             if (workbenchConfig.API_HOST === undefined) {
-                throw new Error(`Unable to start Workbench. API_HOST is undefined in ${WORKBENCH_CONFIG_URL}.`);
+                throw new Error(`Unable to start Workbench. API_HOST is undefined in ${WORKBENCH_CONFIG_URL} or the environment.`);
             }
             return Axios.get<ClusterConfigJSON>(getClusterConfigURL(workbenchConfig.API_HOST)).then(response => {
                 const config = new Config();

commit c8c5fc13ef57812800fb6ba4553d009ba8c5c7be
Author: Eric Biagiotti <ebiagiotti at veritasgenetics.com>
Date:   Thu Aug 1 14:34:24 2019 -0400

    14813: Improves config error handling and logging
    
    Arvados-DCO-1.1-Signed-off-by: Eric Biagiotti <ebiagiotti at veritasgenetics.com>

diff --git a/README.md b/README.md
index bca04b1e..e2e14ce4 100644
--- a/README.md
+++ b/README.md
@@ -46,11 +46,17 @@ Currently this configuration schema is supported:
 }
 ```
 
+#### API_HOST
+
+The Arvados base URL. 
+
+The `REACT_APP_ARVADOS_API_HOST` environment variable can be used to set the default URL if the run time configuration is unreachable.
+
 #### VOCABULARY_URL
 Local path, or any URL that allows cross-origin requests. See 
 [Vocabulary JSON file example](public/vocabulary-example.json).
 
-To use the URL defined in the Arvados cluster configuration, remove the entire VOCABULARY_URL entry from the runtime configuration. Found in `/config.json` by default.
+To use the URL defined in the Arvados cluster configuration, remove the entire `VOCABULARY_URL` entry from the runtime configuration. Found in `/config.json` by default.
 
 ### FILE_VIEWERS_CONFIG_URL
 Local path, or any URL that allows cross-origin requests. See:
@@ -59,7 +65,7 @@ Local path, or any URL that allows cross-origin requests. See:
 
 [File viewers config scheme](src/models/file-viewers-config.ts)
 
-To use the URL defined in the Arvados cluster configuration, remove the entire FILE_VIEWERS_CONFIG_URL entry from the runtime configuration. Found in `/config.json` by default.
+To use the URL defined in the Arvados cluster configuration, remove the entire `FILE_VIEWERS_CONFIG_URL` entry from the runtime configuration. Found in `/config.json` by default.
 
 ### Licensing
 
diff --git a/src/common/config.ts b/src/common/config.ts
index 36fda624..7363b95d 100644
--- a/src/common/config.ts
+++ b/src/common/config.ts
@@ -66,14 +66,17 @@ export const fetchConfig = () => {
     return Axios
         .get<WorkbenchConfig>(WORKBENCH_CONFIG_URL + "?nocache=" + (new Date()).getTime())
         .then(response => response.data)
-        .catch(() => Promise.resolve(getDefaultConfig()))
-        .then(workbenchConfig => Axios
-            .get<ClusterConfigJSON>(getClusterConfigURL(workbenchConfig.API_HOST))
-            .then(response => {
-
+        .catch(() => {
+            console.warn(`There was an exception getting the Workbench config file at ${WORKBENCH_CONFIG_URL}. Using defaults instead.`);
+            return Promise.resolve(getDefaultConfig());
+        })
+        .then(workbenchConfig => {
+            if (workbenchConfig.API_HOST === undefined) {
+                throw new Error(`Unable to start Workbench. API_HOST is undefined in ${WORKBENCH_CONFIG_URL}.`);
+            }
+            return Axios.get<ClusterConfigJSON>(getClusterConfigURL(workbenchConfig.API_HOST)).then(response => {
                 const config = new Config();
                 const clusterConfigJSON = response.data;
-                const docsite = clusterConfigJSON.Workbench.ArvadosDocsite;
                 const warnLocalConfig = (varName: string) => console.warn(
                     `A value for ${varName} was found in ${WORKBENCH_CONFIG_URL}. To use the Arvados centralized configuration instead, \
 remove the entire ${varName} entry from ${WORKBENCH_CONFIG_URL}`);
@@ -111,8 +114,8 @@ remove the entire ${varName} entry from ${WORKBENCH_CONFIG_URL}`);
                 mapRemoteHosts(clusterConfigJSON, config);
 
                 return { config, apiHost: workbenchConfig.API_HOST };
-            })
-        );
+            });
+        });
 };
 
 // Maps remote cluster hosts and removes the default RemoteCluster entry
@@ -135,11 +138,22 @@ export const mockConfig = (config: Partial<Config>): Config => ({
     fileViewersConfigUrl: ""
 });
 
-const getDefaultConfig = (): WorkbenchConfig => ({
-    API_HOST: process.env.REACT_APP_ARVADOS_API_HOST || "",
-    VOCABULARY_URL: undefined,
-    FILE_VIEWERS_CONFIG_URL: undefined,
-});
+const getDefaultConfig = (): WorkbenchConfig => {
+    let apiHost = "";
+    const envHost = process.env.REACT_APP_ARVADOS_API_HOST;
+    if (envHost !== undefined) {
+        console.warn(`Using default API host ${envHost}.`);
+        apiHost = envHost;
+    }
+    else {
+        console.warn(`No API host was found in the environment. Workbench may not be able to communicate with Arvados components.`);
+    }
+    return {
+        API_HOST: apiHost,
+        VOCABULARY_URL: undefined,
+        FILE_VIEWERS_CONFIG_URL: undefined,
+    };
+};
 
 export const ARVADOS_API_PATH = "arvados/v1";
 export const CLUSTER_CONFIG_URL = "arvados/v1/config";

commit de9713360b5bc04dc3586b332bc98db5d97650a5
Author: Eric Biagiotti <ebiagiotti at veritasgenetics.com>
Date:   Wed Jul 31 10:49:14 2019 -0400

    14813: Fix to use cluster config when local config values aren't defined
    
    Also updates readme to describe how to use the cluster config and softens the warning language.
    
    Arvados-DCO-1.1-Signed-off-by: Eric Biagiotti <ebiagiotti at veritasgenetics.com>

diff --git a/README.md b/README.md
index 425d1787..bca04b1e 100644
--- a/README.md
+++ b/README.md
@@ -50,6 +50,8 @@ Currently this configuration schema is supported:
 Local path, or any URL that allows cross-origin requests. See 
 [Vocabulary JSON file example](public/vocabulary-example.json).
 
+To use the URL defined in the Arvados cluster configuration, remove the entire VOCABULARY_URL entry from the runtime configuration. Found in `/config.json` by default.
+
 ### FILE_VIEWERS_CONFIG_URL
 Local path, or any URL that allows cross-origin requests. See:
 
@@ -57,6 +59,8 @@ Local path, or any URL that allows cross-origin requests. See:
 
 [File viewers config scheme](src/models/file-viewers-config.ts)
 
+To use the URL defined in the Arvados cluster configuration, remove the entire FILE_VIEWERS_CONFIG_URL entry from the runtime configuration. Found in `/config.json` by default.
+
 ### Licensing
 
 Arvados is Free Software. See COPYING for information about Arvados Free
diff --git a/src/common/config.ts b/src/common/config.ts
index 975928e3..36fda624 100644
--- a/src/common/config.ts
+++ b/src/common/config.ts
@@ -8,13 +8,13 @@ export const WORKBENCH_CONFIG_URL = process.env.REACT_APP_ARVADOS_CONFIG_URL ||
 
 interface WorkbenchConfig {
     API_HOST: string;
-    VOCABULARY_URL: string;
-    FILE_VIEWERS_CONFIG_URL: string;
+    VOCABULARY_URL?: string;
+    FILE_VIEWERS_CONFIG_URL?: string;
 }
 
 export interface ClusterConfigJSON {
     ClusterID: string;
-    RemoteClusters:  {
+    RemoteClusters: {
         [key: string]: {
             ActivateUsers: boolean
             Host: string
@@ -74,16 +74,16 @@ export const fetchConfig = () => {
                 const config = new Config();
                 const clusterConfigJSON = response.data;
                 const docsite = clusterConfigJSON.Workbench.ArvadosDocsite;
-                const warnDeprecation = (varName: string) => console.warn(
-`A value for ${varName} was found in ${WORKBENCH_CONFIG_URL}. This configuration is deprecated. \
-Please use the centralized configuration instead. See more at ${docsite}admin/config-migration.html`);
+                const warnLocalConfig = (varName: string) => console.warn(
+                    `A value for ${varName} was found in ${WORKBENCH_CONFIG_URL}. To use the Arvados centralized configuration instead, \
+remove the entire ${varName} entry from ${WORKBENCH_CONFIG_URL}`);
 
                 // Check if the workbench config has an entry for vocabulary and file viewer URLs
                 // If so, use these values (even if it is an empty string), but print a console warning.
-                // Otherwise, use the cluster config or default values.
+                // Otherwise, use the cluster config.
                 let fileViewerConfigUrl;
                 if (workbenchConfig.FILE_VIEWERS_CONFIG_URL !== undefined) {
-                    warnDeprecation("FILE_VIEWERS_CONFIG_URL");
+                    warnLocalConfig("FILE_VIEWERS_CONFIG_URL");
                     fileViewerConfigUrl = workbenchConfig.FILE_VIEWERS_CONFIG_URL;
                 }
                 else {
@@ -93,7 +93,7 @@ Please use the centralized configuration instead. See more at ${docsite}admin/co
 
                 let vocabularyUrl;
                 if (workbenchConfig.VOCABULARY_URL !== undefined) {
-                    warnDeprecation("VOCABULARY_URL");
+                    warnLocalConfig("VOCABULARY_URL");
                     vocabularyUrl = workbenchConfig.VOCABULARY_URL;
                 }
                 else {
@@ -107,7 +107,7 @@ Please use the centralized configuration instead. See more at ${docsite}admin/co
                 config.websocketUrl = clusterConfigJSON.Services.Websocket.ExternalURL;
                 config.workbench2Url = clusterConfigJSON.Services.Workbench2.ExternalURL;
                 config.workbenchUrl = clusterConfigJSON.Services.Workbench1.ExternalURL;
-                config.keepWebServiceUrl =  clusterConfigJSON.Services.WebDAV.ExternalURL;
+                config.keepWebServiceUrl = clusterConfigJSON.Services.WebDAV.ExternalURL;
                 mapRemoteHosts(clusterConfigJSON, config);
 
                 return { config, apiHost: workbenchConfig.API_HOST };
@@ -118,7 +118,7 @@ Please use the centralized configuration instead. See more at ${docsite}admin/co
 // Maps remote cluster hosts and removes the default RemoteCluster entry
 export const mapRemoteHosts = (clusterConfigJSON: ClusterConfigJSON, config: Config) => {
     config.remoteHosts = {};
-    Object.keys(clusterConfigJSON.RemoteClusters).forEach (k => { config.remoteHosts[k] = clusterConfigJSON.RemoteClusters[k].Host; });
+    Object.keys(clusterConfigJSON.RemoteClusters).forEach(k => { config.remoteHosts[k] = clusterConfigJSON.RemoteClusters[k].Host; });
     delete config.remoteHosts["*"];
 };
 
@@ -137,8 +137,8 @@ export const mockConfig = (config: Partial<Config>): Config => ({
 
 const getDefaultConfig = (): WorkbenchConfig => ({
     API_HOST: process.env.REACT_APP_ARVADOS_API_HOST || "",
-    VOCABULARY_URL: "",
-    FILE_VIEWERS_CONFIG_URL: "",
+    VOCABULARY_URL: undefined,
+    FILE_VIEWERS_CONFIG_URL: undefined,
 });
 
 export const ARVADOS_API_PATH = "arvados/v1";

commit 9dba704e6e39056af0ec49a64cb2151b2f4321d0
Author: Eric Biagiotti <ebiagiotti at veritasgenetics.com>
Date:   Tue Jul 30 17:01:17 2019 -0400

    14813: Fixes remote cluster config state
    
    Arvados-DCO-1.1-Signed-off-by: Eric Biagiotti <ebiagiotti at veritasgenetics.com>

diff --git a/src/common/config.ts b/src/common/config.ts
index 044d7aa9..975928e3 100644
--- a/src/common/config.ts
+++ b/src/common/config.ts
@@ -110,8 +110,6 @@ Please use the centralized configuration instead. See more at ${docsite}admin/co
                 config.keepWebServiceUrl =  clusterConfigJSON.Services.WebDAV.ExternalURL;
                 mapRemoteHosts(clusterConfigJSON, config);
 
-                console.log(config);
-
                 return { config, apiHost: workbenchConfig.API_HOST };
             })
         );
diff --git a/src/store/auth/auth-action.ts b/src/store/auth/auth-action.ts
index 6356eaa8..b889adf5 100644
--- a/src/store/auth/auth-action.ts
+++ b/src/store/auth/auth-action.ts
@@ -91,6 +91,7 @@ const init = (config: Config) => (dispatch: Dispatch, getState: () => RootState,
         Axios.get<ClusterConfigJSON>(getClusterConfigURL(config.remoteHosts[k]))
             .then(response => {
                 const remoteConfig = new Config();
+                remoteConfig.uuidPrefix = response.data.ClusterID;
                 remoteConfig.workbench2Url = response.data.Services.Workbench2.ExternalURL;
                 mapRemoteHosts(response.data, remoteConfig);
                 dispatch(authActions.REMOTE_CLUSTER_CONFIG({ config: remoteConfig}));

commit 76c84f182253b2b10b570d669589fa576dd8ba81
Author: Eric Biagiotti <ebiagiotti at veritasgenetics.com>
Date:   Tue Jul 30 14:40:42 2019 -0400

    14813: wb2 uses cluster config
    
    Also updates auth-action to get remote cluster info from the remotes cluster config, instead of its discovery doc
    
    Arvados-DCO-1.1-Signed-off-by: Eric Biagiotti <ebiagiotti at veritasgenetics.com>

diff --git a/src/common/config.ts b/src/common/config.ts
index 71b7774c..044d7aa9 100644
--- a/src/common/config.ts
+++ b/src/common/config.ts
@@ -4,137 +4,145 @@
 
 import Axios from "axios";
 
-export const CONFIG_URL = process.env.REACT_APP_ARVADOS_CONFIG_URL || "/config.json";
+export const WORKBENCH_CONFIG_URL = process.env.REACT_APP_ARVADOS_CONFIG_URL || "/config.json";
 
-export interface Config {
-    auth: {};
-    basePath: string;
+interface WorkbenchConfig {
+    API_HOST: string;
+    VOCABULARY_URL: string;
+    FILE_VIEWERS_CONFIG_URL: string;
+}
+
+export interface ClusterConfigJSON {
+    ClusterID: string;
+    RemoteClusters:  {
+        [key: string]: {
+            ActivateUsers: boolean
+            Host: string
+            Insecure: boolean
+            Proxy: boolean
+            Scheme: string
+        }
+    };
+    Services: {
+        Controller: {
+            ExternalURL: string
+        }
+        Workbench1: {
+            ExternalURL: string
+        }
+        Workbench2: {
+            ExternalURL: string
+        }
+        Websocket: {
+            ExternalURL: string
+        }
+        WebDAV: {
+            ExternalURL: string
+        }
+    };
+    Workbench: {
+        ArvadosDocsite: string;
+        VocabularyURL: string;
+        FileViewersConfigURL: string;
+    };
+}
+
+export class Config {
     baseUrl: string;
-    batchPath: string;
-    blobSignatureTtl: number;
-    crunchLimitLogBytesPerJob: number;
-    crunchLogBytesPerEvent: number;
-    crunchLogPartialLineThrottlePeriod: number;
-    crunchLogSecondsBetweenEvents: number;
-    crunchLogThrottleBytes: number;
-    crunchLogThrottleLines: number;
-    crunchLogThrottlePeriod: number;
-    defaultCollectionReplication: number;
-    defaultTrashLifetime: number;
-    description: string;
-    discoveryVersion: string;
-    dockerImageFormats: string[];
-    documentationLink: string;
-    generatedAt: string;
-    gitUrl: string;
-    id: string;
     keepWebServiceUrl: string;
-    kind: string;
-    maxRequestSize: number;
-    name: string;
-    packageVersion: string;
-    parameters: {};
-    protocol: string;
     remoteHosts: {
         [key: string]: string
     };
-    remoteHostsViaDNS: boolean;
-    resources: {};
-    revision: string;
     rootUrl: string;
-    schemas: {};
-    servicePath: string;
-    sourceVersion: string;
-    source_version: string;
-    title: string;
     uuidPrefix: string;
-    version: string;
     websocketUrl: string;
     workbenchUrl: string;
-    workbench2Url?: string;
+    workbench2Url: string;
     vocabularyUrl: string;
     fileViewersConfigUrl: string;
 }
 
 export const fetchConfig = () => {
     return Axios
-        .get<ConfigJSON>(CONFIG_URL + "?nocache=" + (new Date()).getTime())
+        .get<WorkbenchConfig>(WORKBENCH_CONFIG_URL + "?nocache=" + (new Date()).getTime())
         .then(response => response.data)
         .catch(() => Promise.resolve(getDefaultConfig()))
-        .then(config => Axios
-            .get<Config>(getDiscoveryURL(config.API_HOST))
-            .then(response => ({
-                // TODO: After tests delete `|| '/vocabulary-example.json'`
-                // TODO: After tests delete `|| '/file-viewers-example.json'`
-                config: {
-                    ...response.data,
-                    vocabularyUrl: config.VOCABULARY_URL || '/vocabulary-example.json',
-                    fileViewersConfigUrl: config.FILE_VIEWERS_CONFIG_URL || '/file-viewers-example.json'
-                },
-                apiHost: config.API_HOST,
-            })));
+        .then(workbenchConfig => Axios
+            .get<ClusterConfigJSON>(getClusterConfigURL(workbenchConfig.API_HOST))
+            .then(response => {
+
+                const config = new Config();
+                const clusterConfigJSON = response.data;
+                const docsite = clusterConfigJSON.Workbench.ArvadosDocsite;
+                const warnDeprecation = (varName: string) => console.warn(
+`A value for ${varName} was found in ${WORKBENCH_CONFIG_URL}. This configuration is deprecated. \
+Please use the centralized configuration instead. See more at ${docsite}admin/config-migration.html`);
 
+                // Check if the workbench config has an entry for vocabulary and file viewer URLs
+                // If so, use these values (even if it is an empty string), but print a console warning.
+                // Otherwise, use the cluster config or default values.
+                let fileViewerConfigUrl;
+                if (workbenchConfig.FILE_VIEWERS_CONFIG_URL !== undefined) {
+                    warnDeprecation("FILE_VIEWERS_CONFIG_URL");
+                    fileViewerConfigUrl = workbenchConfig.FILE_VIEWERS_CONFIG_URL;
+                }
+                else {
+                    fileViewerConfigUrl = clusterConfigJSON.Workbench.FileViewersConfigURL || "/file-viewers-example.json";
+                }
+                config.fileViewersConfigUrl = fileViewerConfigUrl;
+
+                let vocabularyUrl;
+                if (workbenchConfig.VOCABULARY_URL !== undefined) {
+                    warnDeprecation("VOCABULARY_URL");
+                    vocabularyUrl = workbenchConfig.VOCABULARY_URL;
+                }
+                else {
+                    vocabularyUrl = clusterConfigJSON.Workbench.VocabularyURL || "/vocabulary-example.json";
+                }
+                config.vocabularyUrl = vocabularyUrl;
+
+                config.rootUrl = clusterConfigJSON.Services.Controller.ExternalURL;
+                config.baseUrl = `${config.rootUrl}/${ARVADOS_API_PATH}`;
+                config.uuidPrefix = clusterConfigJSON.ClusterID;
+                config.websocketUrl = clusterConfigJSON.Services.Websocket.ExternalURL;
+                config.workbench2Url = clusterConfigJSON.Services.Workbench2.ExternalURL;
+                config.workbenchUrl = clusterConfigJSON.Services.Workbench1.ExternalURL;
+                config.keepWebServiceUrl =  clusterConfigJSON.Services.WebDAV.ExternalURL;
+                mapRemoteHosts(clusterConfigJSON, config);
+
+                console.log(config);
+
+                return { config, apiHost: workbenchConfig.API_HOST };
+            })
+        );
+};
+
+// Maps remote cluster hosts and removes the default RemoteCluster entry
+export const mapRemoteHosts = (clusterConfigJSON: ClusterConfigJSON, config: Config) => {
+    config.remoteHosts = {};
+    Object.keys(clusterConfigJSON.RemoteClusters).forEach (k => { config.remoteHosts[k] = clusterConfigJSON.RemoteClusters[k].Host; });
+    delete config.remoteHosts["*"];
 };
 
 export const mockConfig = (config: Partial<Config>): Config => ({
-    auth: {},
-    basePath: '',
-    baseUrl: '',
-    batchPath: '',
-    blobSignatureTtl: 0,
-    crunchLimitLogBytesPerJob: 0,
-    crunchLogBytesPerEvent: 0,
-    crunchLogPartialLineThrottlePeriod: 0,
-    crunchLogSecondsBetweenEvents: 0,
-    crunchLogThrottleBytes: 0,
-    crunchLogThrottleLines: 0,
-    crunchLogThrottlePeriod: 0,
-    defaultCollectionReplication: 0,
-    defaultTrashLifetime: 0,
-    description: '',
-    discoveryVersion: '',
-    dockerImageFormats: [],
-    documentationLink: '',
-    generatedAt: '',
-    gitUrl: '',
-    id: '',
-    keepWebServiceUrl: '',
-    kind: '',
-    maxRequestSize: 0,
-    name: '',
-    packageVersion: '',
-    parameters: {},
-    protocol: '',
+    baseUrl: "",
+    keepWebServiceUrl: "",
     remoteHosts: {},
-    remoteHostsViaDNS: false,
-    resources: {},
-    revision: '',
-    rootUrl: '',
-    schemas: {},
-    servicePath: '',
-    sourceVersion: '',
-    source_version: '',
-    title: '',
-    uuidPrefix: '',
-    version: '',
-    websocketUrl: '',
-    workbenchUrl: '',
-    vocabularyUrl: '',
-    fileViewersConfigUrl: '',
-    ...config
+    rootUrl: "",
+    uuidPrefix: "",
+    websocketUrl: "",
+    workbenchUrl: "",
+    workbench2Url: "",
+    vocabularyUrl: "",
+    fileViewersConfigUrl: ""
 });
 
-interface ConfigJSON {
-    API_HOST: string;
-    VOCABULARY_URL: string;
-    FILE_VIEWERS_CONFIG_URL: string;
-}
-
-const getDefaultConfig = (): ConfigJSON => ({
+const getDefaultConfig = (): WorkbenchConfig => ({
     API_HOST: process.env.REACT_APP_ARVADOS_API_HOST || "",
     VOCABULARY_URL: "",
     FILE_VIEWERS_CONFIG_URL: "",
 });
 
-export const DISCOVERY_URL = 'discovery/v1/apis/arvados/v1/rest';
-export const getDiscoveryURL = (apiHost: string) => `${window.location.protocol}//${apiHost}/${DISCOVERY_URL}?nocache=${(new Date()).getTime()}`;
+export const ARVADOS_API_PATH = "arvados/v1";
+export const CLUSTER_CONFIG_URL = "arvados/v1/config";
+export const getClusterConfigURL = (apiHost: string) => `${window.location.protocol}//${apiHost}/${CLUSTER_CONFIG_URL}?nocache=${(new Date()).getTime()}`;
\ No newline at end of file
diff --git a/src/store/auth/auth-action-session.ts b/src/store/auth/auth-action-session.ts
index b889e9cf..ca2e2326 100644
--- a/src/store/auth/auth-action-session.ts
+++ b/src/store/auth/auth-action-session.ts
@@ -9,7 +9,7 @@ import { ServiceRepository } from "~/services/services";
 import Axios from "axios";
 import { getUserFullname, User } from "~/models/user";
 import { authActions } from "~/store/auth/auth-action";
-import { Config, DISCOVERY_URL } from "~/common/config";
+import { Config, ClusterConfigJSON, CLUSTER_CONFIG_URL, ARVADOS_API_PATH } from "~/common/config";
 import { Session, SessionStatus } from "~/models/session";
 import { progressIndicatorActions } from "~/store/progress-indicator/progress-indicator-actions";
 import { AuthService, UserDetailsResponse } from "~/services/auth-service/auth-service";
@@ -24,8 +24,8 @@ const getRemoteHostBaseUrl = async (remoteHost: string): Promise<string | null>
     let baseUrl: string | null = null;
 
     try {
-        const resp = await Axios.get<Config>(`${origin}/${DISCOVERY_URL}`);
-        baseUrl = resp.data.baseUrl;
+        const resp = await Axios.get<ClusterConfigJSON>(`${origin}/${CLUSTER_CONFIG_URL}`);
+        baseUrl = `${resp.data.Services.Controller.ExternalURL}/${ARVADOS_API_PATH}`;
     } catch (err) {
         try {
             const resp = await Axios.get<any>(`${origin}/status.json`);
diff --git a/src/store/auth/auth-action.ts b/src/store/auth/auth-action.ts
index 1d1ad18c..6356eaa8 100644
--- a/src/store/auth/auth-action.ts
+++ b/src/store/auth/auth-action.ts
@@ -10,7 +10,7 @@ import { ServiceRepository } from "~/services/services";
 import { SshKeyResource } from '~/models/ssh-key';
 import { User, UserResource } from "~/models/user";
 import { Session } from "~/models/session";
-import { getDiscoveryURL, Config } from '~/common/config';
+import { getClusterConfigURL, Config, ClusterConfigJSON, mapRemoteHosts } from '~/common/config';
 import { initSessions } from "~/store/auth/auth-action-session";
 import { cancelLinking } from '~/store/link-account-panel/link-account-panel-actions';
 import { matchTokenRoute, matchFedTokenRoute } from '~/routes/routes';
@@ -88,8 +88,13 @@ const init = (config: Config) => (dispatch: Dispatch, getState: () => RootState,
         });
     }
     Object.keys(config.remoteHosts).map((k) => {
-        Axios.get<Config>(getDiscoveryURL(config.remoteHosts[k]))
-            .then(response => dispatch(authActions.REMOTE_CLUSTER_CONFIG({ config: response.data })));
+        Axios.get<ClusterConfigJSON>(getClusterConfigURL(config.remoteHosts[k]))
+            .then(response => {
+                const remoteConfig = new Config();
+                remoteConfig.workbench2Url = response.data.Services.Workbench2.ExternalURL;
+                mapRemoteHosts(response.data, remoteConfig);
+                dispatch(authActions.REMOTE_CLUSTER_CONFIG({ config: remoteConfig}));
+            });
     });
 };
 

commit fea9aae78f6033ba5cc5b18f9350eecb7dabe9b1
Merge: 66eb2fa8 9f12beba
Author: Eric Biagiotti <ebiagiotti at veritasgenetics.com>
Date:   Mon Jul 22 16:13:27 2019 -0400

    Merge remote-tracking branch 'origin/15106-trgm-text-search'
    
    refs #15106
    
    Arvados-DCO-1.1-Signed-off-by: Eric Biagiotti <ebiagiotti at veritasgenetics.com>


commit 66eb2fa83ea055c5214e6e3d2d5d66b9c1173dbe
Merge: 06fc8f6a 24e864bc
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Tue Jun 25 16:00:07 2019 -0300

    Merge branch '14874-properties-editor-error-handling'
    Refs #14874
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima at veritasgenetics.com>


commit 24e864bc823b27ac27c39e9e5879987924330007
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Mon Jun 17 19:38:39 2019 -0300

    14874: Shows error message on properties update.
    
    Also, don't send the entire object when doing API calls.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima at veritasgenetics.com>

diff --git a/src/store/collection-panel/collection-panel-action.ts b/src/store/collection-panel/collection-panel-action.ts
index b1dd8389..cb28fcea 100644
--- a/src/store/collection-panel/collection-panel-action.ts
+++ b/src/store/collection-panel/collection-panel-action.ts
@@ -45,16 +45,19 @@ export const createCollectionTag = (data: TagProperty) =>
         const uuid = item ? item.uuid : '';
         try {
             if (item) {
+                const d: Partial<CollectionResource> = {
+                    properties: JSON.parse(JSON.stringify(item.properties))
+                };
+                d.properties[data.key] = data.value;
+                const updatedCollection = await services.collectionService.update(uuid, d);
                 item.properties[data.key] = data.value;
-                const version = 'version';
-                delete item[version];
-                const updatedCollection = await services.collectionService.update(uuid, item);
                 dispatch(resourcesActions.SET_RESOURCES([updatedCollection]));
                 dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Tag has been successfully added.", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
                 return updatedCollection;
             }
             return;
         } catch (e) {
+            dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.errors[0], hideDuration: 2000, kind: SnackbarKind.ERROR }));
             return;
         }
     };
@@ -65,7 +68,7 @@ export const navigateToProcess = (uuid: string) =>
             await services.containerRequestService.get(uuid);
             dispatch<any>(navigateTo(uuid));
         } catch {
-            dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'This process does not exists!', hideDuration: 2000, kind: SnackbarKind.ERROR }));
+            dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'This process does not exist!', hideDuration: 2000, kind: SnackbarKind.ERROR }));
         }
     };
 
@@ -75,14 +78,19 @@ export const deleteCollectionTag = (key: string) =>
         const uuid = item ? item.uuid : '';
         try {
             if (item) {
+                const data: Partial<CollectionResource> = {
+                    properties: JSON.parse(JSON.stringify(item.properties))
+                };
+                delete data.properties[key];
+                const updatedCollection = await services.collectionService.update(uuid, data);
                 delete item.properties[key];
-                const updatedCollection = await services.collectionService.update(uuid, item);
                 dispatch(resourcesActions.SET_RESOURCES([updatedCollection]));
                 dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Tag has been successfully deleted.", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
                 return updatedCollection;
             }
             return;
         } catch (e) {
+            dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.errors[0], hideDuration: 2000, kind: SnackbarKind.ERROR }));
             return;
         }
     };

commit 9f12beba0400015a833e2719721bf3369f4f2d94
Author: Eric Biagiotti <ebiagiotti at veritasgenetics.com>
Date:   Wed Jun 12 14:41:51 2019 -0400

    15106: Fixes full text search test
    
    Arvados-DCO-1.1-Signed-off-by: Eric Biagiotti <ebiagiotti at veritasgenetics.com>

diff --git a/src/services/api/filter-builder.test.ts b/src/services/api/filter-builder.test.ts
index d9b76b1e..d9656934 100644
--- a/src/services/api/filter-builder.test.ts
+++ b/src/services/api/filter-builder.test.ts
@@ -87,6 +87,6 @@ describe("FilterBuilder", () => {
             new FilterBuilder()
                 .addFullTextSearch('my custom search')
                 .getFilters()
-        ).toEqual(`["any","@@","my:*&custom:*&search"]`);
+        ).toEqual(`["any","ilike","%my%"],["any","ilike","%custom%"],["any","ilike","%search%"]`);
     });
 });

commit b545b17633d2d37242a39a2d1b474e3206d44e41
Author: Eric Biagiotti <ebiagiotti at veritasgenetics.com>
Date:   Wed Jun 12 14:13:55 2019 -0400

    15106: Changes full text search to use trigram indexing
    
    Arvados-DCO-1.1-Signed-off-by: Eric Biagiotti <ebiagiotti at veritasgenetics.com>

diff --git a/src/services/api/filter-builder.ts b/src/services/api/filter-builder.ts
index e8992341..77fcef6f 100644
--- a/src/services/api/filter-builder.ts
+++ b/src/services/api/filter-builder.ts
@@ -56,10 +56,13 @@ export class FilterBuilder {
     }
 
     public addFullTextSearch(value: string) {
-        // Filter construction implementation taken from 
-        // https://dev.arvados.org/projects/arvados/repository/entry/apps/workbench/app/assets/javascripts/filterable.js
-        // https://dev.arvados.org/projects/arvados/repository/entry/apps/workbench/app/assets/javascripts/to_tsquery.js
-        return this.addCondition('any', '@@', value.replace(/[^-\w\.\/]+/g, ' ').trim().replace(/ /g, ':*&'));
+        const terms = value.trim().split(/(\s+)/);
+        terms.forEach(term => {
+            if (term !== " ") {
+                this.addCondition("any", "ilike", term, "%", "%");
+            }
+        });
+        return this;
     }
 
     public getFilters() {

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list