10666: Merge branch 'master' into 10666-report-version
[arvados.git] / apps / workbench / app / assets / javascripts / models / session_db.js
index 1f5c02f9477017b21f808f2d3b9c75978c464410..ad9ad1878417370dfd75294e9bd9cecbe25880d1 100644 (file)
@@ -5,6 +5,7 @@
 window.SessionDB = function() {
     var db = this
     Object.assign(db, {
+        discoveryCache: {},
         loadFromLocalStorage: function() {
             try {
                 return JSON.parse(window.localStorage.getItem('sessions')) || {}
@@ -41,19 +42,34 @@ window.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.replace(/\?.*/, '')+'?baseURL='+encodeURIComponent(baseURL))
             return false
         },
@@ -69,7 +85,7 @@ window.SessionDB = 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) {
@@ -105,6 +121,35 @@ window.SessionDB = function() {
                 })
             })
         },
+        // 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 || {}
             opts.headers = opts.headers || {}