15718: Adds API revision number from discovery doc to sessions.
authorLucas Di Pentima <lucas@di-pentima.com.ar>
Wed, 12 Feb 2020 22:53:20 +0000 (19:53 -0300)
committerLucas Di Pentima <lucas@di-pentima.com.ar>
Wed, 12 Feb 2020 22:53:20 +0000 (19:53 -0300)
Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas@di-pentima.com.ar>

src/common/config.ts
src/models/session.ts
src/services/auth-service/auth-service.ts
src/store/auth/auth-action-session.ts
src/store/auth/auth-action.test.ts

index 23faaf91adbe7c75a16b566edc3538add8d6d5f6..58fa13ae62e6ee946ada2ab931ee54064a8c6e8f 100644 (file)
@@ -78,6 +78,7 @@ export class Config {
     fileViewersConfigUrl: string;
     loginCluster: string;
     clusterConfig: ClusterConfigJSON;
     fileViewersConfigUrl: string;
     loginCluster: string;
     clusterConfig: ClusterConfigJSON;
+    apiRevision: number;
 }
 
 export const buildConfig = (clusterConfigJSON: ClusterConfigJSON): Config => {
 }
 
 export const buildConfig = (clusterConfigJSON: ClusterConfigJSON): Config => {
@@ -91,10 +92,21 @@ export const buildConfig = (clusterConfigJSON: ClusterConfigJSON): Config => {
     config.keepWebServiceUrl = clusterConfigJSON.Services.WebDAVDownload.ExternalURL;
     config.loginCluster = clusterConfigJSON.Login.LoginCluster;
     config.clusterConfig = clusterConfigJSON;
     config.keepWebServiceUrl = clusterConfigJSON.Services.WebDAVDownload.ExternalURL;
     config.loginCluster = clusterConfigJSON.Login.LoginCluster;
     config.clusterConfig = clusterConfigJSON;
+    config.apiRevision = 0;
     mapRemoteHosts(clusterConfigJSON, config);
     return config;
 };
 
     mapRemoteHosts(clusterConfigJSON, config);
     return config;
 };
 
+const getApiRevision = async (apiUrl: string) => {
+    try {
+        const dd = (await Axios.get<any>(`${apiUrl}/${DISCOVERY_DOC_PATH}`)).data;
+        return parseInt(dd.revision, 10) || 0;
+    } catch {
+        console.warn("Unable to get API Revision number, defaulting to zero. Some features may not work properly.");
+        return 0;
+    }
+};
+
 export const fetchConfig = () => {
     return Axios
         .get<WorkbenchConfig>(WORKBENCH_CONFIG_URL + "?nocache=" + (new Date()).getTime())
 export const fetchConfig = () => {
     return Axios
         .get<WorkbenchConfig>(WORKBENCH_CONFIG_URL + "?nocache=" + (new Date()).getTime())
@@ -107,9 +119,10 @@ export const fetchConfig = () => {
             if (workbenchConfig.API_HOST === undefined) {
                 throw new Error(`Unable to start Workbench. API_HOST is undefined in ${WORKBENCH_CONFIG_URL} or the environment.`);
             }
             if (workbenchConfig.API_HOST === undefined) {
                 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 => {
+            return Axios.get<ClusterConfigJSON>(getClusterConfigURL(workbenchConfig.API_HOST)).then(async response => {
                 const clusterConfigJSON = response.data;
                 const clusterConfigJSON = response.data;
-                const config = buildConfig(clusterConfigJSON);
+                const apiRevision = await getApiRevision(clusterConfigJSON.Services.Controller.ExternalURL);
+                const config = {...buildConfig(clusterConfigJSON), apiRevision};
                 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}`);
                 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}`);
@@ -192,6 +205,7 @@ export const mockConfig = (config: Partial<Config>): Config => ({
     fileViewersConfigUrl: "",
     loginCluster: "",
     clusterConfig: mockClusterConfigJSON({}),
     fileViewersConfigUrl: "",
     loginCluster: "",
     clusterConfig: mockClusterConfigJSON({}),
+    apiRevision: 0,
     ...config
 });
 
     ...config
 });
 
index 91a0d997606257391e0958f595dc3696475b9402..d388f59926e0f1235f3c3aef0101252b24d03fe0 100644 (file)
@@ -19,4 +19,5 @@ export interface Session {
     loggedIn: boolean;
     status: SessionStatus;
     active: boolean;
     loggedIn: boolean;
     status: SessionStatus;
     active: boolean;
+    apiRevision: number;
 }
 }
index c6e93a8fe777210e45d8f8a4a1bc9f2abb7fec33..690420e78449ff35815cd63e0b1644a65012793a 100644 (file)
@@ -135,7 +135,8 @@ export class AuthService {
             loggedIn: true,
             active: true,
             uuid: user ? user.uuid : '',
             loggedIn: true,
             active: true,
             uuid: user ? user.uuid : '',
-            status: SessionStatus.VALIDATED
+            status: SessionStatus.VALIDATED,
+            apiRevision: cfg.apiRevision,
         } as Session;
         const localSessions = this.getSessions().map(s => ({
             ...s,
         } as Session;
         const localSessions = this.getSessions().map(s => ({
             ...s,
@@ -155,7 +156,8 @@ export class AuthService {
                 loggedIn: false,
                 active: false,
                 uuid: '',
                 loggedIn: false,
                 active: false,
                 uuid: '',
-                status: SessionStatus.INVALIDATED
+                status: SessionStatus.INVALIDATED,
+                apiRevision: 0,
             } as Session;
         });
         const sessions = [currentSession]
             } as Session;
         });
         const sessions = [currentSession]
index c1b97adc3ea0faa1e9b685832150cfe59bf119b8..a63878286a94dc407d1060c98a8e8c7c5e5cceda 100644 (file)
@@ -21,31 +21,37 @@ import { snackbarActions, SnackbarKind } from "~/store/snackbar/snackbar-actions
 import * as jsSHA from "jssha";
 
 const getClusterConfig = async (origin: string): Promise<Config | null> => {
 import * as jsSHA from "jssha";
 
 const getClusterConfig = async (origin: string): Promise<Config | null> => {
-    // Try the new public config endpoint
+    let configFromDD: Config | undefined;
     try {
     try {
-        const config = (await Axios.get<ClusterConfigJSON>(`${origin}/${CLUSTER_CONFIG_PATH}`)).data;
-        return buildConfig(config);
-    } catch { }
-
-    // Fall back to discovery document
-    try {
-        const config = (await Axios.get<any>(`${origin}/${DISCOVERY_DOC_PATH}`)).data;
-        return {
-            baseUrl: normalizeURLPath(config.baseUrl),
-            keepWebServiceUrl: config.keepWebServiceUrl,
-            remoteHosts: config.remoteHosts,
-            rootUrl: config.rootUrl,
-            uuidPrefix: config.uuidPrefix,
-            websocketUrl: config.websocketUrl,
-            workbenchUrl: config.workbenchUrl,
-            workbench2Url: config.workbench2Url,
+        const dd = (await Axios.get<any>(`${origin}/${DISCOVERY_DOC_PATH}`)).data;
+        configFromDD = {
+            baseUrl: normalizeURLPath(dd.baseUrl),
+            keepWebServiceUrl: dd.keepWebServiceUrl,
+            remoteHosts: dd.remoteHosts,
+            rootUrl: dd.rootUrl,
+            uuidPrefix: dd.uuidPrefix,
+            websocketUrl: dd.websocketUrl,
+            workbenchUrl: dd.workbenchUrl,
+            workbench2Url: dd.workbench2Url,
             loginCluster: "",
             vocabularyUrl: "",
             fileViewersConfigUrl: "",
             loginCluster: "",
             vocabularyUrl: "",
             fileViewersConfigUrl: "",
-            clusterConfig: mockClusterConfigJSON({})
+            clusterConfig: mockClusterConfigJSON({}),
+            apiRevision: parseInt(dd.revision, 10),
         };
     } catch { }
 
         };
     } catch { }
 
+    // Try the new public config endpoint
+    try {
+        const config = (await Axios.get<ClusterConfigJSON>(`${origin}/${CLUSTER_CONFIG_PATH}`)).data;
+        return {...buildConfig(config), apiRevision: configFromDD ? configFromDD.apiRevision : 0};
+    } catch { }
+
+    // Fall back to discovery document
+    if (configFromDD !== undefined) {
+        return configFromDD;
+    }
+
     return null;
 };
 
     return null;
 };
 
@@ -120,13 +126,14 @@ export const validateSession = (session: Session, activeSession: Session) =>
         dispatch(authActions.UPDATE_SESSION({ ...session, status: SessionStatus.BEING_VALIDATED }));
         session.loggedIn = false;
 
         dispatch(authActions.UPDATE_SESSION({ ...session, status: SessionStatus.BEING_VALIDATED }));
         session.loggedIn = false;
 
-        const setupSession = (baseUrl: string, user: User, token: string) => {
+        const setupSession = (baseUrl: string, user: User, token: string, apiRevision: number) => {
             session.baseUrl = baseUrl;
             session.token = token;
             session.email = user.email;
             session.uuid = user.uuid;
             session.name = getUserFullname(user);
             session.loggedIn = true;
             session.baseUrl = baseUrl;
             session.token = token;
             session.email = user.email;
             session.uuid = user.uuid;
             session.name = getUserFullname(user);
             session.loggedIn = true;
+            session.apiRevision = apiRevision;
         };
 
         let fail: Error | null = null;
         };
 
         let fail: Error | null = null;
@@ -135,12 +142,12 @@ export const validateSession = (session: Session, activeSession: Session) =>
             dispatch(authActions.REMOTE_CLUSTER_CONFIG({ config }));
             try {
                 const { user, token } = await validateCluster(config, session.token);
             dispatch(authActions.REMOTE_CLUSTER_CONFIG({ config }));
             try {
                 const { user, token } = await validateCluster(config, session.token);
-                setupSession(config.baseUrl, user, token);
+                setupSession(config.baseUrl, user, token, config.apiRevision);
             } catch (e) {
                 fail = new Error(`Getting current user for ${session.remoteHost}: ${e.message}`);
                 try {
                     const { user, token } = await validateCluster(config, activeSession.token);
             } catch (e) {
                 fail = new Error(`Getting current user for ${session.remoteHost}: ${e.message}`);
                 try {
                     const { user, token } = await validateCluster(config, activeSession.token);
-                    setupSession(config.baseUrl, user, token);
+                    setupSession(config.baseUrl, user, token, config.apiRevision);
                     fail = null;
                 } catch (e2) {
                     if (e.message === invalidV2Token) {
                     fail = null;
                 } catch (e2) {
                     if (e.message === invalidV2Token) {
@@ -227,7 +234,8 @@ export const addSession = (remoteHost: string, token?: string, sendToLogin?: boo
                     baseUrl: config.baseUrl,
                     clusterId: config.uuidPrefix,
                     remoteHost,
                     baseUrl: config.baseUrl,
                     clusterId: config.uuidPrefix,
                     remoteHost,
-                    token
+                    token,
+                    apiRevision: config.apiRevision,
                 };
 
                 if (sessions.find(s => s.clusterId === config.uuidPrefix)) {
                 };
 
                 if (sessions.find(s => s.clusterId === config.uuidPrefix)) {
index d126d9caeb52f519e2b643be84384311765cfa5e..8a17fe9f42da87b0360845580d2ce4e8fcdabb6f 100644 (file)
@@ -67,6 +67,7 @@ describe('auth-actions', () => {
             rootUrl: "https://zzzzz.arvadosapi.com",
             uuidPrefix: "zzzzz",
             remoteHosts: { xc59z: "xc59z.arvadosapi.com" },
             rootUrl: "https://zzzzz.arvadosapi.com",
             uuidPrefix: "zzzzz",
             remoteHosts: { xc59z: "xc59z.arvadosapi.com" },
+            apiRevision: 12345678,
         };
 
         store.dispatch(initAuth(config));
         };
 
         store.dispatch(initAuth(config));
@@ -82,6 +83,7 @@ describe('auth-actions', () => {
                     expect(auth).toEqual({
                         apiToken: "token",
                         config: {
                     expect(auth).toEqual({
                         apiToken: "token",
                         config: {
+                            apiRevision: 12345678,
                             remoteHosts: {
                                 "xc59z": "xc59z.arvadosapi.com",
                             },
                             remoteHosts: {
                                 "xc59z": "xc59z.arvadosapi.com",
                             },
@@ -94,6 +96,7 @@ describe('auth-actions', () => {
                         loginCluster: undefined,
                         remoteHostsConfig: {
                             "zzzzz": {
                         loginCluster: undefined,
                         remoteHostsConfig: {
                             "zzzzz": {
+                                "apiRevision": 12345678,
                                 "remoteHosts": {
                                     "xc59z": "xc59z.arvadosapi.com",
                                 },
                                 "remoteHosts": {
                                     "xc59z": "xc59z.arvadosapi.com",
                                 },
@@ -114,8 +117,9 @@ describe('auth-actions', () => {
                             "remoteHost": "https://zzzzz.arvadosapi.com",
                             "status": 2,
                             "token": "token",
                             "remoteHost": "https://zzzzz.arvadosapi.com",
                             "status": 2,
                             "token": "token",
-                            "name": "John Doe"
-                   "uuid": "zzzzz-tpzed-abcefg",
+                            "name": "John Doe",
+                            "apiRevision": 12345678,
+                            "uuid": "zzzzz-tpzed-abcefg",
                         }, {
                             "active": false,
                             "baseUrl": "",
                         }, {
                             "active": false,
                             "baseUrl": "",
@@ -127,6 +131,7 @@ describe('auth-actions', () => {
                             "token": "",
                             "name": "",
                             "uuid": "",
                             "token": "",
                             "name": "",
                             "uuid": "",
+                            "apiRevision": 0,
                         }],
                         user: {
                             email: "test@test.com",
                         }],
                         user: {
                             email: "test@test.com",