[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