diff --git a/frontend/js/apicalls.js b/frontend/js/apicalls.js index bd9c42b867b059d12d894b3409ab366a9d0e1bcf..d03b0a878903e168803893cbe7982c6bd33ceb81 100644 --- a/frontend/js/apicalls.js +++ b/frontend/js/apicalls.js @@ -1,8 +1,32 @@ // This file contains the api calls, as well as transform the data into html-text - var apiUrl = "http://zam024.fritz.box/api/"; // TODO switch out with real url, ideally during deployment var allowedTypesList = []; +// get data from url query variables +function getUrlVars() +{ + var vars = [], hash; + var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); + for(var i = 0; i < hashes.length; i++) + { + hash = hashes[i].split('='); + vars.push(hash[0]); + vars[hash[0]] = hash[1]; + } + return vars; +} + +// return the storage type +function getType() { + var type = getUrlVars()["type"] + return type +} + +// set the text of the typetext element +function setTypeText() { + $('#typetext').text(getType()) +} + // return the dataset id function getId() { var id = getUrlVars()["oid"]; @@ -25,8 +49,28 @@ get html for table entry with a proeprty The value field is editable, but the edit is blocked by default authenticated users should be able to edit and submit */ -function getPropertyHTMLString(property, value) { - return '<tr><th scope="row">' + property + '</th><td><input class="form-control" type="text" value="' + value + '" readonly></td></tr>'; +function getPropertyHTMLString(property, value, readonly=true) { + return '<tr><th scope="row">' + property + '</th><td><input class="form-control" type="text" value="' + value + (readonly ? '" readonly' : '"') + '></td></tr>'; +} + +/* +* Fill given table with data from given dataset +* if readonly is false, make all but OID editable +* else everything is readonly +*/ +function fillDatasetTable(table, dataset, readonly=faltruese) { + // now append name and url to the view + table.append(getPropertyHTMLString('Name', dataset.name, readonly)); + table.append(getPropertyHTMLString('OID', getId())); + table.append(getPropertyHTMLString('URL', dataset.url, readonly)); + + // insert a linebreak that announces other metadata + table.append('<tr><th class="info" scope="row" colspan="2">Other Metadata</th></tr>'); + + // iterate over metadata map and add additional properties + for (const [key, val] of Object.entries(dataset.metadata)) { + table.append(getPropertyHTMLString(key, val, editable)); + } } // XMLHttpRequest EVENTLISTENER: if a dropdown-menu (a <ul> element) with the dropdownOptions id is present, update it with the available types @@ -54,6 +98,15 @@ function setDatasetList() { }); } +// XMLHttpRequest EVENTLISTENER: show banner with new dataset id +function showNewDatasetID() { + console.log("POST " + this.responseUrl + ": " + this.responseText); + var data = JSON.parse(this.responseText); + var id = data[0]; + var alertHTML = '<div class="alert alert-success" role="alert">Dataset created! OID is: <a href="?type=' + getType() + '&oid=' + id + '">' + id + '</a></div>'; + $('#storageTypeChooser').after(alertHTML); +} + // XMLHttpRequest EVENTLISTENER: show dataset in table function setDatasetView() { console.log("GET " + this.responseUrl + ": " + this.responseText); @@ -68,19 +121,11 @@ function setDatasetView() { $('#datasetListTable').hide(); $('#storageTypeChooser').hide(); $('#datasetViewTable').show(); - - // now append name and url to the view - $('#datasetViewTableBody').append(getPropertyHTMLString('Name', dataset.name)); - $('#datasetViewTableBody').append(getPropertyHTMLString('OID', getId())); - $('#datasetViewTableBody').append(getPropertyHTMLString('URL', dataset.url)); - - // insert a linebreak that announces other metadata - $('#datasetViewTableBody').append('<tr><th class="info" scope="row" colspan="2">Other Metadata</th></tr>'); - - // iterate over metadata map and add additional properties - for (const [key, val] of Object.entries(dataset.metadata)) { - $('#datasetViewTableBody').append(getPropertyHTMLString(key, val)); + if (window.sessionStorage.auth_token) { + $('#modifyDatasetButtonGroup').show(); } + + fillDatasetTable($('#datasetViewTableBody'), dataset, true); } // get available types from api, put them in the relevant dropdown (via listener) @@ -125,13 +170,32 @@ async function showListingOrSingleDataset() { window.location.href = "?type=" + allowedTypesList[0]; } if (!getId()) { // no id given, so list all elements + if (window.sessionStorage.auth_token) { + $('#addNewDatasetButton').show(); + } listDatasets(getType()); + } else if (getId() == "new") { + alert ("Do stuff for new dataset i.e. edit datset with empty oid"); } else { // an id is given, show the dataset, error message if invalid showDataset(getType(), getId()); } } -// TODO function(s) to POST new Dataset (get bearer token from auth.js) -// TODO function(s) to PUT existing Dataset (get bearer token from auth.js) -// TODO function(s) to DELETE existing Dataset (get bearer token from auth.js) \ No newline at end of file +// POST new Dataset (get bearer token from session storage) +function createNewDataset(datatype, name, url, metadata) { + var dataset = {"name" : name, "url" : url, "metadata" : metadata}; + var fullUrl = apiUrl + datatype; + console.log("Full url for creating new dataset is " + fullUrl) + console.log("New Dataset is " + dataset) + var xmlhttp = new XMLHttpRequest(); + xmlhttp.addEventListener("loadend", showNewDatasetID); + xmlhttp.open("POST", fullUrl); + xmlhttp.setRequestHeader('Authorization', 'Bearer ' + window.sessionStorage.auth_token); + xmlhttp.setRequestHeader('Content-Type', 'application/json'); + xmlhttp.send(JSON.stringify(dataset)); + // TODO disable all buttons, put a spinner on save +} + +// TODO function(s) to PUT existing Dataset (get bearer token from session storage) +// TODO function(s) to DELETE existing Dataset (get bearer token from session storage) \ No newline at end of file diff --git a/frontend/js/auth.js b/frontend/js/auth.js index 8c6ff1b57a572685ece77fed7557bc99aae1d11a..07fa1855328550d420b8f7f9b57f4d918153c9e5 100644 --- a/frontend/js/auth.js +++ b/frontend/js/auth.js @@ -1,16 +1,120 @@ // This file will contain functions to manage authentication (including the token storage and access) -// TODO function to rewrite Login as logout -// TODO function to add "ME" option to navbar (maybe add both and hide as needed) +// TODO function to rewrite Login as username -// TODO function(s) to post to receive and store bearer token +/************************************************ + * Event Listeners for XMLHttpRequests + ************************************************/ + +// XMLHttpRequest EVENTLISTENER: if the call was successful, store the token locally and reload login page +function setLoginToken() { + console.log("POST " + this.responseUrl + ": " + this.responseText); + if (this.status >= 400) { + alert("The username and/ or the password is invalid!"); + logout(); + } else { + var tokenData = JSON.parse(this.responseText); + window.sessionStorage.auth_token = tokenData.access_token; + location.reload(); + } +} + +// To be called by an inline XMLHttpRequest EVENTLISTENER: if the call was successful, update the userdata +function setUserdata(data, updateView) { + console.log("GET " + data.responseUrl + ": " + data.responseText); + if (this.status >= 400) { + logout(); + } else { + var userdata = JSON.parse(data.responseText); + console.log("Userdata: " + userdata.username + " - " + userdata.email); + // store username and email in sessionData (blind overwrite if exists) + window.sessionStorage.username = userdata.username; + window.sessionStorage.email = userdata.email; + if (updateView) { + $('#usernameLabel').html(window.sessionStorage.username); + $('#emailLabel').html(window.sessionStorage.email); + } + } +} /* -makes a post call for the token and stores it in localstorage +* makes a post call for the token and stores it in localstorage */ -function login(username, password) { +function loginPOST(username, password) { + var fullUrl = apiUrl + "token"; + console.log("Full url for token request is " + fullUrl) + var xmlhttp = new XMLHttpRequest(); + xmlhttp.addEventListener("loadend", setLoginToken); + xmlhttp.open("POST", fullUrl); + xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + xmlhttp.send("username=" + encodeURIComponent(username) + "&password=" + encodeURIComponent(password)); +} +/** +* checks the textfields for username and password, gives an error message if not given, then calls the loginPOST(username, password) function +*/ +function login() { + $('#loginButton').prepend('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>'); + $('#loginButton').attr("disabled", true); + loginPOST($('#usernameField').val(), $('#passwordField').val()); } -// TODO funciton to call /me -// TODO function to get bearer token (for external use) \ No newline at end of file + +/* +* clear sessionStorage of all auth related data and redirect to the login page +*/ +function logout() { + delete window.sessionStorage.auth_token; + delete window.sessionStorage.username; + delete window.sessionStorage.email; + location.reload(); +} + +/* +* call API/me to get ifo about the user and check if token is valid +* if updateView is true, also update the username and email fields in the login page (with data from the sessionstorage) +* The function returns true if the token is valid and the server responded with the desired data +*/ +function getInfo(updateView=false) { + if (window.sessionStorage.auth_token === undefined) { + return false; + } else { + // if updateView, set text to spinners, eventlistener will fill correct values + if (updateView) { + $('#usernameLabel').append('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>'); + $('#emailLabel').append('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>'); + } + + // start GET /me, pass wether an update of the user labels is needed + var fullUrl = apiUrl + "me"; + console.log("Full url for /me request is " + fullUrl) + var xmlhttp = new XMLHttpRequest(); + xmlhttp.open("GET", fullUrl); + xmlhttp.setRequestHeader('Authorization', 'Bearer ' + window.sessionStorage.auth_token); + xmlhttp.addEventListener("loadend", async function() { + setUserdata(this, updateView); + }); + xmlhttp.updateView = updateView; + xmlhttp.send(); + return true; // this true is okay, if the request fails, it will automatically logout and reload anyway + } +} + +/* +* either show the userinfo table (true) or the loginform (false) (if present) +* also adjust the Log In Navbar element +* also show edit/ save and addNew Buttons (true) (if present) +*/ +function showElementsDependingOnLoginStatus(loggedIn = true) { + if (loggedIn) { + $('#loginForm').hide(); + $('#userinfoViewer').show(); + $('#loginOutText').html('Logged In (<b>' + window.sessionStorage.username + '</b>)'); + } else { + $('#userinfoViewer').hide(); + $('#loginForm').show(); + $('#loginOutText').text("Log In"); + $('#modifyDatasetButtonGroup').hide(); + $('#addNewDatasetButton').hide(); + } +} \ No newline at end of file diff --git a/frontend/js/choose_storage.js b/frontend/js/choose_storage.js deleted file mode 100644 index 07784301ed19e18257147ac846556794616a9e15..0000000000000000000000000000000000000000 --- a/frontend/js/choose_storage.js +++ /dev/null @@ -1,25 +0,0 @@ -// get data from url query variables -function getUrlVars() -{ - var vars = [], hash; - var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); - for(var i = 0; i < hashes.length; i++) - { - hash = hashes[i].split('='); - vars.push(hash[0]); - vars[hash[0]] = hash[1]; - } - return vars; -} - -// return the storage type -function getType() { - var type = getUrlVars()["type"] - console.log("Type: " + type) - return type -} - -// set the text of the typetext element -function setTypeText() { - $('#typetext').text(getType()) -} \ No newline at end of file