From 081ce8c5f4c0d1feeb1959923ed657b3c5cf5741 Mon Sep 17 00:00:00 2001 From: lorenzo-cavazzi Date: Mon, 6 Apr 2020 12:25:46 +0200 Subject: [PATCH] feat(environments): allow anonymous users to start environments Anonymous users can start an environment. They will get a temporary identity and they will see warnings suggesting to log in. BREAKING CHANGE: Requires backend components supporting anonymous users environments fix #857 --- src/api-client/index.js | 47 +++++---- src/api-client/notebook-servers.js | 17 ++-- src/api-client/pipeline.js | 41 ++++++++ src/landing/NavBar.js | 1 + src/model/RenkuModels.js | 1 + src/notebooks/Notebooks.container.js | 6 +- src/notebooks/Notebooks.present.js | 82 +++++++++++++--- src/notebooks/Notebooks.state.js | 136 ++++++++++++++++++++++++--- src/project/Project.present.js | 81 ++++++---------- 9 files changed, 309 insertions(+), 103 deletions(-) diff --git a/src/api-client/index.js b/src/api-client/index.js index 15c93cae3a..7befce77c6 100644 --- a/src/api-client/index.js +++ b/src/api-client/index.js @@ -43,6 +43,14 @@ const ACCESS_LEVELS = { OWNER: 50, }; +const FETCH_DEFAULT = { + options: { headers: new Headers() }, + returnType: RETURN_TYPES.json, + alertOnErr: false, + reLogin: true, + anonymousLogin: false +}; + class APIClient { // GitLab api client for Renku. Note that we do some @@ -76,35 +84,32 @@ class APIClient { // contain a user token. clientFetch( url, - options = { headers: new Headers() }, - returnType = RETURN_TYPES.json, - alertOnErr = false, - reLogin = true + options = FETCH_DEFAULT.options, + returnType = FETCH_DEFAULT.returnType, + alertOnErr = FETCH_DEFAULT.alertOnErr, + reLogin = FETCH_DEFAULT.reLogin, + anonymousLogin = FETCH_DEFAULT.anonymousLogin ) { - return renkuFetch(url, options) .catch((error) => { - // For permission errors we send the user to login - if (reLogin && error.case === API_ERRORS.unauthorizedError) + if (reLogin && error.case === API_ERRORS.unauthorizedError) { + if (anonymousLogin) + return this.doAnonymousLogin(); return this.doLogin(); - - + } // Alert only if corresponding option is set to true - else if (alertOnErr) + else if (alertOnErr) { alertAPIErrors(error); - - + } // Default case: Re-raise the error for the application // to take care of it. - else + else { return Promise.reject(error); - + } }) - .then(response => { switch (returnType) { - case RETURN_TYPES.json: return response.json().then(data => { return { @@ -112,13 +117,10 @@ class APIClient { pagination: processPaginationHeaders(this, response.headers) }; }); - case RETURN_TYPES.text: return response.text(); - case RETURN_TYPES.full: return response; - default: return response; } @@ -151,6 +153,11 @@ class APIClient { return fetch(urlObject, { headers, method }); } + doAnonymousLogin() { + window.location = `${this.baseUrl}/auth/jupyterhub/login-tmp` + + `?redirect-url=${encodeURIComponent(window.location.href)}`; + } + doLogin() { window.location = `${this.baseUrl}/auth/login?redirect_url=${encodeURIComponent(window.location.href)}`; } @@ -168,4 +175,4 @@ class APIClient { } export default APIClient; -export { alertAPIErrors, APIError, ACCESS_LEVELS, API_ERRORS, testClient }; +export { alertAPIErrors, APIError, ACCESS_LEVELS, API_ERRORS, FETCH_DEFAULT, testClient }; diff --git a/src/api-client/notebook-servers.js b/src/api-client/notebook-servers.js index 69117e727c..95790bd56c 100644 --- a/src/api-client/notebook-servers.js +++ b/src/api-client/notebook-servers.js @@ -16,8 +16,10 @@ * limitations under the License. */ +import { FETCH_DEFAULT } from "./index"; + function addNotebookServersMethods(client) { - client.getNotebookServers = (namespace, project, branch, commit) => { + client.getNotebookServers = (namespace, project, branch, commit, anonymous = false) => { const headers = client.getBasicHeaders(); const url = `${client.baseUrl}/notebooks/servers`; let parameters = {}; @@ -26,11 +28,14 @@ function addNotebookServersMethods(client) { if (branch) parameters.branch = branch; if (commit) parameters.commit_sha = commit; - return client.clientFetch(url, { - method: "GET", - headers, - queryParams: parameters - }).then(resp => { + return client.clientFetch( + url, + { method: "GET", headers, queryParams: parameters }, + FETCH_DEFAULT.returnType, + FETCH_DEFAULT.alertOnErr, + FETCH_DEFAULT.reLogin, + anonymous + ).then(resp => { return { "data": resp.data.servers }; }); }; diff --git a/src/api-client/pipeline.js b/src/api-client/pipeline.js index 70e9fd3758..834656305e 100644 --- a/src/api-client/pipeline.js +++ b/src/api-client/pipeline.js @@ -16,6 +16,8 @@ * limitations under the License. */ +import { API_ERRORS } from "./errors"; + function addPipelineMethods(client) { client.runPipeline = (projectId) => { const headers = client.getBasicHeaders(); @@ -63,6 +65,7 @@ function addPipelineMethods(client) { method: "GET", headers, }).then(response => response.data); + //return Promise.resolve([{ id: projectId, "status": "success" }]); }; /** @@ -81,6 +84,44 @@ function addPipelineMethods(client) { headers, }).then(response => response.data); }; + + /** + * Get the array of pipeline from GitLab + * + * @param {number|string} projectId - project id or slug + */ + client.getRegistries = (projectId) => { + const headers = client.getBasicHeaders(); + headers.append("Content-Type", "application/json"); + const url = `${client.baseUrl}/projects/${projectId}/registry/repositories`; + + return client.clientFetch(url, { + method: "GET", + headers, + }).then(response => response.data); + }; + + /** + * Get the array of pipeline from GitLab + * + * @param {number|string} projectId - project id or slug + * @param {number} registryId - registry id + * @param {string} tag - tag name, our convention uses the first 7 chars from commit id + */ + client.getRegistryTag = (projectId, registryId, tag) => { + const headers = client.getBasicHeaders(); + headers.append("Content-Type", "application/json"); + const url = `${client.baseUrl}/projects/${projectId}/registry/repositories` + + `/${registryId}/tags/${tag}`; + + return client.clientFetch(url, { method: "GET", headers }) + .then(response => response.data) + .catch((error) => { + if (error.case === API_ERRORS.notFoundError) + return null; + throw error; + }); + }; } export default addPipelineMethods; diff --git a/src/landing/NavBar.js b/src/landing/NavBar.js index ce1b3f9236..5a37b258c5 100644 --- a/src/landing/NavBar.js +++ b/src/landing/NavBar.js @@ -261,6 +261,7 @@ class AnonymousNavBar extends Component {