Work in progress on a React-based error reporting mechanism.
The idea is that REST handlers always fail with throwing an Error (subclass of InteroperableError). The InteroperableError contains type and data field which are JSON-serialized and sent to client. It's up to the client to interpret the error and choose an appropriate way to present it.
This commit is contained in:
parent
4504d539c5
commit
79ea9e1897
10 changed files with 304 additions and 142 deletions
53
app.js
53
app.js
|
@ -44,6 +44,8 @@ const reports = require('./routes/reports');
|
|||
const reportsTemplates = require('./routes/report-templates');
|
||||
const namespaces = require('./routes/namespaces');
|
||||
|
||||
const interoperableErrors = require('./lib/interoperable-errors');
|
||||
|
||||
const app = express();
|
||||
|
||||
// view engine setup
|
||||
|
@ -245,11 +247,27 @@ if (app.get('env') === 'development') {
|
|||
if (!err) {
|
||||
return next();
|
||||
}
|
||||
res.status(err.status || 500);
|
||||
res.render('error', {
|
||||
message: err.message,
|
||||
error: err
|
||||
});
|
||||
|
||||
if (req.needsJSONResponse) {
|
||||
const resp = {
|
||||
message: err.message,
|
||||
error: err
|
||||
};
|
||||
|
||||
if (err instanceof InteroperableError) {
|
||||
resp.type = err.type;
|
||||
resp.data = err.data;
|
||||
}
|
||||
|
||||
res.status(err.status || 500).json(resp);
|
||||
} else {
|
||||
res.status(err.status || 500);
|
||||
res.render('error', {
|
||||
message: err.message,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -259,11 +277,26 @@ app.use((err, req, res, next) => {
|
|||
if (!err) {
|
||||
return next();
|
||||
}
|
||||
res.status(err.status || 500);
|
||||
res.render('error', {
|
||||
message: err.message,
|
||||
error: {}
|
||||
});
|
||||
|
||||
if (req.needsJSONResponse) {
|
||||
const resp = {
|
||||
message: err.message,
|
||||
error: {}
|
||||
};
|
||||
|
||||
if (err instanceof interoperableErrors.InteroperableError) {
|
||||
resp.type = err.type;
|
||||
resp.data = err.data;
|
||||
}
|
||||
|
||||
res.status(err.status || 500).json(resp);
|
||||
} else {
|
||||
res.status(err.status || 500);
|
||||
res.render('error', {
|
||||
message: err.message,
|
||||
error: {}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
|
|
|
@ -5,7 +5,6 @@ import axios from 'axios';
|
|||
import Immutable from 'immutable';
|
||||
import { translate } from 'react-i18next';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button } from './page.js';
|
||||
|
||||
const FormState = {
|
||||
Loading: 0,
|
||||
|
@ -13,12 +12,11 @@ const FormState = {
|
|||
Ready: 2
|
||||
};
|
||||
|
||||
|
||||
@translate()
|
||||
class Form extends Component {
|
||||
static propTypes = {
|
||||
stateOwner: PropTypes.object.isRequired,
|
||||
onSubmit: PropTypes.func
|
||||
onSubmitAsync: PropTypes.func
|
||||
}
|
||||
|
||||
static childContextTypes = {
|
||||
|
@ -31,10 +29,27 @@ class Form extends Component {
|
|||
};
|
||||
}
|
||||
|
||||
async onSubmit(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
const t = this.props.t;
|
||||
|
||||
if (this.props.onSubmitAsync) {
|
||||
this.props.stateOwner.disableForm();
|
||||
this.props.stateOwner.setFormStatusMessage(t('Submitting...'));
|
||||
|
||||
await this.props.onSubmitAsync(evt);
|
||||
|
||||
this.props.stateOwner.setFormStatusMessage();
|
||||
this.props.stateOwner.enableForm();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const t = this.props.t;
|
||||
const owner = this.props.stateOwner;
|
||||
const props = this.props;
|
||||
const statusMessage = owner.getFormStatusMessage();
|
||||
|
||||
if (!owner.isFormReady()) {
|
||||
if (owner.isFormWithLoadingNotice()) {
|
||||
|
@ -44,8 +59,11 @@ class Form extends Component {
|
|||
}
|
||||
} else {
|
||||
return (
|
||||
<form className="form-horizontal" onSubmit={props.onSubmit}>
|
||||
{props.children}
|
||||
<form className="form-horizontal" onSubmit={::this.onSubmit}>
|
||||
<fieldset disabled={owner.isFormDisabled()}>
|
||||
{props.children}
|
||||
</fieldset>
|
||||
{statusMessage && <p className="col-sm-10 col-sm-offset-2 alert alert-info mt-form-status" role="alert">{owner.getFormStatusMessage()}</p>}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
@ -84,7 +102,7 @@ class InputField extends Component {
|
|||
const htmlId = 'form_' + id;
|
||||
|
||||
return wrapInput(id, htmlId, owner, props.label,
|
||||
<input type="text" value={owner.getFormState(id)} placeholder={props.placeholder} id={htmlId} className="form-control" aria-describedby={htmlId + '_help'} onChange={owner.bindToFormState(id)}/>
|
||||
<input type="text" value={owner.getFormValue(id)} placeholder={props.placeholder} id={htmlId} className="form-control" aria-describedby={htmlId + '_help'} onChange={owner.bindToFormValue(id)}/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +125,7 @@ class TextArea extends Component {
|
|||
const htmlId = 'form_' + id;
|
||||
|
||||
return wrapInput(id, htmlId, owner, props.label,
|
||||
<textarea id={htmlId} value={owner.getFormState(id)} className="form-control" aria-describedby={htmlId + '_help'} onChange={owner.bindToFormState(id)}></textarea>
|
||||
<textarea id={htmlId} value={owner.getFormValue(id)} className="form-control" aria-describedby={htmlId + '_help'} onChange={owner.bindToFormValue(id)}></textarea>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -124,8 +142,60 @@ class ButtonRow extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
class Button extends Component {
|
||||
static propTypes = {
|
||||
onClickAsync: PropTypes.func,
|
||||
onClick: PropTypes.func,
|
||||
label: PropTypes.string,
|
||||
icon: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
type: PropTypes.string
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
stateOwner: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
async onClick(evt) {
|
||||
if (this.props.onClick) {
|
||||
evt.preventDefault();
|
||||
|
||||
onClick(evt);
|
||||
|
||||
} else if (this.props.onClickAsync) {
|
||||
evt.preventDefault();
|
||||
|
||||
this.context.stateOwner.disableForm();
|
||||
await this.props.onClickAsync(evt);
|
||||
this.context.stateOwner.enableForm();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const props = this.props;
|
||||
|
||||
let className = 'btn';
|
||||
if (props.className) {
|
||||
className = className + ' ' + props.className;
|
||||
}
|
||||
|
||||
let type = props.type || 'button';
|
||||
|
||||
let icon;
|
||||
if (props.icon) {
|
||||
icon = <span className={'glyphicon glyphicon-' + props.icon}></span>
|
||||
}
|
||||
|
||||
let iconSpacer;
|
||||
if (props.icon && props.label) {
|
||||
iconSpacer = ' ';
|
||||
}
|
||||
|
||||
return (
|
||||
<button type={type} className={className} onClick={::this.onClick}>{icon}{iconSpacer}{props.label}</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function withForm(target) {
|
||||
const inst = target.prototype;
|
||||
|
@ -134,76 +204,89 @@ function withForm(target) {
|
|||
const state = this.state || {};
|
||||
|
||||
state.formState = Immutable.Map({
|
||||
_state: FormState.Loading,
|
||||
_isValidationShown: false
|
||||
state: FormState.Loading,
|
||||
isValidationShown: false,
|
||||
isDisabled: false,
|
||||
statusMessage: '',
|
||||
data: Immutable.Map()
|
||||
});
|
||||
|
||||
this.state = state;
|
||||
};
|
||||
|
||||
inst.populateFormStateFromURL = function(url) {
|
||||
inst.populateFormValuesFromURL = function(url) {
|
||||
setTimeout(() => {
|
||||
this.setState(previousState => {
|
||||
if (previousState.formState.get('_state') === FormState.Loading) {
|
||||
if (previousState.formState.get('state') === FormState.Loading) {
|
||||
return {
|
||||
formState: previousState.formState.set('_state', FormState.LoadingWithNotice)
|
||||
formState: previousState.formState.set('state', FormState.LoadingWithNotice)
|
||||
};
|
||||
}
|
||||
});
|
||||
}, 500);
|
||||
|
||||
axios.get(url).then(response => {
|
||||
this.populateFormState(response.data);
|
||||
this.populateFormValues(response.data);
|
||||
});
|
||||
};
|
||||
|
||||
inst.populateFormState = function(data) {
|
||||
inst.populateFormValues = function(data) {
|
||||
this.setState(previousState => ({
|
||||
formState: previousState.formState.withMutations(state => {
|
||||
state.set('_state', FormState.Ready);
|
||||
state.set('state', FormState.Ready);
|
||||
|
||||
for (const key in data) {
|
||||
state.set(key, Immutable.Map({
|
||||
value: data[key]
|
||||
}));
|
||||
}
|
||||
state.update('data', stateData => stateData.withMutations(mutableStateData => {
|
||||
for (const key in data) {
|
||||
mutableStateData.set(key, Immutable.Map({
|
||||
value: data[key]
|
||||
}));
|
||||
}
|
||||
|
||||
this.validateFormState(state);
|
||||
this.validateFormValues(mutableStateData);
|
||||
}));
|
||||
})
|
||||
}));
|
||||
};
|
||||
|
||||
inst.updateFormState = function(key, value) {
|
||||
inst.updateFormValue = function(key, value) {
|
||||
this.setState(previousState => ({
|
||||
formState: previousState.formState.withMutations(state => {
|
||||
state.setIn([key, 'value'], value);
|
||||
this.validateFormState(state);
|
||||
})
|
||||
formState: previousState.formState.update('data', stateData => stateData.withMutations(mutableStateData => {
|
||||
mutableStateData.setIn([key, 'value'], value);
|
||||
this.validateFormValues(mutableStateData);
|
||||
}))
|
||||
}));
|
||||
};
|
||||
|
||||
inst.bindToFormState = function(name) {
|
||||
return evt => this.updateFormState(name, evt.target.value);
|
||||
inst.bindToFormValue = function(name) {
|
||||
return evt => this.updateFormValue(name, evt.target.value);
|
||||
};
|
||||
|
||||
inst.getFormState = function(name) {
|
||||
return this.state.formState.getIn([name, 'value']);
|
||||
inst.getFormValue = function(name) {
|
||||
return this.state.formState.getIn(['data', name, 'value']);
|
||||
};
|
||||
|
||||
inst.getFormValues = function(name) {
|
||||
return this.state.formState.get('data').map(attr => attr.get('value')).toJS();
|
||||
};
|
||||
|
||||
inst.getFormError = function(name) {
|
||||
return this.state.formState.getIn([name, 'error']);
|
||||
return this.state.formState.getIn(['data', name, 'error']);
|
||||
};
|
||||
|
||||
inst.isFormWithLoadingNotice = function() {
|
||||
return this.state.formState.get('_state') === FormState.LoadingWithNotice;
|
||||
return this.state.formState.get('state') === FormState.LoadingWithNotice;
|
||||
};
|
||||
|
||||
inst.isFormLoading = function() {
|
||||
return this.state.formState.get('state') === FormState.Loading || this.state.formState.get('state') === FormState.LoadingWithNotice;
|
||||
};
|
||||
|
||||
inst.isFormReady = function() {
|
||||
return this.state.formState.get('_state') === FormState.Ready;
|
||||
return this.state.formState.get('state') === FormState.Ready;
|
||||
};
|
||||
|
||||
inst.isFormValidationShown = function() {
|
||||
return this.state.formState.get('_isValidationShown');
|
||||
return this.state.formState.get('isValidationShown');
|
||||
};
|
||||
|
||||
inst.addFormValidationClass = function(className, name) {
|
||||
|
@ -228,12 +311,37 @@ function withForm(target) {
|
|||
};
|
||||
|
||||
inst.showFormValidation = function() {
|
||||
this.setState(previousState => ({formState: previousState.formState.set('_isValidationShown', true)}));
|
||||
this.setState(previousState => ({formState: previousState.formState.set('isValidationShown', true)}));
|
||||
};
|
||||
|
||||
inst.hideFormValidation = function() {
|
||||
this.setState(previousState => ({formState: previousState.formState.set('_isValidationShown', false)}));
|
||||
this.setState(previousState => ({formState: previousState.formState.set('isValidationShown', false)}));
|
||||
};
|
||||
|
||||
inst.isFormWithoutErrors = function() {
|
||||
return !this.state.formState.get('data').find(attr => attr.get('error'));
|
||||
};
|
||||
|
||||
inst.getFormStatusMessage = function() {
|
||||
return this.state.formState.get('statusMessage');
|
||||
};
|
||||
|
||||
inst.setFormStatusMessage = function(message) {
|
||||
this.setState(previousState => ({formState: previousState.formState.set('statusMessage', message)}));
|
||||
};
|
||||
|
||||
inst.enableForm = function() {
|
||||
this.setState(previousState => ({formState: previousState.formState.set('isDisabled', false)}));
|
||||
};
|
||||
|
||||
inst.disableForm = function() {
|
||||
this.setState(previousState => ({formState: previousState.formState.set('isDisabled', true)}));
|
||||
};
|
||||
|
||||
inst.isFormDisabled = function() {
|
||||
return this.state.formState.get('isDisabled');
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -4,4 +4,9 @@
|
|||
|
||||
.mt-button-row > button:last-child {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.mt-form-status {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
|
@ -5,7 +5,6 @@ import { translate } from 'react-i18next';
|
|||
import PropTypes from 'prop-types';
|
||||
import { withRouter } from 'react-router';
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom'
|
||||
|
||||
import './page.css';
|
||||
|
||||
|
||||
|
@ -138,41 +137,6 @@ class Toolbar extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
class Button extends Component {
|
||||
static propTypes = {
|
||||
onClick: PropTypes.func,
|
||||
label: PropTypes.string,
|
||||
icon: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
type: PropTypes.string
|
||||
}
|
||||
|
||||
render() {
|
||||
const props = this.props;
|
||||
|
||||
let className = 'btn';
|
||||
if (props.className) {
|
||||
className = className + ' ' + props.className;
|
||||
}
|
||||
|
||||
let type = props.type || 'button';
|
||||
|
||||
let icon;
|
||||
if (props.icon) {
|
||||
icon = <span className={'glyphicon glyphicon-' + props.icon}></span>
|
||||
}
|
||||
|
||||
let iconSpacer;
|
||||
if (props.icon && props.label) {
|
||||
iconSpacer = ' ';
|
||||
}
|
||||
|
||||
return (
|
||||
<button type={type} className={className} onClick={props.onClick}>{icon}{iconSpacer}{props.label}</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NavButton extends Component {
|
||||
static propTypes = {
|
||||
label: PropTypes.string,
|
||||
|
@ -190,6 +154,46 @@ class NavButton extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
class Button extends Component {
|
||||
static propTypes = {
|
||||
onClick: PropTypes.func,
|
||||
label: PropTypes.string,
|
||||
icon: PropTypes.string,
|
||||
className: PropTypes.string
|
||||
}
|
||||
|
||||
async onClick(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
if (this.props.onClick) {
|
||||
onClick(evt);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const props = this.props;
|
||||
|
||||
let className = 'btn';
|
||||
if (props.className) {
|
||||
className = className + ' ' + props.className;
|
||||
}
|
||||
|
||||
let icon;
|
||||
if (props.icon) {
|
||||
icon = <span className={'glyphicon glyphicon-' + props.icon}></span>
|
||||
}
|
||||
|
||||
let iconSpacer;
|
||||
if (props.icon && props.label) {
|
||||
iconSpacer = ' ';
|
||||
}
|
||||
|
||||
return (
|
||||
<button type="button" className={className} onClick={::this.onClick}>{icon}{iconSpacer}{props.label}</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
Section,
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
import React, { Component } from 'react';
|
||||
import { translate } from 'react-i18next';
|
||||
import csfrToken from 'csfrToken';
|
||||
import { withForm, Form, InputField, TextArea, ButtonRow, Button } from '../lib/form';
|
||||
import { withForm, Form, InputField, TextArea, ButtonRow, Button} from '../lib/form';
|
||||
import { Title } from "../lib/page";
|
||||
import axios from 'axios';
|
||||
|
||||
@translate()
|
||||
@withForm
|
||||
|
@ -15,11 +16,11 @@ export default class Edit extends Component {
|
|||
this.nsId = parseInt(this.props.match.params.nsId);
|
||||
|
||||
this.initFormState();
|
||||
this.populateFormStateFromURL(`/namespaces/rest/namespaces/${this.nsId}`);
|
||||
this.populateFormValuesFromURL(`/namespaces/rest/namespaces/${this.nsId}`);
|
||||
}
|
||||
|
||||
|
||||
validateFormState(state) {
|
||||
validateFormValues(state) {
|
||||
const t = this.props.t;
|
||||
|
||||
if (!state.getIn(['name','value']).trim()) {
|
||||
|
@ -29,13 +30,22 @@ export default class Edit extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
submitHandler(evt) {
|
||||
evt.preventDefault();
|
||||
this.showFormValidation();
|
||||
async submitHandler() {
|
||||
if (this.isFormWithoutErrors()) {
|
||||
const data = this.getFormValues();
|
||||
console.log(data);
|
||||
|
||||
const response = await axios.put(`/namespaces/rest/namespaces/${this.nsId}`);
|
||||
console.log(response);
|
||||
|
||||
} else {
|
||||
this.showFormValidation();
|
||||
}
|
||||
}
|
||||
|
||||
deleteHandler() {
|
||||
this.hideFormValidation();
|
||||
async deleteHandler() {
|
||||
this.setFormStatusMessage('Deleting namespace')
|
||||
this.setFormStatusMessage()
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -45,13 +55,13 @@ export default class Edit extends Component {
|
|||
<div>
|
||||
<Title>{t('Edit Namespace')}</Title>
|
||||
|
||||
<Form stateOwner={this} onSubmit={::this.submitHandler}>
|
||||
<Form stateOwner={this} onSubmitAsync={::this.submitHandler}>
|
||||
<InputField id="name" label={t('Name')} description={t('Namespace Name')}/>
|
||||
<TextArea id="description" label={t('Description')} description={t('Description')}/>
|
||||
|
||||
<ButtonRow>
|
||||
<Button type="submit" className="btn-primary" icon="ok" label={t('Update')}/>
|
||||
<Button className="btn-danger" icon="remove" label={t('Delete Namespace')} onClick={::this.deleteHandler}/>
|
||||
<Button className="btn-danger" icon="remove" label={t('Delete Namespace')} onClickAsync={::this.deleteHandler}/>
|
||||
</ButtonRow>
|
||||
</Form>
|
||||
</div>
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
class InteroperableError extends Error {
|
||||
constructor(type, msg, data) {
|
||||
super(msg);
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = InteroperableError;
|
21
lib/interoperable-errors.js
Normal file
21
lib/interoperable-errors.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
'use strict';
|
||||
|
||||
class InteroperableError extends Error {
|
||||
constructor(type, msg, data) {
|
||||
super(msg);
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
class NotLoggedInError extends InteroperableError {
|
||||
constructor(msg, data) {
|
||||
super('NotLoggedIn', msg, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
InteroperableError,
|
||||
NotLoggedInError
|
||||
};
|
|
@ -3,15 +3,15 @@
|
|||
const knex = require('../knex');
|
||||
const hasher = require('node-object-hash')();
|
||||
const { filterObject } = require('../helpers');
|
||||
const InteroperableError = require('../InteroperableError');
|
||||
const interoperableErrors = require('../interoperable-errors');
|
||||
|
||||
class ChangedError extends InteroperableError {
|
||||
class ChangedError extends interoperableErrors.InteroperableError {
|
||||
constructor(msg, data) {
|
||||
super('namespaces.ChangedError', msg, data);
|
||||
}
|
||||
}
|
||||
|
||||
class NotFoundError extends InteroperableError {
|
||||
class NotFoundError extends interoperableErrors.InteroperableError {
|
||||
constructor(msg, data) {
|
||||
super('namespaces.NotFoundError', msg, data);
|
||||
}
|
||||
|
|
|
@ -1,28 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
const express = require('express');
|
||||
const InteroperableError = require('./InteroperableError');
|
||||
|
||||
function safeHandler(handler) {
|
||||
return function(req, res) {
|
||||
handler(req, res).catch(error => res.status(500).send(error.message));
|
||||
};
|
||||
}
|
||||
|
||||
function safeJSONHandler(handler) {
|
||||
return function(req, res) {
|
||||
handler(req, res).catch(error => {
|
||||
const resp = {
|
||||
message: error.message
|
||||
};
|
||||
|
||||
if (error instanceof InteroperableError) {
|
||||
resp.type = error.type;
|
||||
resp.data = error.data;
|
||||
}
|
||||
|
||||
res.status(500).json(resp);
|
||||
});
|
||||
function safeAsyncHandler(handler) {
|
||||
return function(req, res, next) {
|
||||
handler(req, res).catch(error => next(error));
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -42,12 +24,10 @@ function replaceLast(elems, replaceFn) {
|
|||
function create() {
|
||||
const router = new express.Router();
|
||||
|
||||
router.getAsync = (path, ...handlers) => router.get(path, ...replaceLast(handlers, safeHandler));
|
||||
|
||||
router.getAsyncJSON = (path, ...handlers) => router.get(path, ...replaceLast(handlers, safeJSONHandler));
|
||||
router.postAsyncJSON = (path, ...handlers) => router.post(path, ...replaceLast(handlers, safeJSONHandler));
|
||||
router.putAsyncJSON = (path, ...handlers) => router.put(path, ...replaceLast(handlers, safeJSONHandler));
|
||||
router.deleteAsyncJSON = (path, ...handlers) => router.delete(path, ...replaceLast(handlers, safeJSONHandler));
|
||||
router.getAsync = (path, ...handlers) => router.get(path, ...replaceLast(handlers, safeAsyncHandler));
|
||||
router.postAsync = (path, ...handlers) => router.post(path, ...replaceLast(handlers, safeAsyncHandler));
|
||||
router.putAsync = (path, ...handlers) => router.put(path, ...replaceLast(handlers, safeAsyncHandler));
|
||||
router.deleteAsync = (path, ...handlers) => router.delete(path, ...replaceLast(handlers, safeAsyncHandler));
|
||||
|
||||
return router;
|
||||
}
|
||||
|
|
|
@ -4,28 +4,31 @@ const passport = require('../lib/passport');
|
|||
const router = require('../lib/router-async').create();
|
||||
const _ = require('../lib/translate')._;
|
||||
const namespaces = require('../lib/models/namespaces');
|
||||
const interoperableErrors = require('../lib/interoperable-errors');
|
||||
|
||||
router.all('/rest/*', (req, res, next) => {
|
||||
req.needsJSONResponse = true;
|
||||
|
||||
router.all('/*', (req, res, next) => {
|
||||
if (!req.user) {
|
||||
req.flash('danger', _('Need to be logged in to access restricted content'));
|
||||
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
||||
throw new interoperableErrors.NotLoggedInError();
|
||||
}
|
||||
// res.setSelectedMenu('namespaces');
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
router.getAsyncJSON('/rest/namespaces/:nsId', async (req, res) => {
|
||||
|
||||
router.getAsync('/rest/namespaces/:nsId', async (req, res) => {
|
||||
const ns = await namespaces.getById(req.params.nsId);
|
||||
return res.json(ns);
|
||||
});
|
||||
|
||||
router.postAsyncJSON('/rest/namespaces', passport.csrfProtection, async (req, res) => {
|
||||
router.postAsync('/rest/namespaces', passport.csrfProtection, async (req, res) => {
|
||||
console.log(req.body);
|
||||
// await namespaces.create(req.body);
|
||||
return res.json();
|
||||
});
|
||||
|
||||
router.putAsyncJSON('/rest/namespaces/:nsId', passport.csrfProtection, async (req, res) => {
|
||||
router.putAsync('/rest/namespaces/:nsId', passport.csrfProtection, async (req, res) => {
|
||||
console.log(req.body);
|
||||
ns = req.body;
|
||||
ns.id = req.params.nsId;
|
||||
|
@ -34,13 +37,13 @@ router.putAsyncJSON('/rest/namespaces/:nsId', passport.csrfProtection, async (re
|
|||
return res.json();
|
||||
});
|
||||
|
||||
router.deleteAsyncJSON('/rest/namespaces/:nsId', passport.csrfProtection, async (req, res) => {
|
||||
router.deleteAsync('/rest/namespaces/:nsId', passport.csrfProtection, async (req, res) => {
|
||||
console.log(req.body);
|
||||
// await namespaces.remove(req.params.nsId);
|
||||
return res.json();
|
||||
});
|
||||
|
||||
router.getAsyncJSON('/rest/namespacesTree', async (req, res) => {
|
||||
router.getAsync('/rest/namespacesTree', async (req, res) => {
|
||||
const entries = {};
|
||||
|
||||
/* Example of roots:
|
||||
|
@ -93,6 +96,15 @@ router.getAsyncJSON('/rest/namespacesTree', async (req, res) => {
|
|||
return res.json(roots);
|
||||
});
|
||||
|
||||
router.all('/*', (req, res, next) => {
|
||||
if (!req.user) {
|
||||
req.flash('danger', _('Need to be logged in to access restricted content'));
|
||||
return res.redirect('/users/login?next=' + encodeURIComponent(req.originalUrl));
|
||||
}
|
||||
// res.setSelectedMenu('namespaces');
|
||||
next();
|
||||
});
|
||||
|
||||
router.getAsync('/*', passport.csrfProtection, async (req, res) => {
|
||||
res.render('react-root', {
|
||||
title: _('Namespaces'),
|
||||
|
|
Loading…
Reference in a new issue