Newer
Older
import ClientOAuth2 from 'client-oauth2';
import { Snackbar } from 'polythene-mithril';
import { apiUrl, ownUrl, oAuthID } from 'networkConfig';
import * as localStorage from './localStorage';
import config from './resourceConfig.json';
// Object which stores the current login-state
const APISession = {
authenticated: false,
token: '',
const amivapi = axios.create({
baseURL: apiUrl,
headers: { 'Content-Type': 'application/json' },
validateStatus: () => true,
// OAuth Handler
const oauth = new ClientOAuth2({
clientId: oAuthID,
authorizationUri: `${apiUrl}/oauth`,
redirectUri: `${ownUrl}/oauthcallback`,
APISession.authenticated = false;
APISession.token = '';
localStorage.remove('token');
window.location.replace(oauth.token.getUri());
function checkToken(token) {
// check if a token is still valid
return new Promise((resolve, reject) => {
headers: { 'Content-Type': 'application/json', Authorization: token },
if (response.status === 200) resolve(response.data);
else reject();
}).catch(reject);
});
}
export function checkAuthenticated() {
// return a promise that resolves always, with a bool that shows whether
// the user is authenticated
return new Promise((resolve) => {
if (APISession.authenticated) resolve();
else {
// let's see if we have a stored token
const token = localStorage.get('token');
console.log(`found this token: ${token}`);
if (token !== '') {
// check of token is valid
APISession.token = token;
APISession.authenticated = true;
resolve();
}).catch(resetSession);
} else resetSession();
}
});
}
export function getSession() {
// Promise resolves with authenticated axios-session or fails
return new Promise((resolve) => {
checkAuthenticated().then(() => {
const authenticatedSession = axios.create({
baseURL: apiUrl,
headers: {
'Content-Type': 'application/json',
Authorization: APISession.token,
},
validateStatus: () => true,
});
resolve(authenticatedSession);
}).catch(resetSession);
export function deleteSession() {
return new Promise((resolve, reject) => {
getSession().then((api) => {
api.get(`sessions/${APISession.token}`).then((response) => {
if (response.status === 200) {
api.delete(
`sessions/${response.data._id}`,
{ headers: { 'If-Match': response.data._etag } },
).then((deleteResponse) => {
if (deleteResponse.status === 204) {
resetSession();
resolve(deleteResponse.data);
} else reject();
}).catch(reject);
} else reject();
}).catch(reject);
});
});
}
export function getCurrentUser() {
return APISession.userID;
}
/* Handler to get and manipulate resource items
*
* resource: String of the resource to accessm e,g, 'events'
* searchKeys: keys in the resource item on which to perform search, i.e.
* when search is set, any of these keys may match the search pattern
* E.g. for an event, this may be ['title_de', 'title_en', 'location']
*/
constructor(resource, searchKeys = false) {
// special case for users
if (resource === 'users') this.searchKeys = ['firstname', 'lastname', 'nethz'];
else this.searchKeys = searchKeys || config[resource].searchKeys;

Hermann
committed
this.noPatchKeys = [
'_etag', '_id', '_created', '_links', '_updated',
...(config[resource].notPatchableKeys || [])];
checkAuthenticated().then(() => {
// again special case for users
if (resource === 'users' && APISession.isUserAdmin) {
this.searchKeys = searchKeys || config[resource].searchKeys;
}
});
/*
* query is a dictionary of different queries
* Additional to anything specified from eve
* (http://python-eve.org/features.html#filtering)
* we support the key `search`, which is translated into a `where` filter
*/
buildQuerystring(query) {
const queryKeys = Object.keys(query);
if (queryKeys.length === 0) return '';
const fullQuery = {};
if ('search' in query && query.search && query.search.length > 0 && this.searchKeys) {
// translate search into where, we just look if any field contains search
// The search-string may match any of the keys in the object specified in the
// constructor
const searchQuery = {
$or: this.searchKeys.map((key) => {
const fieldQuery = {};
fieldQuery[key] = {
$regex: `${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);
}
}
// add all other keys
queryKeys.filter(key => (key !== 'where' && key !== 'search'))
.forEach((key) => { fullQuery[key] = JSON.stringify(query[key]); });
// now we can acutally build the query string
return `?${m.buildQueryString(fullQuery)}`;
}
networkError(e) {
console.log(e);
Snackbar.show({ title: 'Network error, try again.', style: { color: 'red' } });
}
// in future, we may communicate based on the data available
// therefore, require data already here
// eslint-disable-next-line no-unused-vars
error422(data) {
Snackbar.show({ title: 'Errors in object, please fix.' });
}
successful(title) {
Snackbar.show({ title, style: { color: 'green' } });
}
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();
Snackbar.show({ title: response.data, style: { color: 'red' } });
this.rights = response.data._links.self.methods;
resolve(response.data);
}
}).catch((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({
})}`;
}
api.get(url).then((response) => {
if (response.status >= 400) {
Snackbar.show({ title: response.data, style: { color: 'red' } });
resetSession();
reject();
} else {
resolve(response.data);
}
}).catch((e) => {
reject(e);
});
});
});
}
post(item) {
return new Promise((resolve, reject) => {
getSession().then((api) => {
api.post(this.resource, item).then((response) => {
this.successful('Creation successful.');
resolve({});
} else if (response.status === 422) {
this.error422(response.data);
reject(response.data);
} else if (response.status >= 400) {
Snackbar.show({ title: response.data, style: { color: 'red' } });
resetSession();
reject();
} else {
resolve(response.data);
}
}).catch((e) => {
reject(e);
});
});
});
}
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
let submitData;
if (formData) {
submitData = new FormData();
Object.keys(item).forEach((key) => {
if (!this.noPatchKeys.includes(key)) {
submitData.append(key, item[key]);
}
});
} else {
submitData = Object.assign({}, item);
this.noPatchKeys.forEach((key) => { delete submitData[key]; });
}

Hermann
committed
api.patch(`${this.resource}/${item._id}`, submitData, {
headers: { 'If-Match': item._etag },
}).then((response) => {
this.error422(response.data);
reject(response.data);
} else if (response.status >= 400) {
Snackbar.show({ title: response.data, style: { color: 'red' } });
resetSession();
reject();
} else {
this.successful('Change successful.');
resolve(response.data);
}
}).catch((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) {
Snackbar.show({ title: response.data, style: { color: 'red' } });
resetSession();
reject();
} else {
this.successful('Delete successful.');
resolve();
}
}).catch((e) => {
reject(e);
});
});
});
}
}
export class OauthRedirect {
view() {
oauth.token.getToken(m.route.get()).then((auth) => {
APISession.token = auth.accessToken;
localStorage.set('token', auth.accessToken);
amivapi.get(`sessions/${auth.accessToken}`, {
headers: { 'Content-Type': 'application/json', Authorization: APISession.token },
}).then((response) => {
console.log(response);
APISession.userID = response.data.user;
m.route.set('/');
}).catch(() => {
resetSession();
});