Merge branch '12934-cwl-conformance' refs #12934
[arvados.git] / apps / workbench / app / assets / javascripts / models / session_db.js
index fde02f637a8ba6106d745bcb788c658363fae956..a43cd79545b66ff47dda07f3097b3c8803a47262 100644 (file)
@@ -2,10 +2,10 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-window.models = window.models || {}
-window.models.SessionDB = function() {
+window.SessionDB = function() {
     var db = this
     Object.assign(db, {
+        discoveryCache: {},
         loadFromLocalStorage: function() {
             try {
                 return JSON.parse(window.localStorage.getItem('sessions')) || {}
@@ -20,6 +20,25 @@ window.models.SessionDB = function() {
             }
             return all
         },
+        loadActive: function() {
+            var sessions = db.loadAll()
+            Object.keys(sessions).forEach(function(key) {
+                if (!sessions[key].token)
+                    delete sessions[key]
+            })
+            return sessions
+        },
+        loadLocal: function() {
+            var sessions = db.loadActive()
+            var s = false
+            Object.values(sessions).forEach(function(session) {
+                if (session.isFromRails) {
+                    s = session
+                    return
+                }
+            })
+            return s
+        },
         save: function(k, v) {
             var sessions = db.loadAll()
             sessions[k] = v
@@ -34,27 +53,50 @@ window.models.SessionDB = function() {
             delete sessions[k]
             window.localStorage.setItem('sessions', JSON.stringify(sessions))
         },
-        login: function(host) {
-            // Initiate login procedure with given API host (which can
-            // optionally include scheme://).
+        findAPI: function(url) {
+            // Given a Workbench or API host or URL, return a promise
+            // for the corresponding API server's base URL.  Typical
+            // use:
+            // sessionDB.findAPI('https://workbench.example/foo').then(sessionDB.login)
+            if (url.indexOf('://') < 0)
+                url = 'https://' + url
+            url = new URL(url)
+            return m.request(url.origin + '/discovery/v1/apis/arvados/v1/rest').then(function() {
+                return url.origin + '/'
+            }).catch(function(err) {
+                // If url is a Workbench site (and isn't too old),
+                // /status.json will tell us its API host.
+                return m.request(url.origin + '/status.json').then(function(resp) {
+                    if (!resp.apiBaseURL)
+                        throw 'no apiBaseURL in status response'
+                    return resp.apiBaseURL
+                })
+            })
+        },
+        login: function(baseURL) {
+            // Initiate login procedure with given API base URL (e.g.,
+            // "http://api.example/").
             //
             // Any page that has a button that invokes login() must
             // also call checkForNewToken() on (at least) its first
             // render. Otherwise, the login procedure can't be
             // completed.
-            var baseURL = host
-            if (baseURL.indexOf('://') < 0)
-                baseURL = 'https://' + baseURL
-            if (!baseURL.endsWith('/'))
-                baseURL = baseURL + '/'
-            document.location = baseURL + 'login?return_to=' + encodeURIComponent(document.location.href+'?baseURL='+encodeURIComponent(baseURL))
+            document.location = baseURL + 'login?return_to=' + encodeURIComponent(document.location.href.replace(/\?.*/, '')+'?baseURL='+encodeURIComponent(baseURL))
             return false
         },
+        logout: function(k) {
+            // Forget the token, but leave the other info in the db so
+            // the user can log in again without providing the login
+            // host again.
+            var sessions = db.loadAll()
+            delete sessions[k].token
+            db.save(k, sessions[k])
+        },
         checkForNewToken: function() {
             // If there's a token and baseURL in the location bar (i.e.,
             // we just landed here after a successful login), save it and
             // scrub the location bar.
-            if (!document.location.search.startsWith('?'))
+            if (document.location.search[0] != '?')
                 return
             var params = {}
             document.location.search.slice(1).split('&').map(function(kv) {
@@ -89,7 +131,36 @@ window.models.SessionDB = function() {
                     db.trash(key)
                 })
             })
-            // m.request(session.baseURL + 'discovery/v1/apis/arvados/v1/rest').then(function(dd) {})
+        },
+        // Return the Workbench base URL advertised by the session's
+        // API server, or a reasonable guess, or (if neither strategy
+        // works out) null.
+        workbenchBaseURL: function(session) {
+            var dd = db.discoveryDoc(session)()
+            if (!dd)
+                // Don't fall back to guessing until we receive the discovery doc
+                return null
+            if (dd.workbenchUrl)
+                return dd.workbenchUrl
+            // Guess workbench.{apihostport} is a Workbench... unless
+            // the host part of apihostport is an IPv4 or [IPv6]
+            // address.
+            if (!session.baseURL.match('://(\\[|\\d+\\.\\d+\\.\\d+\\.\\d+[:/])')) {
+                var wbUrl = session.baseURL.replace('://', '://workbench.')
+                // Remove the trailing slash, if it's there.
+                return wbUrl.slice(-1) == '/' ? wbUrl.slice(0, -1) : wbUrl
+            }
+            return null
+        },
+        // Return a m.stream that will get fulfilled with the
+        // discovery doc from a session's API server.
+        discoveryDoc: function(session) {
+            var cache = db.discoveryCache[session.baseURL]
+            if (!cache) {
+                db.discoveryCache[session.baseURL] = cache = m.stream()
+                m.request(session.baseURL+'discovery/v1/apis/arvados/v1/rest').then(cache)
+            }
+            return cache
         },
         request: function(session, path, opts) {
             opts = opts || {}