12033: Canary query before initiating login procedure.
authorTom Clegg <tom@curoverse.com>
Sat, 26 Aug 2017 02:15:05 +0000 (22:15 -0400)
committerTom Clegg <tom@curoverse.com>
Sat, 26 Aug 2017 02:15:05 +0000 (22:15 -0400)
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg@veritasgenetics.com>

apps/workbench/app/assets/javascripts/components/sessions.js
apps/workbench/app/assets/javascripts/models/session_db.js
apps/workbench/app/controllers/status_controller.rb [new file with mode: 0644]
apps/workbench/config/routes.rb

index 17c144c589ac9e952b056ad263f01bf5b2b4cb47..cbf2a7e8095bb79bcf58a3db786884c70a9321f2 100644 (file)
@@ -12,6 +12,8 @@ window.SessionsTable = {
     oninit: function(vnode) {
         vnode.state.db = new SessionDB()
         vnode.state.hostToAdd = m.stream('')
+        vnode.state.error = m.stream()
+        vnode.state.checking = m.stream()
     },
     view: function(vnode) {
         var db = vnode.state.db
@@ -60,12 +62,22 @@ window.SessionsTable = {
             m('.row', m('.col-md-6', [
                 m('form', {
                     onsubmit: function() {
-                        db.login(vnode.state.hostToAdd())
+                        vnode.state.error(null)
+                        vnode.state.checking(true)
+                        db.findAPI(vnode.state.hostToAdd())
+                            .then(db.login)
+                            .catch(function() {
+                                vnode.state.error(true)
+                            })
+                            .then(vnode.state.checking.bind(null, null))
                         return false
                     },
                 }, [
-                    m('.input-group', [
-                        m('input.form-control[type=text][name=apiHost][placeholder="API host"]', {
+                    m('p', [
+                        'To add a remote Arvados site, paste the remote site\'s host here (see "ARVADOS_API_HOST" on the "current token" page).',
+                    ]),
+                    m('.input-group', { className: vnode.state.error() && 'has-error' }, [
+                        m('input.form-control[type=text][name=apiHost][placeholder="zzzzz.arvadosapi.com"]', {
                             oninput: m.withAttr('value', vnode.state.hostToAdd),
                         }),
                         m('.input-group-btn', [
@@ -75,6 +87,9 @@ window.SessionsTable = {
                         ]),
                     ]),
                 ]),
+                m('p'),
+                vnode.state.error() && m('p.alert.alert-danger', 'Request failed. Make sure this is a working API server address.'),
+                vnode.state.checking() && m('p.alert.alert-info', 'Checking...'),
             ])),
         ])
     },
index 1f5c02f9477017b21f808f2d3b9c75978c464410..13409d9048d8a669380a79b810c00bdb2820c7c7 100644 (file)
@@ -41,19 +41,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
         },
diff --git a/apps/workbench/app/controllers/status_controller.rb b/apps/workbench/app/controllers/status_controller.rb
new file mode 100644 (file)
index 0000000..90b7be5
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+class StatusController < ApplicationController
+  skip_around_filter :require_thread_api_token
+  skip_before_filter :find_object_by_uuid
+  def status
+    # Allow non-credentialed cross-origin requests
+    headers['Access-Control-Allow-Origin'] = '*'
+    resp = {
+      apiBaseURL: arvados_api_client.arvados_v1_base.sub(%r{/arvados/v\d+.*}, '/'),
+      version: AppVersion.hash,
+    }
+    render json: resp
+  end
+end
index 8dcc7fdd20cadbf41bb9da7997c06597169e29eb..fee49c14ce213aa2cd001252043f4cc7dfc79745 100644 (file)
@@ -132,6 +132,8 @@ ArvadosWorkbench::Application.routes.draw do
 
   get '/tests/mithril', to: 'tests#mithril'
 
+  get '/status', to: 'status#status'
+
   # Send unroutable requests to an arbitrary controller
   # (ends up at ApplicationController#render_not_found)
   match '*a', to: 'links#render_not_found', via: [:get, :post]