[ARVADOS-WORKBENCH2] created: 1.2.0-219-gde93a71

Git user git at public.curoverse.com
Thu Aug 30 08:38:40 EDT 2018


        at  de93a71c82562f2fdedfd485c5d0164651300140 (commit)


commit de93a71c82562f2fdedfd485c5d0164651300140
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Thu Aug 30 14:38:30 2018 +0200

    Create websocket service and enable live process updates
    
    Feature #14099
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --git a/src/common/api/common-resource-service.ts b/src/common/api/common-resource-service.ts
index 2c9bfb5..8c4b65d 100644
--- a/src/common/api/common-resource-service.ts
+++ b/src/common/api/common-resource-service.ts
@@ -38,7 +38,7 @@ export enum CommonResourceServiceError {
 
 export class CommonResourceService<T extends Resource> {
 
-    static mapResponseKeys = (response: any): Promise<any> =>
+    static mapResponseKeys = (response: { data: any }): Promise<any> =>
         CommonResourceService.mapKeys(_.camelCase)(response.data)
 
     static mapKeys = (mapFn: (key: string) => string) =>
diff --git a/src/index.tsx b/src/index.tsx
index d3115a6..20d2c1e 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -31,6 +31,9 @@ import { processActionSet } from './views-components/context-menu/action-sets/pr
 import { addRouteChangeHandlers } from './routes/routes';
 import { loadWorkbench } from './store/workbench/workbench-actions';
 import { Routes } from '~/routes/routes';
+import { ServiceRepository } from '~/services/services';
+import { initWebSocket } from '~/websocket/websocket';
+import { Config } from '~/common/config';
 
 const getBuildNumber = () => "BN-" + (process.env.REACT_APP_BUILD_NUMBER || "dev");
 const getGitCommit = () => "GIT-" + (process.env.REACT_APP_GIT_COMMIT || "latest").substr(0, 7);
@@ -56,8 +59,7 @@ fetchConfig()
         const services = createServices(config);
         const store = configureStore(history, services);
 
-        store.subscribe(initListener(history, store));
-
+        store.subscribe(initListener(history, store, services, config));
         store.dispatch(initAuth());
 
         const TokenComponent = (props: any) => <ApiToken authService={services.authService} {...props} />;
@@ -83,12 +85,13 @@ fetchConfig()
 
     });
 
-const initListener = (history: History, store: RootStore) => {
+const initListener = (history: History, store: RootStore, services: ServiceRepository, config: Config) => {
     let initialized = false;
     return async () => {
         const { router, auth } = store.getState();
         if (router.location && auth.user && !initialized) {
             initialized = true;
+            initWebSocket(config, services.authService, store);
             await store.dispatch(loadWorkbench());
             addRouteChangeHandlers(history, store);
         }
diff --git a/src/websocket/resource-event-message.ts b/src/websocket/resource-event-message.ts
new file mode 100644
index 0000000..274b2e1
--- /dev/null
+++ b/src/websocket/resource-event-message.ts
@@ -0,0 +1,23 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+export enum ResourceEventMessageType {
+    CREATE = 'create',
+    UPDATE = 'update',
+    HOTSTAT = 'hotstat',
+    CRUNCH_RUN = 'crunch-run',
+    NODE_INFO = 'node-info',
+}
+
+export interface ResourceEventMessage {
+    eventAt: string;
+    eventType: ResourceEventMessageType;
+    id: string;
+    msgID: string;
+    objectKind: string;
+    objectOwnerUuid: string;
+    objectUuid: string;
+    properties: {};
+    uuid: string;
+}
diff --git a/src/websocket/websocket-service.ts b/src/websocket/websocket-service.ts
new file mode 100644
index 0000000..77c1fd3
--- /dev/null
+++ b/src/websocket/websocket-service.ts
@@ -0,0 +1,47 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { AuthService } from '~/services/auth-service/auth-service';
+import { ResourceEventMessage } from './resource-event-message';
+import { CommonResourceService } from '~/common/api/common-resource-service';
+import { camelCase } from 'lodash';
+
+type MessageListener = (message: ResourceEventMessage) => void;
+
+export class WebSocketService {
+    private ws: WebSocket;
+    private messageListener: MessageListener;
+
+    constructor(private url: string, private authService: AuthService) { }
+
+    connect() {
+        if (this.ws) {
+            this.ws.close();
+        }
+        this.ws = new WebSocket(this.getUrl());
+        this.ws.addEventListener('message', this.handleMessage);
+        this.ws.addEventListener('open', this.handleOpen);
+    }
+
+    setMessageListener = (listener: MessageListener) => {
+        this.messageListener = listener;
+    }
+
+    private getUrl() {
+        return `${this.url}?api_token=${this.authService.getApiToken()}`;
+    }
+
+    private handleMessage = (event: MessageEvent) => {
+        if (this.messageListener) {
+            const data = JSON.parse(event.data);
+            const message = CommonResourceService.mapKeys(camelCase)(data);
+            this.messageListener(message);
+        }
+    }
+
+    private handleOpen = () => {
+        this.ws.send('{"method":"subscribe"}');
+    }
+
+}
diff --git a/src/websocket/websocket.ts b/src/websocket/websocket.ts
new file mode 100644
index 0000000..d45f379
--- /dev/null
+++ b/src/websocket/websocket.ts
@@ -0,0 +1,32 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { RootStore } from '~/store/store';
+import { AuthService } from '~/services/auth-service/auth-service';
+import { Config } from '~/common/config';
+import { WebSocketService } from './websocket-service';
+import { ResourceEventMessage } from './resource-event-message';
+import { ResourceKind } from '~/models/resource';
+import { loadProcess } from '~/store/processes/processes-actions';
+import { loadContainers } from '../store/processes/processes-actions';
+import { FilterBuilder } from '~/common/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();
+};
+
+const messageListener = (store: RootStore) => (message: ResourceEventMessage) => {
+    switch (message.objectKind) {
+        case ResourceKind.CONTAINER_REQUEST:
+            return store.dispatch(loadProcess(message.objectUuid));
+        case ResourceKind.CONTAINER:
+            return store.dispatch(loadContainers(
+                new FilterBuilder().addIn('uuid', [message.objectUuid]).getFilters()
+            ));
+        default:
+            return;
+    }
+};

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


hooks/post-receive
-- 




More information about the arvados-commits mailing list