diff --git a/src/auth.js b/src/auth.js index 5f6159b19a1da98f9c30da7178d2dfab6d852ec8..31945a591b9ee1e8a79ddde8bfeec7b2619effbf 100644 --- a/src/auth.js +++ b/src/auth.js @@ -1,6 +1,8 @@ import axios from 'axios'; import * as localStorage from './localStorage'; -import { apiUrl } from './config.json'; +import config from './config.json'; + +const m = require('mithril'); // Object which stores the current login-state const APISession = { @@ -8,8 +10,14 @@ const APISession = { token: '', }; +export function resetSession() { + APISession.authenticated = false; + APISession.token = ''; + m.route.set('/login'); +} + const amivapi = axios.create({ - baseURL: apiUrl, + baseURL: config.apiUrl, timeout: 10000, headers: { 'Content-Type': 'application/json' }, }); @@ -33,8 +41,8 @@ function checkToken(token) { export function checkAuthenticated() { // return a promise that resolves always, with a bool that shows whether // the user is authenticated - return new Promise((resolve, reject) => { - if (APISession.authenticated) resolve(true); + return new Promise((resolve) => { + if (APISession.authenticated) resolve(); else { console.log('looking for token'); // let's see if we have a stored token @@ -45,31 +53,27 @@ export function checkAuthenticated() { checkToken(token).then(() => { APISession.token = token; APISession.authenticated = true; - resolve(true); - }).catch(() => { - resolve(false); - }); - } else resolve(false); + resolve(); + }).catch(resetSession); + } else resetSession(); } }); } export function getSession() { // Promise resolves with authenticated axios-session or fails - return new Promise((resolve, reject) => { - checkAuthenticated().then((authenticated) => { - if (authenticated) { - const authenticatedSession = axios.create({ - baseURL: apiUrl, - timeout: 10000, - headers: { - 'Content-Type': 'application/json', - Authorization: APISession.token, - }, - }); - resolve(authenticatedSession); - } else reject(); - }).catch(reject); + return new Promise((resolve) => { + checkAuthenticated().then(() => { + const authenticatedSession = axios.create({ + baseURL: config.apiUrl, + timeout: 10000, + headers: { + 'Content-Type': 'application/json', + Authorization: APISession.token, + }, + }); + resolve(authenticatedSession); + }).catch(resetSession); }); } @@ -87,3 +91,162 @@ export function login(username, password) { }).catch(reject); }); } + +export class ResourceHandler { + constructor(resource, searchKeys = false) { + this.resource = resource; + this.searchKeys = searchKeys || config[resource].searchKeys; + this.patchKeys = config[resource].patchableKeys; + + checkAuthenticated(); + } + + // definitions of query parameters in addition to API go here + buildQuerystring(query) { + const queryKeys = Object.keys(query); + + if (queryKeys.length === 0) return ''; + + const fullQuery = {}; + + if ('search' in query && query.search.length > 0) { + // translate search into where, we just look if any field contains search + const searchQuery = { + $or: this.searchKeys.map((key) => { + const fieldQuery = {}; + fieldQuery[key] = query.search; + return fieldQuery; + }), + }; + + // if there exists already a where-filter, AND them together + if ('where' in query) { + fullQuery.where = JSON.stringify({ $and: [searchQuery, query.where] }); + } else { + fullQuery.where = JSON.stringify(searchQuery); + } + } else { + fullQuery.where = JSON.stringify(query.where); + } + + // add all other keys + queryKeys.filter(key => (key !== 'where' && key !== 'search')) + .forEach((key) => { fullQuery[key] = JSON.stringify(query[key]); }); + + console.log(fullQuery); + + // now we can acutally build the query string + return `?${m.buildQueryString(fullQuery)}`; + } + + get(query) { + return new Promise((resolve, reject) => { + getSession().then((api) => { + let url = this.resource; + if (Object.keys(query).length > 0) url += this.buildQuerystring(query); + api.get(url).then((response) => { + if (response.status >= 400) { + resetSession(); + reject(); + } else { + resolve(response.data); + } + }).catch((e) => { + console.log(e); + reject(e); + }); + }); + }); + } + + getItem(id, embedded = {}) { + return new Promise((resolve, reject) => { + getSession().then((api) => { + let url = `${this.resource}/${id}`; + // in case embedded is specified, append to url + if (Object.keys(embedded).length > 0) { + url += `?${m.buildQueryString({ + embedded: JSON.stringify(this.embedded), + })}`; + } + api.get(url).then((response) => { + if (response.status >= 400) { + resetSession(); + reject(); + } else { + resolve(response.data); + } + }).catch((e) => { + console.log(e); + reject(e); + }); + }); + }); + } + + post(item) { + return new Promise((resolve, reject) => { + getSession().then((api) => { + api.post(this.resource, { + data: item, + }).then((response) => { + if (response.status >= 400) { + resetSession(); + reject(); + } else { + resolve(response.data); + } + }).catch((e) => { + console.log(e); + reject(e); + }); + }); + }); + } + + patch(item) { + return new Promise((resolve, reject) => { + getSession().then((api) => { + // not all fields in the item can be patched. We filter out the fields + // we are allowed to send + const submitData = {}; + this.patchFields.forEach((key) => { submitData[key] = this.data[key]; }); + + api.patch(`${this.resource}/${item._id}`, { + headers: { 'If-Match': item._etag }, + data: submitData, + }).then((response) => { + if (response.status >= 400) { + resetSession(); + reject(); + } else { + resolve(response.data); + } + }).catch((e) => { + console.log(e); + reject(e); + }); + }); + }); + } + + delete(item) { + return new Promise((resolve, reject) => { + getSession().then((api) => { + api.delete(`${this.resource}/${item._id}`, { + headers: { 'If-Match': item._etag }, + }).then((response) => { + if (response.status >= 400) { + resetSession(); + reject(); + } else { + resolve(); + } + }).catch((e) => { + console.log(e); + reject(e); + }); + }); + }); + } +} diff --git a/src/config.json b/src/config.json index 7369fd494bc01d0e35323db11637d18e2b25332e..980c1971aefb30ff4d6ef8a374029d9e6f3f5f8d 100644 --- a/src/config.json +++ b/src/config.json @@ -1,6 +1,6 @@ { "apiUrl": "https://amiv-api.ethz.ch/", - "Events": { + "events": { "keyDescriptors": { "title_de": "Titel auf Deutsch", "title_en": "Title in English", @@ -41,7 +41,7 @@ "priority" ] }, - "Users": { + "users": { "keyDescriptors": { "legi": "Legi Number", "firstname": "First Name", @@ -60,6 +60,13 @@ "legi", "membership" ], + "searchKeys": [ + "firstname", + "lastname", + "nethz", + "legi", + "department" + ], "patchableKeys": [ "firstname", "lastname", @@ -67,5 +74,18 @@ "membership", "gender" ] + }, + "groups": { + "keyDescriptors": { + "name": "Name" + }, + "searchKeys": ["name"], + "patchableKeys": ["name"] + }, + "groupmemberships": { + "patchableKeys": ["user", "group"] + }, + "eventsignups": { + "patchableKeys": ["event"] } } diff --git a/src/eventTool.js b/src/eventTool.js index f455fda56dbc6622b5653c485461b1fb5f0b1dce..2cdf9fa59ef35f55b9cf7a5e64c1ba79d6b3205b 100644 --- a/src/eventTool.js +++ b/src/eventTool.js @@ -2,7 +2,7 @@ import ItemView from './views/itemView'; import EditView from './views/editView'; import { inputGroup, selectGroup, submitButton } from './views/elements'; import TableView from './views/tableView'; -import { Events as config } from './config.json'; +import { events as config } from './config.json'; const m = require('mithril'); diff --git a/src/userTool.js b/src/userTool.js index f2baa5ce0995ba936a9586ed67297a12edf014e5..8e302c33a39529787c05d0caeb235634d505c7d9 100644 --- a/src/userTool.js +++ b/src/userTool.js @@ -3,7 +3,7 @@ import EditView from './views/editView'; import TableView from './views/tableView'; import { inputGroup, selectGroup, submitButton } from './views/elements'; import SelectList from './views/selectList'; -import { Users as config } from './config.json'; +import { users as config } from './config.json'; import { getSession } from './auth'; const m = require('mithril'); @@ -59,6 +59,7 @@ class UserView extends ItemView { m(TableView, { resource: 'groupmemberships', keys: ['group.name', 'expiry'], + searchKeys: ['group.name'], query: { where: { user: this.id }, embedded: { group: 1 }, diff --git a/src/views/itemView.js b/src/views/itemView.js index edc23f4fdd3ef474860e51122d7de5f5df5726dc..bed55516635cb9327af137a005e1a894f9aa1d9b 100644 --- a/src/views/itemView.js +++ b/src/views/itemView.js @@ -1,4 +1,4 @@ -import { getSession } from '../auth'; +import { ResourceHandler } from '../auth'; const m = require('mithril'); @@ -9,46 +9,18 @@ export default class ItemView { * - call constructor with 'resource' * - either make sure m.route.params('id') exists or set this.id in * constructor - * - * Provides Methods: - * - loadItemData: Loads data specified by resource and id into this.data - * (is per default called by oninit) */ constructor(resource, embedded) { this.data = null; this.id = m.route.param('id'); - this.resource = resource; - this.embedded = embedded; - } - - loadItemData(session) { - let url = `${this.resource}/${this.id}`; - if (this.embedded) { - url += `?${m.buildQueryString({ embedded: JSON.stringify(this.embedded) })}`; - //url += `?embedded=${JSON.stringify(this.embedded)}`; - } - - m.request({ - url: `https://amiv-api.ethz.ch/${url}`, - headers: { - Authorization: "ZF3D6SxEK1TvmcZ9qEGB/VUTo+8Td3UpyOPZJQ+WzgufoAJpmmirIiUTo84QDdCPtzOUiS47OnoXdpXo1jSGWWACjweLABGinKntKMd8QUQ7ESsYA6F4SQZ3nMr6csAP3EBB1MKKPa12i9lvWCOJlt4SwCkZf6MiExeTXsfNldw8z25bXHkivCaXbdD67mogum19w22rj8dNUdafGA51dp146NVpfhXDNtRFHtsHw0jPVETfbt+mN+0QrgQ0LdI6BdeBQhFPVL2zQuHRR6JmnA1m1dMji5DVFzNCRHDm0l2SZfOqrw9nFtkUegd86KooNrT6xoXrfH7q7jaaeRer7Q==" - } - }).then((response) => { - console.log(response) - }) - - console.log(url); - session.get(url).then((response) => { - this.data = response.data; - m.redraw(); - }); + this.handler = new ResourceHandler(resource); + this.embedded = embedded || {}; } oninit() { - getSession().then((apiSession) => { - this.loadItemData(apiSession); - }).catch(() => { - m.route.set('/login'); + this.handler.getItem(this.id, this.embedded).then((item) => { + this.data = item; + m.redraw(); }); } } diff --git a/src/views/tableView.js b/src/views/tableView.js index 99cc1c220812587dd39b927a14ab47fe179a37d0..33c4a00f0a9e38bfb518f27c901f31b9552cd47d 100644 --- a/src/views/tableView.js +++ b/src/views/tableView.js @@ -1,4 +1,4 @@ -import { getSession } from '../auth'; +import { ResourceHandler } from '../auth'; const m = require('mithril'); @@ -45,69 +45,24 @@ export default class TableView { titles, resource, query = false, + searchKeys = false, onAdd = () => {}, }, }) { this.items = []; this.showKeys = keys; this.titles = titles || keys; - this.resource = resource; + this.handler = new ResourceHandler(resource, searchKeys); // the querystring is either given or will be parsed from the url this.query = query || m.route.param(); this.onAdd = onAdd; } - // definitions of query parameters in addition to API go here - buildQuerystring() { - const queryKeys = Object.keys(this.query); - - if (queryKeys.length === 0) return ''; - - const query = {}; - - if ('search' in this.query && this.query.search.length > 0) { - // translate search into where, we just look if any field contains search - const searchQuery = { - $or: this.showKeys.map((key) => { - const fieldQuery = {}; - fieldQuery[key] = this.query.search; - return fieldQuery; - }), - }; - - // if there exists already a where-filter, AND them together - if ('where' in this.query) { - query.where = JSON.stringify({ $and: [searchQuery, this.query.where] }); - } else { - query.where = JSON.stringify(searchQuery); - } - } else { - query.where = JSON.stringify(this.query.where); - } - - // add all other keys - queryKeys.filter(key => (key !== 'where' && key !== 'search')) - .forEach((key) => { query[key] = JSON.stringify(this.query[key]); }); - - console.log(query); - - // now we can acutally build the query string - return `?${m.buildQueryString(query)}`; - } - buildList() { - getSession().then((apiSession) => { - let url = this.resource; - if (Object.keys(this.query).length > 0) url += this.buildQuerystring(); - apiSession.get(url).then((response) => { - this.items = response.data._items; - console.log(this.items); - m.redraw(); - }).catch((e) => { - console.log(e); - }); - }).catch(() => { - m.route.set('/login'); + this.handler.get(this.query).then((data) => { + this.items = data._items; + console.log(this.items); + m.redraw(); }); }