Merge pull request #46 in DEFINMA/definma-api from proper-indentation to master
* commit '7c5b6ec605e7ecd3d4cc9ba59c41f45ef4783c80': Properly indent all source files
This commit is contained in:
commit
a08bfd9922
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dfop-api",
|
"name": "definma-api",
|
||||||
"version": "1.0.0",
|
"version": "0.9.1",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -9,10 +9,10 @@
|
|||||||
|
|
||||||
/* =================== USAGE ===================
|
/* =================== USAGE ===================
|
||||||
|
|
||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
let app = express();
|
let app = express();
|
||||||
|
|
||||||
=============================================== */
|
=============================================== */
|
||||||
|
|
||||||
/// <reference types="express-serve-static-core" />
|
/// <reference types="express-serve-static-core" />
|
||||||
/// <reference types="serve-static" />
|
/// <reference types="serve-static" />
|
||||||
@ -28,96 +28,96 @@ import * as qs from "qs";
|
|||||||
declare function e(): core.Express;
|
declare function e(): core.Express;
|
||||||
|
|
||||||
declare namespace e {
|
declare namespace e {
|
||||||
/**
|
/**
|
||||||
* This is a built-in middleware function in Express. It parses incoming requests with JSON payloads and is based on
|
* This is a built-in middleware function in Express. It parses incoming requests with JSON payloads and is based on
|
||||||
* body-parser.
|
* body-parser.
|
||||||
* @since 4.16.0
|
* @since 4.16.0
|
||||||
*/
|
*/
|
||||||
let json: typeof bodyParser.json;
|
let json: typeof bodyParser.json;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a built-in middleware function in Express. It parses incoming requests with Buffer payloads and is based
|
* This is a built-in middleware function in Express. It parses incoming requests with Buffer payloads and is based
|
||||||
* on body-parser.
|
* on body-parser.
|
||||||
* @since 4.17.0
|
* @since 4.17.0
|
||||||
*/
|
*/
|
||||||
let raw: typeof bodyParser.raw;
|
let raw: typeof bodyParser.raw;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a built-in middleware function in Express. It parses incoming requests with text payloads and is based on
|
* This is a built-in middleware function in Express. It parses incoming requests with text payloads and is based on
|
||||||
* body-parser.
|
* body-parser.
|
||||||
* @since 4.17.0
|
* @since 4.17.0
|
||||||
*/
|
*/
|
||||||
let text: typeof bodyParser.text;
|
let text: typeof bodyParser.text;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These are the exposed prototypes.
|
* These are the exposed prototypes.
|
||||||
*/
|
*/
|
||||||
let application: Application;
|
let application: Application;
|
||||||
let request: Request;
|
let request: Request;
|
||||||
let response: Response;
|
let response: Response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a built-in middleware function in Express. It serves static files and is based on serve-static.
|
* This is a built-in middleware function in Express. It serves static files and is based on serve-static.
|
||||||
*/
|
*/
|
||||||
let static: typeof serveStatic;
|
let static: typeof serveStatic;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a built-in middleware function in Express. It parses incoming requests with urlencoded payloads and is
|
* This is a built-in middleware function in Express. It parses incoming requests with urlencoded payloads and is
|
||||||
* based on body-parser.
|
* based on body-parser.
|
||||||
* @since 4.16.0
|
* @since 4.16.0
|
||||||
*/
|
*/
|
||||||
let urlencoded: typeof bodyParser.urlencoded;
|
let urlencoded: typeof bodyParser.urlencoded;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a built-in middleware function in Express. It parses incoming request query parameters.
|
* This is a built-in middleware function in Express. It parses incoming request query parameters.
|
||||||
*/
|
*/
|
||||||
export function query(options: qs.IParseOptions | typeof qs.parse): Handler;
|
export function query(options: qs.IParseOptions | typeof qs.parse): Handler;
|
||||||
|
|
||||||
export function Router(options?: RouterOptions): core.Router;
|
export function Router(options?: RouterOptions): core.Router;
|
||||||
|
|
||||||
interface RouterOptions {
|
interface RouterOptions {
|
||||||
/**
|
/**
|
||||||
* Enable case sensitivity.
|
* Enable case sensitivity.
|
||||||
*/
|
*/
|
||||||
caseSensitive?: boolean;
|
caseSensitive?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preserve the req.params values from the parent router.
|
* Preserve the req.params values from the parent router.
|
||||||
* If the parent and the child have conflicting param names, the child’s value take precedence.
|
* If the parent and the child have conflicting param names, the child’s value take precedence.
|
||||||
*
|
*
|
||||||
* @default false
|
* @default false
|
||||||
* @since 4.5.0
|
* @since 4.5.0
|
||||||
*/
|
*/
|
||||||
mergeParams?: boolean;
|
mergeParams?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable strict routing.
|
* Enable strict routing.
|
||||||
*/
|
*/
|
||||||
strict?: boolean;
|
strict?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Application extends core.Application { }
|
interface Application extends core.Application { }
|
||||||
interface CookieOptions extends core.CookieOptions { }
|
interface CookieOptions extends core.CookieOptions { }
|
||||||
interface Errback extends core.Errback { }
|
interface Errback extends core.Errback { }
|
||||||
interface ErrorRequestHandler<P extends core.Params = core.ParamsDictionary, ResBody = any, ReqBody = any,
|
interface ErrorRequestHandler<P extends core.Params = core.ParamsDictionary, ResBody = any, ReqBody = any,
|
||||||
ReqQuery = core.Query>
|
ReqQuery = core.Query>
|
||||||
extends core.ErrorRequestHandler<P, ResBody, ReqBody, ReqQuery> { }
|
extends core.ErrorRequestHandler<P, ResBody, ReqBody, ReqQuery> { }
|
||||||
interface Express extends core.Express { }
|
interface Express extends core.Express { }
|
||||||
interface Handler extends core.Handler { }
|
interface Handler extends core.Handler { }
|
||||||
interface IRoute extends core.IRoute { }
|
interface IRoute extends core.IRoute { }
|
||||||
interface IRouter extends core.IRouter { }
|
interface IRouter extends core.IRouter { }
|
||||||
interface IRouterHandler<T> extends core.IRouterHandler<T> { }
|
interface IRouterHandler<T> extends core.IRouterHandler<T> { }
|
||||||
interface IRouterMatcher<T> extends core.IRouterMatcher<T> { }
|
interface IRouterMatcher<T> extends core.IRouterMatcher<T> { }
|
||||||
interface MediaType extends core.MediaType { }
|
interface MediaType extends core.MediaType { }
|
||||||
interface NextFunction extends core.NextFunction { }
|
interface NextFunction extends core.NextFunction { }
|
||||||
interface Request<P extends core.Params = core.ParamsDictionary, ResBody = any, ReqBody = any,
|
interface Request<P extends core.Params = core.ParamsDictionary, ResBody = any, ReqBody = any,
|
||||||
ReqQuery = core.Query> extends core.Request<P, ResBody, ReqBody, ReqQuery> { }
|
ReqQuery = core.Query> extends core.Request<P, ResBody, ReqBody, ReqQuery> { }
|
||||||
interface RequestHandler<P extends core.Params = core.ParamsDictionary, ResBody = any, ReqBody = any,
|
interface RequestHandler<P extends core.Params = core.ParamsDictionary, ResBody = any, ReqBody = any,
|
||||||
ReqQuery = core.Query> extends core.RequestHandler<P, ResBody, ReqBody, ReqQuery> { }
|
ReqQuery = core.Query> extends core.RequestHandler<P, ResBody, ReqBody, ReqQuery> { }
|
||||||
interface RequestParamHandler extends core.RequestParamHandler { }
|
interface RequestParamHandler extends core.RequestParamHandler { }
|
||||||
export interface Response<ResBody = any> extends core.Response<ResBody> { }
|
export interface Response<ResBody = any> extends core.Response<ResBody> { }
|
||||||
interface Router extends core.Router { }
|
interface Router extends core.Router { }
|
||||||
interface Send extends core.Send { }
|
interface Send extends core.Send { }
|
||||||
}
|
}
|
||||||
|
|
||||||
export = e;
|
export = e;
|
||||||
|
@ -9,111 +9,111 @@ import globals from '../globals';
|
|||||||
// req.authDetails returns eg. {methods: ['basic'], username: 'johndoe', level: 'write'}
|
// req.authDetails returns eg. {methods: ['basic'], username: 'johndoe', level: 'write'}
|
||||||
|
|
||||||
module.exports = async (req, res, next) => {
|
module.exports = async (req, res, next) => {
|
||||||
let givenMethod = ''; // authorization method given by client, basic taken preferred
|
let givenMethod = ''; // authorization method given by client, basic taken preferred
|
||||||
let user = {name: '', level: '', id: '', location: '', models: []}; // user object
|
let user = {name: '', level: '', id: '', location: '', models: []}; // user object
|
||||||
|
|
||||||
// test authentications
|
// test authentications
|
||||||
const userBasic = await basic(req, next);
|
const userBasic = await basic(req, next);
|
||||||
|
|
||||||
if (userBasic) { // basic available
|
if (userBasic) { // basic available
|
||||||
givenMethod = 'basic';
|
givenMethod = 'basic';
|
||||||
user = userBasic;
|
user = userBasic;
|
||||||
}
|
}
|
||||||
else { // if basic not available, test key
|
else { // if basic not available, test key
|
||||||
const userKey = await key(req, next);
|
const userKey = await key(req, next);
|
||||||
if (userKey) {
|
if (userKey) {
|
||||||
givenMethod = 'key';
|
givenMethod = 'key';
|
||||||
user = userKey;
|
user = userKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req.auth = (res, levels, method = 'all') => {
|
req.auth = (res, levels, method = 'all') => {
|
||||||
if (givenMethod === method || (method === 'all' && givenMethod !== '')) { // method is available
|
if (givenMethod === method || (method === 'all' && givenMethod !== '')) { // method is available
|
||||||
if (levels.indexOf(user.level) > -1) { // level is available
|
if (levels.indexOf(user.level) > -1) { // level is available
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.status(403).json({status: 'Forbidden'});
|
res.status(403).json({status: 'Forbidden'});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.status(401).json({status: 'Unauthorized'});
|
res.status(401).json({status: 'Unauthorized'});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req.authDetails = {
|
req.authDetails = {
|
||||||
method: givenMethod,
|
method: givenMethod,
|
||||||
username: user.name,
|
username: user.name,
|
||||||
level: user.level,
|
level: user.level,
|
||||||
id: user.id,
|
id: user.id,
|
||||||
location: user.location,
|
location: user.location,
|
||||||
models: user.models
|
models: user.models
|
||||||
};
|
};
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function basic (req, next): any { // checks basic auth and returns changed user object
|
function basic (req, next): any { // checks basic auth and returns changed user object
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const auth = basicAuth(req);
|
const auth = basicAuth(req);
|
||||||
if (auth !== undefined) { // basic auth available
|
if (auth !== undefined) { // basic auth available
|
||||||
UserModel.find({name: auth.name, status: globals.status.new}).lean().exec( (err, data: any) => { // find user
|
UserModel.find({name: auth.name, status: globals.status.new}).lean().exec( (err, data: any) => { // find user
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (data.length === 1) { // one user found
|
if (data.length === 1) { // one user found
|
||||||
bcrypt.compare(auth.pass, data[0].pass, (err, res) => { // check password
|
bcrypt.compare(auth.pass, data[0].pass, (err, res) => { // check password
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (res === true) { // password correct
|
if (res === true) { // password correct
|
||||||
resolve({
|
resolve({
|
||||||
level: Object.entries(globals.levels).find(e => e[1] === data[0].level)[0],
|
level: Object.entries(globals.levels).find(e => e[1] === data[0].level)[0],
|
||||||
name: data[0].name,
|
name: data[0].name,
|
||||||
id: data[0]._id.toString(),
|
id: data[0]._id.toString(),
|
||||||
location: data[0].location,
|
location: data[0].location,
|
||||||
models: data[0].models
|
models: data[0].models
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function key (req, next): any { // checks API key and returns changed user object
|
function key (req, next): any { // checks API key and returns changed user object
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (req.query.key !== undefined) { // key available
|
if (req.query.key !== undefined) { // key available
|
||||||
UserModel.find({key: req.query.key, status: globals.status.new}).lean().exec( (err, data: any) => { // find user
|
UserModel.find({key: req.query.key, status: globals.status.new}).lean().exec( (err, data: any) => { // find user
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (data.length === 1) { // one user found
|
if (data.length === 1) { // one user found
|
||||||
resolve({
|
resolve({
|
||||||
level: Object.entries(globals.levels).find(e => e[1] === data[0].level)[0],
|
level: Object.entries(globals.levels).find(e => e[1] === data[0].level)[0],
|
||||||
name: data[0].name,
|
name: data[0].name,
|
||||||
id: data[0]._id.toString(),
|
id: data[0]._id.toString(),
|
||||||
location: data[0].location,
|
location: data[0].location,
|
||||||
models: data[0].models
|
models: data[0].models
|
||||||
});
|
});
|
||||||
if (!/^\/api/m.test(req.url)){
|
if (!/^\/api/m.test(req.url)){
|
||||||
delete req.query.key; // delete query parameter to avoid interference with later validation
|
delete req.query.key; // delete query parameter to avoid interference with later validation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -2,8 +2,8 @@ import {parseAsync} from 'json2csv';
|
|||||||
import flatten from './flatten';
|
import flatten from './flatten';
|
||||||
|
|
||||||
export default function csv(input: any[], f: (err, data) => void) { // parse JSON to CSV
|
export default function csv(input: any[], f: (err, data) => void) { // parse JSON to CSV
|
||||||
parseAsync(input.map(e => flatten(e)), {includeEmptyRows: true})
|
parseAsync(input.map(e => flatten(e)), {includeEmptyRows: true})
|
||||||
.then(csv => f(null, csv))
|
.then(csv => f(null, csv))
|
||||||
.catch(err => f(err, null));
|
.catch(err => f(err, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,42 +1,42 @@
|
|||||||
import globals from '../globals';
|
import globals from '../globals';
|
||||||
|
|
||||||
export default function flatten (data, keepArray = false) { // flatten object: {a: {b: true}} -> {a.b: true}
|
export default function flatten (data, keepArray = false) { // flatten object: {a: {b: true}} -> {a.b: true}
|
||||||
const result = {};
|
const result = {};
|
||||||
function recurse (cur, prop) {
|
function recurse (cur, prop) {
|
||||||
if (Object(cur) !== cur || Object.keys(cur).length === 0) { // simple value
|
if (Object(cur) !== cur || Object.keys(cur).length === 0) { // simple value
|
||||||
result[prop] = cur;
|
result[prop] = cur;
|
||||||
}
|
}
|
||||||
else if (prop === `${globals.spectrum.spectrum}.${globals.spectrum.dpt}`) { // convert spectrum for ML
|
else if (prop === `${globals.spectrum.spectrum}.${globals.spectrum.dpt}`) { // convert spectrum for ML
|
||||||
result[prop + '.labels'] = cur.map(e => parseFloat(e[0]));
|
result[prop + '.labels'] = cur.map(e => parseFloat(e[0]));
|
||||||
result[prop + '.values'] = cur.map(e => parseFloat(e[1]));
|
result[prop + '.values'] = cur.map(e => parseFloat(e[1]));
|
||||||
}
|
}
|
||||||
else if (Array.isArray(cur)) {
|
else if (Array.isArray(cur)) {
|
||||||
if (keepArray) {
|
if (keepArray) {
|
||||||
result[prop] = cur;
|
result[prop] = cur;
|
||||||
}
|
}
|
||||||
else { // array to string
|
else { // array to string
|
||||||
if (cur.length && (Object(cur[0]) !== cur || Object.keys(cur[0]).length === 0)) { // array of non-objects
|
if (cur.length && (Object(cur[0]) !== cur || Object.keys(cur[0]).length === 0)) { // array of non-objects
|
||||||
result[prop] = '[' + cur.join(', ') + ']';
|
result[prop] = '[' + cur.join(', ') + ']';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let l = 0;
|
let l = 0;
|
||||||
for(let i = 0, l = cur.length; i < l; i++)
|
for(let i = 0, l = cur.length; i < l; i++)
|
||||||
recurse(cur[i], prop + "[" + i + "]");
|
recurse(cur[i], prop + "[" + i + "]");
|
||||||
if (l == 0)
|
if (l == 0)
|
||||||
result[prop] = [];
|
result[prop] = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { // object
|
else { // object
|
||||||
let isEmpty = true;
|
let isEmpty = true;
|
||||||
for (let p in cur) {
|
for (let p in cur) {
|
||||||
isEmpty = false;
|
isEmpty = false;
|
||||||
recurse(cur[p], prop ? prop+"."+p : p);
|
recurse(cur[p], prop ? prop+"."+p : p);
|
||||||
}
|
}
|
||||||
if (isEmpty && prop)
|
if (isEmpty && prop)
|
||||||
result[prop] = {};
|
result[prop] = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recurse(data, '');
|
recurse(data, '');
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
@ -4,86 +4,86 @@ import axios from 'axios';
|
|||||||
|
|
||||||
export default class Mail{
|
export default class Mail{
|
||||||
|
|
||||||
static readonly address = 'definma@bosch-iot.com'; // email address
|
static readonly address = 'definma@bosch-iot.com'; // email address
|
||||||
static uri: string; // mail API URI
|
static uri: string; // mail API URI
|
||||||
static auth = {username: '', password: ''}; // mail API credentials
|
static auth = {username: '', password: ''}; // mail API credentials
|
||||||
static mailPass: string; // mail API generates password
|
static mailPass: string; // mail API generates password
|
||||||
|
|
||||||
static init() {
|
static init() {
|
||||||
if (process.env.NODE_ENV === 'production') { // only send mails in production
|
if (process.env.NODE_ENV === 'production') { // only send mails in production
|
||||||
this.mailPass = Array(64).fill(0).map(() => Math.floor(Math.random() * 10)).join('');
|
this.mailPass = Array(64).fill(0).map(() => Math.floor(Math.random() * 10)).join('');
|
||||||
this.uri = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.uri;
|
this.uri = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.uri;
|
||||||
this.auth.username = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.username;
|
this.auth.username = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.username;
|
||||||
this.auth.password = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.password;
|
this.auth.password = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.password;
|
||||||
axios({ // get registered mail addresses
|
axios({ // get registered mail addresses
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: this.uri + '/management/userDomainMapping',
|
url: this.uri + '/management/userDomainMapping',
|
||||||
auth: this.auth
|
auth: this.auth
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
if (res.data.addresses.indexOf(this.address) < 0) { // mail address not registered
|
if (res.data.addresses.indexOf(this.address) < 0) { // mail address not registered
|
||||||
if (res.data.addresses.length) { // delete wrong registered mail address
|
if (res.data.addresses.length) { // delete wrong registered mail address
|
||||||
await axios({
|
await axios({
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
url: this.uri + '/management/mailAddresses/' + res.data.addresses[0],
|
url: this.uri + '/management/mailAddresses/' + res.data.addresses[0],
|
||||||
auth: this.auth
|
auth: this.auth
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await axios({ // register right mail address
|
await axios({ // register right mail address
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: this.uri + '/management/mailAddresses/' + this.address,
|
url: this.uri + '/management/mailAddresses/' + this.address,
|
||||||
auth: this.auth
|
auth: this.auth
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
reject(e);
|
reject(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
return axios({ // set new mail password
|
return axios({ // set new mail password
|
||||||
method: 'put',
|
method: 'put',
|
||||||
url: this.uri + '/management/mailAddresses/' + this.address + '/password/' + this.mailPass,
|
url: this.uri + '/management/mailAddresses/' + this.address + '/password/' + this.mailPass,
|
||||||
auth: this.auth
|
auth: this.auth
|
||||||
});
|
});
|
||||||
}).then(() => { // init done successfully
|
}).then(() => { // init done successfully
|
||||||
console.info('Mail service established successfully');
|
console.info('Mail service established successfully');
|
||||||
}).catch(err => { // somewhere an error occurred
|
}).catch(err => { // somewhere an error occurred
|
||||||
console.error(`Mail init error: ${err.request.method} ${err.request.path}: ${err.response.status}`,
|
console.error(`Mail init error: ${err.request.method} ${err.request.path}: ${err.response.status}`,
|
||||||
err.response.data);
|
err.response.data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static send (mailAddress, subject, content, f: (x?) => void = () => {}) { // callback executed empty or with error
|
static send (mailAddress, subject, content, f: (x?) => void = () => {}) { // callback executed empty or with error
|
||||||
if (process.env.NODE_ENV === 'production') { // only send mails in production
|
if (process.env.NODE_ENV === 'production') { // only send mails in production
|
||||||
axios({
|
axios({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: this.uri + '/email',
|
url: this.uri + '/email',
|
||||||
auth: this.auth,
|
auth: this.auth,
|
||||||
data: {
|
data: {
|
||||||
recipients: [{to: mailAddress}],
|
recipients: [{to: mailAddress}],
|
||||||
subject: {content: subject},
|
subject: {content: subject},
|
||||||
body: {
|
body: {
|
||||||
content: content,
|
content: content,
|
||||||
contentType: "text/html"
|
contentType: "text/html"
|
||||||
},
|
},
|
||||||
from: {
|
from: {
|
||||||
eMail: this.address,
|
eMail: this.address,
|
||||||
password: this.mailPass
|
password: this.mailPass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
f();
|
f();
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
f(err);
|
f(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else { // dev dummy replacement
|
else { // dev dummy replacement
|
||||||
console.info('Sending mail to ' + mailAddress + ': -- ' + subject + ' -- ' + content);
|
console.info('Sending mail to ' + mailAddress + ': -- ' + subject + ' -- ' + content);
|
||||||
f();
|
f();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
const ChangelogSchema = new mongoose.Schema({
|
const ChangelogSchema = new mongoose.Schema({
|
||||||
action: String,
|
action: String,
|
||||||
collection_name: String,
|
collection_name: String,
|
||||||
conditions: mongoose.Schema.Types.Mixed,
|
conditions: mongoose.Schema.Types.Mixed,
|
||||||
data: Object,
|
data: Object,
|
||||||
user_id: mongoose.Schema.Types.ObjectId
|
user_id: mongoose.Schema.Types.ObjectId
|
||||||
}, {minimize: false, strict: false});
|
}, {minimize: false, strict: false});
|
||||||
|
|
||||||
export default mongoose.model<any, mongoose.Model<any, any>>('changelog', ChangelogSchema);
|
export default mongoose.model<any, mongoose.Model<any, any>>('changelog', ChangelogSchema);
|
@ -2,19 +2,19 @@ import mongoose from 'mongoose';
|
|||||||
import db from '../db';
|
import db from '../db';
|
||||||
|
|
||||||
const ConditionTemplateSchema = new mongoose.Schema({
|
const ConditionTemplateSchema = new mongoose.Schema({
|
||||||
first_id: mongoose.Schema.Types.ObjectId,
|
first_id: mongoose.Schema.Types.ObjectId,
|
||||||
name: String,
|
name: String,
|
||||||
version: Number,
|
version: Number,
|
||||||
parameters: [new mongoose.Schema({
|
parameters: [new mongoose.Schema({
|
||||||
name: String,
|
name: String,
|
||||||
range: mongoose.Schema.Types.Mixed
|
range: mongoose.Schema.Types.Mixed
|
||||||
} ,{ _id : false })]
|
} ,{ _id : false })]
|
||||||
}, {minimize: false}); // to allow empty objects
|
}, {minimize: false}); // to allow empty objects
|
||||||
|
|
||||||
// changelog query helper
|
// changelog query helper
|
||||||
ConditionTemplateSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
ConditionTemplateSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
||||||
db.log(req, this);
|
db.log(req, this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default mongoose.model<any, mongoose.Model<any, any>>('condition_template', ConditionTemplateSchema);
|
export default mongoose.model<any, mongoose.Model<any, any>>('condition_template', ConditionTemplateSchema);
|
@ -2,15 +2,15 @@ import db from '../db';
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
const HelpSchema = new mongoose.Schema({
|
const HelpSchema = new mongoose.Schema({
|
||||||
key: {type: String, index: {unique: true}},
|
key: {type: String, index: {unique: true}},
|
||||||
level: String,
|
level: String,
|
||||||
text: String
|
text: String
|
||||||
}, {minimize: false});
|
}, {minimize: false});
|
||||||
|
|
||||||
// changelog query helper
|
// changelog query helper
|
||||||
HelpSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
HelpSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
||||||
db.log(req, this);
|
db.log(req, this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default mongoose.model<any, mongoose.Model<any, any>>('help', HelpSchema);
|
export default mongoose.model<any, mongoose.Model<any, any>>('help', HelpSchema);
|
@ -4,18 +4,18 @@ import MaterialGroupsModel from '../models/material_groups';
|
|||||||
import db from '../db';
|
import db from '../db';
|
||||||
|
|
||||||
const MaterialSchema = new mongoose.Schema({
|
const MaterialSchema = new mongoose.Schema({
|
||||||
name: {type: String, index: {unique: true}},
|
name: {type: String, index: {unique: true}},
|
||||||
supplier_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialSupplierModel},
|
supplier_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialSupplierModel},
|
||||||
group_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialGroupsModel},
|
group_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialGroupsModel},
|
||||||
properties: mongoose.Schema.Types.Mixed,
|
properties: mongoose.Schema.Types.Mixed,
|
||||||
numbers: [String],
|
numbers: [String],
|
||||||
status: String
|
status: String
|
||||||
}, {minimize: false});
|
}, {minimize: false});
|
||||||
|
|
||||||
// changelog query helper
|
// changelog query helper
|
||||||
MaterialSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
MaterialSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
||||||
db.log(req, this);
|
db.log(req, this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
MaterialSchema.index({supplier_id: 1});
|
MaterialSchema.index({supplier_id: 1});
|
||||||
MaterialSchema.index({group_id: 1});
|
MaterialSchema.index({group_id: 1});
|
||||||
|
@ -2,13 +2,13 @@ import mongoose from 'mongoose';
|
|||||||
import db from '../db';
|
import db from '../db';
|
||||||
|
|
||||||
const MaterialGroupsSchema = new mongoose.Schema({
|
const MaterialGroupsSchema = new mongoose.Schema({
|
||||||
name: {type: String, index: {unique: true}}
|
name: {type: String, index: {unique: true}}
|
||||||
});
|
});
|
||||||
|
|
||||||
// changelog query helper
|
// changelog query helper
|
||||||
MaterialGroupsSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
MaterialGroupsSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
||||||
db.log(req, this);
|
db.log(req, this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default mongoose.model<any, mongoose.Model<any, any>>('material_groups', MaterialGroupsSchema);
|
export default mongoose.model<any, mongoose.Model<any, any>>('material_groups', MaterialGroupsSchema);
|
@ -2,13 +2,13 @@ import mongoose from 'mongoose';
|
|||||||
import db from '../db';
|
import db from '../db';
|
||||||
|
|
||||||
const MaterialSuppliersSchema = new mongoose.Schema({
|
const MaterialSuppliersSchema = new mongoose.Schema({
|
||||||
name: {type: String, index: {unique: true}}
|
name: {type: String, index: {unique: true}}
|
||||||
});
|
});
|
||||||
|
|
||||||
// changelog query helper
|
// changelog query helper
|
||||||
MaterialSuppliersSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
MaterialSuppliersSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
||||||
db.log(req, this);
|
db.log(req, this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default mongoose.model<any, mongoose.Model<any, any>>('material_suppliers', MaterialSuppliersSchema);
|
export default mongoose.model<any, mongoose.Model<any, any>>('material_suppliers', MaterialSuppliersSchema);
|
@ -2,19 +2,19 @@ import mongoose from 'mongoose';
|
|||||||
import db from '../db';
|
import db from '../db';
|
||||||
|
|
||||||
const MaterialTemplateSchema = new mongoose.Schema({
|
const MaterialTemplateSchema = new mongoose.Schema({
|
||||||
first_id: mongoose.Schema.Types.ObjectId,
|
first_id: mongoose.Schema.Types.ObjectId,
|
||||||
name: String,
|
name: String,
|
||||||
version: Number,
|
version: Number,
|
||||||
parameters: [new mongoose.Schema({
|
parameters: [new mongoose.Schema({
|
||||||
name: String,
|
name: String,
|
||||||
range: mongoose.Schema.Types.Mixed
|
range: mongoose.Schema.Types.Mixed
|
||||||
} ,{ _id : false })]
|
} ,{ _id : false })]
|
||||||
}, {minimize: false}); // to allow empty objects
|
}, {minimize: false}); // to allow empty objects
|
||||||
|
|
||||||
// changelog query helper
|
// changelog query helper
|
||||||
MaterialTemplateSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
MaterialTemplateSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
||||||
db.log(req, this);
|
db.log(req, this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default mongoose.model<any, mongoose.Model<any, any>>('material_template', MaterialTemplateSchema);
|
export default mongoose.model<any, mongoose.Model<any, any>>('material_template', MaterialTemplateSchema);
|
@ -6,16 +6,16 @@ import db from '../db';
|
|||||||
|
|
||||||
|
|
||||||
const MeasurementSchema = new mongoose.Schema({
|
const MeasurementSchema = new mongoose.Schema({
|
||||||
sample_id: {type: mongoose.Schema.Types.ObjectId, ref: SampleModel},
|
sample_id: {type: mongoose.Schema.Types.ObjectId, ref: SampleModel},
|
||||||
values: mongoose.Schema.Types.Mixed,
|
values: mongoose.Schema.Types.Mixed,
|
||||||
measurement_template: {type: mongoose.Schema.Types.ObjectId, ref: MeasurementTemplateModel},
|
measurement_template: {type: mongoose.Schema.Types.ObjectId, ref: MeasurementTemplateModel},
|
||||||
status: String
|
status: String
|
||||||
}, {minimize: false});
|
}, {minimize: false});
|
||||||
|
|
||||||
// changelog query helper
|
// changelog query helper
|
||||||
MeasurementSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
MeasurementSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
||||||
db.log(req, this);
|
db.log(req, this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
MeasurementSchema.index({sample_id: 1});
|
MeasurementSchema.index({sample_id: 1});
|
||||||
MeasurementSchema.index({measurement_template: 1});
|
MeasurementSchema.index({measurement_template: 1});
|
||||||
|
@ -2,19 +2,19 @@ import mongoose from 'mongoose';
|
|||||||
import db from '../db';
|
import db from '../db';
|
||||||
|
|
||||||
const MeasurementTemplateSchema = new mongoose.Schema({
|
const MeasurementTemplateSchema = new mongoose.Schema({
|
||||||
first_id: mongoose.Schema.Types.ObjectId,
|
first_id: mongoose.Schema.Types.ObjectId,
|
||||||
name: String,
|
name: String,
|
||||||
version: Number,
|
version: Number,
|
||||||
parameters: [new mongoose.Schema({
|
parameters: [new mongoose.Schema({
|
||||||
name: String,
|
name: String,
|
||||||
range: mongoose.Schema.Types.Mixed
|
range: mongoose.Schema.Types.Mixed
|
||||||
} ,{ _id : false })]
|
} ,{ _id : false })]
|
||||||
}, {minimize: false}); // to allow empty objects
|
}, {minimize: false}); // to allow empty objects
|
||||||
|
|
||||||
// changelog query helper
|
// changelog query helper
|
||||||
MeasurementTemplateSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
MeasurementTemplateSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
||||||
db.log(req, this);
|
db.log(req, this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default mongoose.model<any, mongoose.Model<any, any>>('measurement_template', MeasurementTemplateSchema);
|
export default mongoose.model<any, mongoose.Model<any, any>>('measurement_template', MeasurementTemplateSchema);
|
@ -2,18 +2,18 @@ import mongoose from 'mongoose';
|
|||||||
import db from '../db';
|
import db from '../db';
|
||||||
|
|
||||||
const ModelSchema = new mongoose.Schema({
|
const ModelSchema = new mongoose.Schema({
|
||||||
group: {type: String, index: {unique: true}},
|
group: {type: String, index: {unique: true}},
|
||||||
models: [new mongoose.Schema({
|
models: [new mongoose.Schema({
|
||||||
name: String,
|
name: String,
|
||||||
url: String,
|
url: String,
|
||||||
label: String
|
label: String
|
||||||
} ,{ _id : true })]
|
} ,{ _id : true })]
|
||||||
});
|
});
|
||||||
|
|
||||||
// changelog query helper
|
// changelog query helper
|
||||||
ModelSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
ModelSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
||||||
db.log(req, this);
|
db.log(req, this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
ModelSchema.index({group: 1});
|
ModelSchema.index({group: 1});
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
const ModelFileSchema = new mongoose.Schema({
|
const ModelFileSchema = new mongoose.Schema({
|
||||||
name: {type: String, index: {unique: true}},
|
name: {type: String, index: {unique: true}},
|
||||||
data: Buffer
|
data: Buffer
|
||||||
});
|
});
|
||||||
|
|
||||||
export default mongoose.model<any, mongoose.Model<any, any>>('model_file', ModelFileSchema);
|
export default mongoose.model<any, mongoose.Model<any, any>>('model_file', ModelFileSchema);
|
@ -2,18 +2,18 @@ import mongoose from 'mongoose';
|
|||||||
import db from '../db';
|
import db from '../db';
|
||||||
|
|
||||||
const NoteSchema = new mongoose.Schema({
|
const NoteSchema = new mongoose.Schema({
|
||||||
comment: String,
|
comment: String,
|
||||||
sample_references: [{
|
sample_references: [{
|
||||||
sample_id: mongoose.Schema.Types.ObjectId,
|
sample_id: mongoose.Schema.Types.ObjectId,
|
||||||
relation: String
|
relation: String
|
||||||
}],
|
}],
|
||||||
custom_fields: mongoose.Schema.Types.Mixed
|
custom_fields: mongoose.Schema.Types.Mixed
|
||||||
});
|
});
|
||||||
|
|
||||||
// changelog query helper
|
// changelog query helper
|
||||||
NoteSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
NoteSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
||||||
db.log(req, this);
|
db.log(req, this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default mongoose.model<any, mongoose.Model<any, any>>('note', NoteSchema);
|
export default mongoose.model<any, mongoose.Model<any, any>>('note', NoteSchema);
|
@ -2,14 +2,14 @@ import mongoose from 'mongoose';
|
|||||||
import db from '../db';
|
import db from '../db';
|
||||||
|
|
||||||
const NoteFieldSchema = new mongoose.Schema({
|
const NoteFieldSchema = new mongoose.Schema({
|
||||||
name: {type: String, index: {unique: true}},
|
name: {type: String, index: {unique: true}},
|
||||||
qty: Number
|
qty: Number
|
||||||
});
|
});
|
||||||
|
|
||||||
// changelog query helper
|
// changelog query helper
|
||||||
NoteFieldSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
NoteFieldSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
||||||
db.log(req, this);
|
db.log(req, this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default mongoose.model<any, mongoose.Model<any, any>>('note_field', NoteFieldSchema);
|
export default mongoose.model<any, mongoose.Model<any, any>>('note_field', NoteFieldSchema);
|
@ -6,21 +6,21 @@ import UserModel from './user';
|
|||||||
import db from '../db';
|
import db from '../db';
|
||||||
|
|
||||||
const SampleSchema = new mongoose.Schema({
|
const SampleSchema = new mongoose.Schema({
|
||||||
number: {type: String, index: {unique: true}},
|
number: {type: String, index: {unique: true}},
|
||||||
type: String,
|
type: String,
|
||||||
color: String,
|
color: String,
|
||||||
batch: String,
|
batch: String,
|
||||||
condition: mongoose.Schema.Types.Mixed,
|
condition: mongoose.Schema.Types.Mixed,
|
||||||
material_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialModel},
|
material_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialModel},
|
||||||
note_id: {type: mongoose.Schema.Types.ObjectId, ref: NoteModel},
|
note_id: {type: mongoose.Schema.Types.ObjectId, ref: NoteModel},
|
||||||
user_id: {type: mongoose.Schema.Types.ObjectId, ref: UserModel},
|
user_id: {type: mongoose.Schema.Types.ObjectId, ref: UserModel},
|
||||||
status: String
|
status: String
|
||||||
}, {minimize: false});
|
}, {minimize: false});
|
||||||
|
|
||||||
// changelog query helper
|
// changelog query helper
|
||||||
SampleSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
SampleSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
||||||
db.log(req, this);
|
db.log(req, this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
SampleSchema.index({material_id: 1});
|
SampleSchema.index({material_id: 1});
|
||||||
SampleSchema.index({note_id: 1});
|
SampleSchema.index({note_id: 1});
|
||||||
|
@ -3,21 +3,21 @@ import db from '../db';
|
|||||||
import ModelModel from './model';
|
import ModelModel from './model';
|
||||||
|
|
||||||
const UserSchema = new mongoose.Schema({
|
const UserSchema = new mongoose.Schema({
|
||||||
name: {type: String, index: {unique: true}},
|
name: {type: String, index: {unique: true}},
|
||||||
email: String,
|
email: String,
|
||||||
pass: String,
|
pass: String,
|
||||||
key: String,
|
key: String,
|
||||||
level: String,
|
level: String,
|
||||||
location: String,
|
location: String,
|
||||||
devices: [String],
|
devices: [String],
|
||||||
models: [{type: mongoose.Schema.Types.ObjectId, ref: ModelModel}],
|
models: [{type: mongoose.Schema.Types.ObjectId, ref: ModelModel}],
|
||||||
status: String
|
status: String
|
||||||
});
|
});
|
||||||
|
|
||||||
// changelog query helper
|
// changelog query helper
|
||||||
UserSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
UserSchema.query.log = function <Q extends mongoose.DocumentQuery<any, any>> (req) {
|
||||||
db.log(req, this);
|
db.log(req, this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default mongoose.model<any, mongoose.Model<any, any>>('user', UserSchema);
|
export default mongoose.model<any, mongoose.Model<any, any>>('user', UserSchema);
|
@ -4,181 +4,181 @@ import HelpModel from '../models/help';
|
|||||||
|
|
||||||
|
|
||||||
describe('/help', () => {
|
describe('/help', () => {
|
||||||
let server;
|
let server;
|
||||||
before(done => TestHelper.before(done));
|
before(done => TestHelper.before(done));
|
||||||
beforeEach(done => server = TestHelper.beforeEach(server, done));
|
beforeEach(done => server = TestHelper.beforeEach(server, done));
|
||||||
afterEach(done => TestHelper.afterEach(server, done));
|
afterEach(done => TestHelper.afterEach(server, done));
|
||||||
after(done => TestHelper.after(done));
|
after(done => TestHelper.after(done));
|
||||||
|
|
||||||
describe('GET /help/{key}', () => {
|
describe('GET /help/{key}', () => {
|
||||||
it('returns the required text', done => {
|
it('returns the required text', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/help/%2Fsamples',
|
url: '/help/%2Fsamples',
|
||||||
auth: {basic: 'janedoe'},
|
auth: {basic: 'janedoe'},
|
||||||
httpStatus: 200,
|
httpStatus: 200,
|
||||||
res: {text: 'Samples help', level: 'read'}
|
res: {text: 'Samples help', level: 'read'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('returns the required text without authorization if allowed', done => {
|
it('returns the required text without authorization if allowed', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/help/%2Fdocumentation',
|
url: '/help/%2Fdocumentation',
|
||||||
auth: {basic: 'janedoe'},
|
auth: {basic: 'janedoe'},
|
||||||
httpStatus: 200,
|
httpStatus: 200,
|
||||||
res: {text: 'Documentation help', level: 'none'}
|
res: {text: 'Documentation help', level: 'none'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('returns 404 for an invalid key', done => {
|
it('returns 404 for an invalid key', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/help/documentation/database',
|
url: '/help/documentation/database',
|
||||||
httpStatus: 404
|
httpStatus: 404
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('returns 404 for an unknown key', done => {
|
it('returns 404 for an unknown key', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/help/xxx',
|
url: '/help/xxx',
|
||||||
auth: {basic: 'janedoe'},
|
auth: {basic: 'janedoe'},
|
||||||
httpStatus: 404
|
httpStatus: 404
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('returns 403 for a text with a higher level than given', done => {
|
it('returns 403 for a text with a higher level than given', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/help/%2Fmodels',
|
url: '/help/%2Fmodels',
|
||||||
auth: {basic: 'janedoe'},
|
auth: {basic: 'janedoe'},
|
||||||
httpStatus: 403
|
httpStatus: 403
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects an API key', done => {
|
it('rejects an API key', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/help/%2Fsamples',
|
url: '/help/%2Fsamples',
|
||||||
auth: {api: 'janedoe'},
|
auth: {api: 'janedoe'},
|
||||||
httpStatus: 401,
|
httpStatus: 401,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects an unauthorized request if a level is given', done => {
|
it('rejects an unauthorized request if a level is given', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/help/%2Fsamples',
|
url: '/help/%2Fsamples',
|
||||||
httpStatus: 401
|
httpStatus: 401
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /help/{key}', () => {
|
describe('POST /help/{key}', () => {
|
||||||
it('changes the required text', done => {
|
it('changes the required text', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: '/help/%2Fsamples',
|
url: '/help/%2Fsamples',
|
||||||
auth: {basic: 'admin'},
|
auth: {basic: 'admin'},
|
||||||
httpStatus: 200,
|
httpStatus: 200,
|
||||||
req: {text: 'New samples help', level: 'write'}
|
req: {text: 'New samples help', level: 'write'}
|
||||||
}).end((err, res) => {
|
}).end((err, res) => {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
should(res.body).be.eql({status: 'OK'});
|
should(res.body).be.eql({status: 'OK'});
|
||||||
HelpModel.find({key: '/samples'}).lean().exec((err, data) => {
|
HelpModel.find({key: '/samples'}).lean().exec((err, data) => {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
should(data).have.lengthOf(1);
|
should(data).have.lengthOf(1);
|
||||||
should(data[0]).have.only.keys('_id', 'key', 'text', 'level');
|
should(data[0]).have.only.keys('_id', 'key', 'text', 'level');
|
||||||
should(data[0]).property('key', '/samples');
|
should(data[0]).property('key', '/samples');
|
||||||
should(data[0]).property('text', 'New samples help');
|
should(data[0]).property('text', 'New samples help');
|
||||||
should(data[0]).property('level', 'write');
|
should(data[0]).property('level', 'write');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('saves a new text', done => {
|
it('saves a new text', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: '/help/%2Fmaterials',
|
url: '/help/%2Fmaterials',
|
||||||
auth: {basic: 'admin'},
|
auth: {basic: 'admin'},
|
||||||
httpStatus: 200,
|
httpStatus: 200,
|
||||||
req: {text: 'Materials help', level: 'dev'}
|
req: {text: 'Materials help', level: 'dev'}
|
||||||
}).end((err, res) => {
|
}).end((err, res) => {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
should(res.body).be.eql({status: 'OK'});
|
should(res.body).be.eql({status: 'OK'});
|
||||||
HelpModel.find({key: '/materials'}).lean().exec((err, data) => {
|
HelpModel.find({key: '/materials'}).lean().exec((err, data) => {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
should(data).have.lengthOf(1);
|
should(data).have.lengthOf(1);
|
||||||
should(data[0]).have.only.keys('_id', 'key', 'text', 'level', '__v');
|
should(data[0]).have.only.keys('_id', 'key', 'text', 'level', '__v');
|
||||||
should(data[0]).property('key', '/materials');
|
should(data[0]).property('key', '/materials');
|
||||||
should(data[0]).property('text', 'Materials help');
|
should(data[0]).property('text', 'Materials help');
|
||||||
should(data[0]).property('level', 'dev');
|
should(data[0]).property('level', 'dev');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects an API key', done => {
|
it('rejects an API key', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: '/help/%2Fsamples',
|
url: '/help/%2Fsamples',
|
||||||
auth: {key: 'admin'},
|
auth: {key: 'admin'},
|
||||||
httpStatus: 401,
|
httpStatus: 401,
|
||||||
req: {text: 'New samples help', level: 'write'}
|
req: {text: 'New samples help', level: 'write'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects a write user', done => {
|
it('rejects a write user', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: '/help/%2Fsamples',
|
url: '/help/%2Fsamples',
|
||||||
auth: {basic: 'janedoe'},
|
auth: {basic: 'janedoe'},
|
||||||
httpStatus: 403,
|
httpStatus: 403,
|
||||||
req: {text: 'New samples help', level: 'write'}
|
req: {text: 'New samples help', level: 'write'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects an unauthorized request', done => {
|
it('rejects an unauthorized request', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: '/help/%2Fsamples',
|
url: '/help/%2Fsamples',
|
||||||
httpStatus: 401,
|
httpStatus: 401,
|
||||||
req: {text: 'New samples help', level: 'write'}
|
req: {text: 'New samples help', level: 'write'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /help/{key}', () => {
|
describe('DELETE /help/{key}', () => {
|
||||||
it('deletes the required entry', done => {
|
it('deletes the required entry', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
url: '/help/%2Fsamples',
|
url: '/help/%2Fsamples',
|
||||||
auth: {basic: 'admin'},
|
auth: {basic: 'admin'},
|
||||||
httpStatus: 200
|
httpStatus: 200
|
||||||
}).end((err, res) => {
|
}).end((err, res) => {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
should(res.body).be.eql({status: 'OK'});
|
should(res.body).be.eql({status: 'OK'});
|
||||||
HelpModel.find({key: '/materials'}).lean().exec((err, data) => {
|
HelpModel.find({key: '/materials'}).lean().exec((err, data) => {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
should(data).have.lengthOf(0);
|
should(data).have.lengthOf(0);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects an API key', done => {
|
it('rejects an API key', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
url: '/help/%2Fsamples',
|
url: '/help/%2Fsamples',
|
||||||
auth: {key: 'admin'},
|
auth: {key: 'admin'},
|
||||||
httpStatus: 401
|
httpStatus: 401
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects a write user', done => {
|
it('rejects a write user', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
url: '/help/%2Fsamples',
|
url: '/help/%2Fsamples',
|
||||||
auth: {basic: 'janedoe'},
|
auth: {basic: 'janedoe'},
|
||||||
httpStatus: 403
|
httpStatus: 403
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects an unauthorized request', done => {
|
it('rejects an unauthorized request', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
url: '/help/%2Fsamples',
|
url: '/help/%2Fsamples',
|
||||||
httpStatus: 401
|
httpStatus: 401
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -7,49 +7,49 @@ import globals from '../globals';
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.get('/help/:key', (req, res, next) => {
|
router.get('/help/:key', (req, res, next) => {
|
||||||
const {error: paramError, value: key} = HelpValidate.params(req.params);
|
const {error: paramError, value: key} = HelpValidate.params(req.params);
|
||||||
if (paramError) return res400(paramError, res);
|
if (paramError) return res400(paramError, res);
|
||||||
|
|
||||||
HelpModel.findOne(key).lean().exec((err, data) => {
|
HelpModel.findOne(key).lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
if (data.level !== 'none') { // check level
|
if (data.level !== 'none') { // check level
|
||||||
if (!req.auth(res,
|
if (!req.auth(res,
|
||||||
Object.values(globals.levels).slice(Object.values(globals.levels).findIndex(e => e === data.level))
|
Object.values(globals.levels).slice(Object.values(globals.levels).findIndex(e => e === data.level))
|
||||||
, 'basic')) return;
|
, 'basic')) return;
|
||||||
}
|
}
|
||||||
res.json(HelpValidate.output(data));
|
res.json(HelpValidate.output(data));
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/help/:key', (req, res, next) => {
|
router.post('/help/:key', (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||||
const {error: paramError, value: key} = HelpValidate.params(req.params);
|
const {error: paramError, value: key} = HelpValidate.params(req.params);
|
||||||
if (paramError) return res400(paramError, res);
|
if (paramError) return res400(paramError, res);
|
||||||
const {error, value: help} = HelpValidate.input(req.body);
|
const {error, value: help} = HelpValidate.input(req.body);
|
||||||
if (error) return res400(error, res);
|
if (error) return res400(error, res);
|
||||||
|
|
||||||
HelpModel.findOneAndUpdate(key, help, {upsert: true}).log(req).lean().exec(err => {
|
HelpModel.findOneAndUpdate(key, help, {upsert: true}).log(req).lean().exec(err => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
res.json({status: 'OK'});
|
res.json({status: 'OK'});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/help/:key', (req, res, next) => {
|
router.delete('/help/:key', (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||||
const {error: paramError, value: key} = HelpValidate.params(req.params);
|
const {error: paramError, value: key} = HelpValidate.params(req.params);
|
||||||
if (paramError) return res400(paramError, res);
|
if (paramError) return res400(paramError, res);
|
||||||
|
|
||||||
HelpModel.findOneAndDelete(key).log(req).lean().exec((err, data) => {
|
HelpModel.findOneAndDelete(key).log(req).lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
res.json({status: 'OK'});
|
res.json({status: 'OK'});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
File diff suppressed because it is too large
Load Diff
@ -19,169 +19,169 @@ import globals from '../globals';
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.get('/materials', (req, res, next) => {
|
router.get('/materials', (req, res, next) => {
|
||||||
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
|
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
|
||||||
|
|
||||||
const {error, value: filters} =
|
const {error, value: filters} =
|
||||||
MaterialValidate.query(req.query, ['dev', 'admin'].indexOf(req.authDetails.level) >= 0);
|
MaterialValidate.query(req.query, ['dev', 'admin'].indexOf(req.authDetails.level) >= 0);
|
||||||
if (error) return res400(error, res);
|
if (error) return res400(error, res);
|
||||||
|
|
||||||
MaterialModel.find(filters).sort({name: 1}).populate('group_id').populate('supplier_id')
|
MaterialModel.find(filters).sort({name: 1}).populate('group_id').populate('supplier_id')
|
||||||
.lean().exec((err, data) => {
|
.lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
// validate all and filter null values from validation errors
|
// validate all and filter null values from validation errors
|
||||||
res.json(_.compact(data.map(e => MaterialValidate.output(e, true))));
|
res.json(_.compact(data.map(e => MaterialValidate.output(e, true))));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get(`/materials/:state(${globals.status.new}|${globals.status.del})`, (req, res, next) => {
|
router.get(`/materials/:state(${globals.status.new}|${globals.status.del})`, (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
MaterialModel.find({status: req.params.state}).populate('group_id').populate('supplier_id')
|
MaterialModel.find({status: req.params.state}).populate('group_id').populate('supplier_id')
|
||||||
.lean().exec((err, data) => {
|
.lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
// validate all and filter null values from validation errors
|
// validate all and filter null values from validation errors
|
||||||
res.json(_.compact(data.map(e => MaterialValidate.output(e))));
|
res.json(_.compact(data.map(e => MaterialValidate.output(e))));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/material/' + IdValidate.parameter(), (req, res, next) => {
|
router.get('/material/' + IdValidate.parameter(), (req, res, next) => {
|
||||||
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
|
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
|
||||||
|
|
||||||
MaterialModel.findById(req.params.id).populate('group_id').populate('supplier_id').lean().exec((err, data: any) => {
|
MaterialModel.findById(req.params.id).populate('group_id').populate('supplier_id').lean().exec((err, data: any) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleted materials only available for dev/admin
|
// deleted materials only available for dev/admin
|
||||||
if (data.status === globals.status.del && !req.auth(res, ['dev', 'admin'], 'all')) return;
|
if (data.status === globals.status.del && !req.auth(res, ['dev', 'admin'], 'all')) return;
|
||||||
res.json(MaterialValidate.output(data));
|
res.json(MaterialValidate.output(data));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/material/' + IdValidate.parameter(), (req, res, next) => {
|
router.put('/material/' + IdValidate.parameter(), (req, res, next) => {
|
||||||
if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
let {error, value: material} = MaterialValidate.input(req.body, 'change');
|
let {error, value: material} = MaterialValidate.input(req.body, 'change');
|
||||||
if (error) return res400(error, res);
|
if (error) return res400(error, res);
|
||||||
|
|
||||||
MaterialModel.findById(req.params.id).lean().exec(async (err, materialData: any) => {
|
MaterialModel.findById(req.params.id).lean().exec(async (err, materialData: any) => {
|
||||||
if (!materialData) {
|
if (!materialData) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
if (materialData.status === 'deleted') {
|
if (materialData.status === 'deleted') {
|
||||||
return res.status(403).json({status: 'Forbidden'});
|
return res.status(403).json({status: 'Forbidden'});
|
||||||
}
|
}
|
||||||
if (material.hasOwnProperty('name') && material.name !== materialData.name) {
|
if (material.hasOwnProperty('name') && material.name !== materialData.name) {
|
||||||
if (!await nameCheck(material, res, next)) return;
|
if (!await nameCheck(material, res, next)) return;
|
||||||
}
|
}
|
||||||
if (material.hasOwnProperty('group')) {
|
if (material.hasOwnProperty('group')) {
|
||||||
material = await groupResolve(material, req, next);
|
material = await groupResolve(material, req, next);
|
||||||
if (!material) return;
|
if (!material) return;
|
||||||
}
|
}
|
||||||
if (material.hasOwnProperty('supplier')) {
|
if (material.hasOwnProperty('supplier')) {
|
||||||
material = await supplierResolve(material, req, next);
|
material = await supplierResolve(material, req, next);
|
||||||
if (!material) return;
|
if (!material) return;
|
||||||
}
|
}
|
||||||
if (material.hasOwnProperty('properties')) {
|
if (material.hasOwnProperty('properties')) {
|
||||||
if (!await propertiesCheck(material.properties, 'change', res, next,
|
if (!await propertiesCheck(material.properties, 'change', res, next,
|
||||||
materialData.properties.material_template.toString() !== material.properties.material_template)) return;
|
materialData.properties.material_template.toString() !== material.properties.material_template)) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for changes
|
// check for changes
|
||||||
if (!_.isEqual(_.pick(IdValidate.stringify(materialData), _.keys(material)), IdValidate.stringify(material))) {
|
if (!_.isEqual(_.pick(IdValidate.stringify(materialData), _.keys(material)), IdValidate.stringify(material))) {
|
||||||
material.status = globals.status.new; // set status to new
|
material.status = globals.status.new; // set status to new
|
||||||
}
|
}
|
||||||
|
|
||||||
await MaterialModel.findByIdAndUpdate(req.params.id, material, {new: true})
|
await MaterialModel.findByIdAndUpdate(req.params.id, material, {new: true})
|
||||||
.log(req).populate('group_id').populate('supplier_id').lean().exec((err, data) => {
|
.log(req).populate('group_id').populate('supplier_id').lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
res.json(MaterialValidate.output(data));
|
res.json(MaterialValidate.output(data));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/material/' + IdValidate.parameter(), (req, res, next) => {
|
router.delete('/material/' + IdValidate.parameter(), (req, res, next) => {
|
||||||
if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
// check if there are still samples referencing this material
|
// check if there are still samples referencing this material
|
||||||
SampleModel.find({'material_id': new mongoose.Types.ObjectId(req.params.id), status: {$ne: globals.status.del}})
|
SampleModel.find({'material_id': new mongoose.Types.ObjectId(req.params.id), status: {$ne: globals.status.del}})
|
||||||
.lean().exec((err, data) => {
|
.lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (data.length) {
|
if (data.length) {
|
||||||
return res.status(400).json({status: 'Material still in use'});
|
return res.status(400).json({status: 'Material still in use'});
|
||||||
}
|
}
|
||||||
MaterialModel.findByIdAndUpdate(req.params.id, {status: globals.status.del})
|
MaterialModel.findByIdAndUpdate(req.params.id, {status: globals.status.del})
|
||||||
.log(req).populate('group_id').populate('supplier_id').lean().exec((err, data) => {
|
.log(req).populate('group_id').populate('supplier_id').lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (data) {
|
if (data) {
|
||||||
res.json({status: 'OK'});
|
res.json({status: 'OK'});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.status(404).json({status: 'Not found'});
|
res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/material/restore/' + IdValidate.parameter(), (req, res, next) => {
|
router.put('/material/restore/' + IdValidate.parameter(), (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
setStatus(globals.status.new, req, res, next);
|
setStatus(globals.status.new, req, res, next);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/material/validate/' + IdValidate.parameter(), (req, res, next) => {
|
router.put('/material/validate/' + IdValidate.parameter(), (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
setStatus(globals.status.val, req, res, next);
|
setStatus(globals.status.val, req, res, next);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/material/new', async (req, res, next) => {
|
router.post('/material/new', async (req, res, next) => {
|
||||||
if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
let {error, value: material} = MaterialValidate.input(req.body, 'new');
|
let {error, value: material} = MaterialValidate.input(req.body, 'new');
|
||||||
if (error) return res400(error, res);
|
if (error) return res400(error, res);
|
||||||
|
|
||||||
if (!await nameCheck(material, res, next)) return;
|
if (!await nameCheck(material, res, next)) return;
|
||||||
material = await groupResolve(material, req, next);
|
material = await groupResolve(material, req, next);
|
||||||
if (!material) return;
|
if (!material) return;
|
||||||
material = await supplierResolve(material, req, next);
|
material = await supplierResolve(material, req, next);
|
||||||
if (!material) return;
|
if (!material) return;
|
||||||
if (!await propertiesCheck(material.properties, 'new', res, next)) return;
|
if (!await propertiesCheck(material.properties, 'new', res, next)) return;
|
||||||
|
|
||||||
material.status = globals.status.new; // set status to new
|
material.status = globals.status.new; // set status to new
|
||||||
await new MaterialModel(material).save(async (err, data) => {
|
await new MaterialModel(material).save(async (err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
db.log(req, 'materials', {_id: data._id}, data.toObject());
|
db.log(req, 'materials', {_id: data._id}, data.toObject());
|
||||||
await data.populate('group_id').populate('supplier_id').execPopulate().catch(err => next(err));
|
await data.populate('group_id').populate('supplier_id').execPopulate().catch(err => next(err));
|
||||||
if (data instanceof Error) return;
|
if (data instanceof Error) return;
|
||||||
res.json(MaterialValidate.output(data.toObject()));
|
res.json(MaterialValidate.output(data.toObject()));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/material/groups', (req, res, next) => {
|
router.get('/material/groups', (req, res, next) => {
|
||||||
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
|
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
|
||||||
|
|
||||||
MaterialGroupModel.find().lean().exec((err, data: any) => {
|
MaterialGroupModel.find().lean().exec((err, data: any) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
// validate all and filter null values from validation errors
|
// validate all and filter null values from validation errors
|
||||||
res.json(_.compact(data.map(e => MaterialValidate.outputGroups(e.name))));
|
res.json(_.compact(data.map(e => MaterialValidate.outputGroups(e.name))));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/material/suppliers', (req, res, next) => {
|
router.get('/material/suppliers', (req, res, next) => {
|
||||||
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
|
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
|
||||||
|
|
||||||
MaterialSupplierModel.find().lean().exec((err, data: any) => {
|
MaterialSupplierModel.find().lean().exec((err, data: any) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
// validate all and filter null values from validation errors
|
// validate all and filter null values from validation errors
|
||||||
res.json(_.compact(data.map(e => MaterialValidate.outputSuppliers(e.name))));
|
res.json(_.compact(data.map(e => MaterialValidate.outputSuppliers(e.name))));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -189,81 +189,81 @@ module.exports = router;
|
|||||||
|
|
||||||
|
|
||||||
async function nameCheck (material, res, next) { // check if name was already taken
|
async function nameCheck (material, res, next) { // check if name was already taken
|
||||||
const materialData = await MaterialModel.findOne({name: material.name}).lean().exec().catch(err => next(err)) as any;
|
const materialData = await MaterialModel.findOne({name: material.name}).lean().exec().catch(err => next(err)) as any;
|
||||||
if (materialData instanceof Error) return false;
|
if (materialData instanceof Error) return false;
|
||||||
if (materialData) { // could not find material_id
|
if (materialData) { // could not find material_id
|
||||||
res.status(400).json({status: 'Material name already taken'});
|
res.status(400).json({status: 'Material name already taken'});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function groupResolve (material, req, next) {
|
async function groupResolve (material, req, next) {
|
||||||
const groupData = await MaterialGroupModel.findOneAndUpdate(
|
const groupData = await MaterialGroupModel.findOneAndUpdate(
|
||||||
{name: material.group},
|
{name: material.group},
|
||||||
{name: material.group},
|
{name: material.group},
|
||||||
{upsert: true, new: true}
|
{upsert: true, new: true}
|
||||||
).log(req).lean().exec().catch(err => next(err)) as any;
|
).log(req).lean().exec().catch(err => next(err)) as any;
|
||||||
if (groupData instanceof Error) return false;
|
if (groupData instanceof Error) return false;
|
||||||
material.group_id = groupData._id;
|
material.group_id = groupData._id;
|
||||||
delete material.group;
|
delete material.group;
|
||||||
return material;
|
return material;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function supplierResolve (material, req, next) {
|
async function supplierResolve (material, req, next) {
|
||||||
const supplierData = await MaterialSupplierModel.findOneAndUpdate(
|
const supplierData = await MaterialSupplierModel.findOneAndUpdate(
|
||||||
{name: material.supplier},
|
{name: material.supplier},
|
||||||
{name: material.supplier},
|
{name: material.supplier},
|
||||||
{upsert: true, new: true}
|
{upsert: true, new: true}
|
||||||
).log(req).lean().exec().catch(err => next(err)) as any;
|
).log(req).lean().exec().catch(err => next(err)) as any;
|
||||||
if (supplierData instanceof Error) return false;
|
if (supplierData instanceof Error) return false;
|
||||||
material.supplier_id = supplierData._id;
|
material.supplier_id = supplierData._id;
|
||||||
delete material.supplier;
|
delete material.supplier;
|
||||||
return material;
|
return material;
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate material properties, returns false if invalid, otherwise template data
|
// validate material properties, returns false if invalid, otherwise template data
|
||||||
async function propertiesCheck (properties, param, res, next, checkVersion = true) {
|
async function propertiesCheck (properties, param, res, next, checkVersion = true) {
|
||||||
if (!properties.material_template || !IdValidate.valid(properties.material_template)) { // template id not found
|
if (!properties.material_template || !IdValidate.valid(properties.material_template)) { // template id not found
|
||||||
res.status(400).json({status: 'Material template not available'});
|
res.status(400).json({status: 'Material template not available'});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const materialData = await MaterialTemplateModel.findById(properties.material_template)
|
const materialData = await MaterialTemplateModel.findById(properties.material_template)
|
||||||
.lean().exec().catch(err => next(err)) as any;
|
.lean().exec().catch(err => next(err)) as any;
|
||||||
if (materialData instanceof Error) return false;
|
if (materialData instanceof Error) return false;
|
||||||
if (!materialData) { // template not found
|
if (!materialData) { // template not found
|
||||||
res.status(400).json({status: 'Material template not available'});
|
res.status(400).json({status: 'Material template not available'});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checkVersion) {
|
if (checkVersion) {
|
||||||
// get all template versions and check if given is latest
|
// get all template versions and check if given is latest
|
||||||
const materialVersions = await MaterialTemplateModel.find({first_id: materialData.first_id}).sort({version: -1})
|
const materialVersions = await MaterialTemplateModel.find({first_id: materialData.first_id}).sort({version: -1})
|
||||||
.lean().exec().catch(err => next(err)) as any;
|
.lean().exec().catch(err => next(err)) as any;
|
||||||
if (materialVersions instanceof Error) return false;
|
if (materialVersions instanceof Error) return false;
|
||||||
if (properties.material_template !== materialVersions[0]._id.toString()) { // template not latest
|
if (properties.material_template !== materialVersions[0]._id.toString()) { // template not latest
|
||||||
res.status(400).json({status: 'Old template version not allowed'});
|
res.status(400).json({status: 'Old template version not allowed'});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate parameters
|
// validate parameters
|
||||||
const {error, value} = ParametersValidate
|
const {error, value} = ParametersValidate
|
||||||
.input(_.omit(properties, 'material_template'), materialData.parameters, param);
|
.input(_.omit(properties, 'material_template'), materialData.parameters, param);
|
||||||
if (error) {res400(error, res); return false;}
|
if (error) {res400(error, res); return false;}
|
||||||
Object.keys(value).forEach(key => {
|
Object.keys(value).forEach(key => {
|
||||||
properties[key] = value[key];
|
properties[key] = value[key];
|
||||||
});
|
});
|
||||||
return materialData;
|
return materialData;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setStatus (status, req, res, next) { // set measurement status
|
function setStatus (status, req, res, next) { // set measurement status
|
||||||
MaterialModel.findByIdAndUpdate(req.params.id, {status: status}).log(req).lean().exec((err, data) => {
|
MaterialModel.findByIdAndUpdate(req.params.id, {status: status}).log(req).lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
res.json({status: 'OK'});
|
res.json({status: 'OK'});
|
||||||
});
|
});
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
@ -16,114 +16,114 @@ import mongoose from "mongoose";
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.get('/measurement/' + IdValidate.parameter(), (req, res, next) => {
|
router.get('/measurement/' + IdValidate.parameter(), (req, res, next) => {
|
||||||
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
|
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
|
||||||
|
|
||||||
MeasurementModel.findById(req.params.id).lean().exec((err, data: any) => {
|
MeasurementModel.findById(req.params.id).lean().exec((err, data: any) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
// deleted measurements only available for dev/admin
|
// deleted measurements only available for dev/admin
|
||||||
if (data.status === globals.status.del && !req.auth(res, ['dev', 'admin'], 'all')) return;
|
if (data.status === globals.status.del && !req.auth(res, ['dev', 'admin'], 'all')) return;
|
||||||
|
|
||||||
res.json(MeasurementValidate.output(data, req));
|
res.json(MeasurementValidate.output(data, req));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/measurement/' + IdValidate.parameter(), async (req, res, next) => {
|
router.put('/measurement/' + IdValidate.parameter(), async (req, res, next) => {
|
||||||
if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
const {error, value: measurement} = MeasurementValidate.input(req.body, 'change');
|
const {error, value: measurement} = MeasurementValidate.input(req.body, 'change');
|
||||||
if (error) return res400(error, res);
|
if (error) return res400(error, res);
|
||||||
|
|
||||||
const data = await MeasurementModel.findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
|
const data = await MeasurementModel.findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
|
||||||
if (data instanceof Error) return;
|
if (data instanceof Error) return;
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
if (data.status === 'deleted') {
|
if (data.status === 'deleted') {
|
||||||
return res.status(403).json({status: 'Forbidden'});
|
return res.status(403).json({status: 'Forbidden'});
|
||||||
}
|
}
|
||||||
|
|
||||||
// add properties needed for sampleIdCheck
|
// add properties needed for sampleIdCheck
|
||||||
measurement.measurement_template = data.measurement_template;
|
measurement.measurement_template = data.measurement_template;
|
||||||
measurement.sample_id = data.sample_id;
|
measurement.sample_id = data.sample_id;
|
||||||
if (!await sampleIdCheck(measurement, req, res, next)) return;
|
if (!await sampleIdCheck(measurement, req, res, next)) return;
|
||||||
|
|
||||||
// check for changes
|
// check for changes
|
||||||
if (measurement.values) { // fill not changed values from database
|
if (measurement.values) { // fill not changed values from database
|
||||||
measurement.values = _.assign({}, data.values, measurement.values);
|
measurement.values = _.assign({}, data.values, measurement.values);
|
||||||
if (!_.isEqual(measurement.values, data.values)) {
|
if (!_.isEqual(measurement.values, data.values)) {
|
||||||
measurement.status = globals.status.new; // set status to new
|
measurement.status = globals.status.new; // set status to new
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await templateCheck(measurement, 'change', res, next)) return;
|
if (!await templateCheck(measurement, 'change', res, next)) return;
|
||||||
await MeasurementModel.findByIdAndUpdate(req.params.id, measurement, {new: true})
|
await MeasurementModel.findByIdAndUpdate(req.params.id, measurement, {new: true})
|
||||||
.log(req).lean().exec((err, data) => {
|
.log(req).lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
res.json(MeasurementValidate.output(data, req));
|
res.json(MeasurementValidate.output(data, req));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/measurement/' + IdValidate.parameter(), (req, res, next) => {
|
router.delete('/measurement/' + IdValidate.parameter(), (req, res, next) => {
|
||||||
if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
MeasurementModel.findById(req.params.id).lean().exec(async (err, data) => {
|
MeasurementModel.findById(req.params.id).lean().exec(async (err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
if (!await sampleIdCheck(data, req, res, next)) return;
|
if (!await sampleIdCheck(data, req, res, next)) return;
|
||||||
await MeasurementModel.findByIdAndUpdate(req.params.id, {status: globals.status.del})
|
await MeasurementModel.findByIdAndUpdate(req.params.id, {status: globals.status.del})
|
||||||
.log(req).lean().exec(err => {
|
.log(req).lean().exec(err => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
return res.json({status: 'OK'});
|
return res.json({status: 'OK'});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/measurement/sample/' + IdValidate.parameter(), (req, res, next) => {
|
router.get('/measurement/sample/' + IdValidate.parameter(), (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
MeasurementModel.find({sample_id: mongoose.Types.ObjectId(req.params.id)}).lean().exec((err, data: any) => {
|
MeasurementModel.find({sample_id: mongoose.Types.ObjectId(req.params.id)}).lean().exec((err, data: any) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (!data.length) {
|
if (!data.length) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json(_.compact(data.map(e => MeasurementValidate.output(e, req, true))));
|
res.json(_.compact(data.map(e => MeasurementValidate.output(e, req, true))));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/measurement/restore/' + IdValidate.parameter(), (req, res, next) => {
|
router.put('/measurement/restore/' + IdValidate.parameter(), (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
setStatus(globals.status.new, req, res, next);
|
setStatus(globals.status.new, req, res, next);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/measurement/validate/' + IdValidate.parameter(), (req, res, next) => {
|
router.put('/measurement/validate/' + IdValidate.parameter(), (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
setStatus(globals.status.val, req, res, next);
|
setStatus(globals.status.val, req, res, next);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/measurement/new', async (req, res, next) => {
|
router.post('/measurement/new', async (req, res, next) => {
|
||||||
if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
const {error, value: measurement} = MeasurementValidate.input(req.body, 'new');
|
const {error, value: measurement} = MeasurementValidate.input(req.body, 'new');
|
||||||
if (error) return res400(error, res);
|
if (error) return res400(error, res);
|
||||||
|
|
||||||
if (!await sampleIdCheck(measurement, req, res, next)) return;
|
if (!await sampleIdCheck(measurement, req, res, next)) return;
|
||||||
measurement.values = await templateCheck(measurement, 'new', res, next);
|
measurement.values = await templateCheck(measurement, 'new', res, next);
|
||||||
if (!measurement.values) return;
|
if (!measurement.values) return;
|
||||||
|
|
||||||
measurement.status = globals.status.new;
|
measurement.status = globals.status.new;
|
||||||
await new MeasurementModel(measurement).save((err, data) => {
|
await new MeasurementModel(measurement).save((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
db.log(req, 'measurements', {_id: data._id}, data.toObject());
|
db.log(req, 'measurements', {_id: data._id}, data.toObject());
|
||||||
res.json(MeasurementValidate.output(data.toObject(), req));
|
res.json(MeasurementValidate.output(data.toObject(), req));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -132,61 +132,61 @@ module.exports = router;
|
|||||||
|
|
||||||
// validate sample_id, returns false if invalid or user has no access for this sample
|
// validate sample_id, returns false if invalid or user has no access for this sample
|
||||||
async function sampleIdCheck (measurement, req, res, next) {
|
async function sampleIdCheck (measurement, req, res, next) {
|
||||||
const sampleData = await SampleModel.findById(measurement.sample_id)
|
const sampleData = await SampleModel.findById(measurement.sample_id)
|
||||||
.lean().exec().catch(err => {next(err); return false;}) as any;
|
.lean().exec().catch(err => {next(err); return false;}) as any;
|
||||||
if (!sampleData) { // sample_id not found
|
if (!sampleData) { // sample_id not found
|
||||||
res.status(400).json({status: 'Sample id not available'});
|
res.status(400).json({status: 'Sample id not available'});
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// sample does not belong to user
|
// sample does not belong to user
|
||||||
return !(sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['dev', 'admin'], 'basic'));
|
return !(sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['dev', 'admin'], 'basic'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate measurement_template and values, returns values, true if values are {} or false if invalid,
|
// validate measurement_template and values, returns values, true if values are {} or false if invalid,
|
||||||
// param for 'new'/'change'
|
// param for 'new'/'change'
|
||||||
async function templateCheck (measurement, param, res, next) {
|
async function templateCheck (measurement, param, res, next) {
|
||||||
const templateData = await MeasurementTemplateModel.findById(measurement.measurement_template)
|
const templateData = await MeasurementTemplateModel.findById(measurement.measurement_template)
|
||||||
.lean().exec().catch(err => {next(err); return false;}) as any;
|
.lean().exec().catch(err => {next(err); return false;}) as any;
|
||||||
if (!templateData) { // template not found
|
if (!templateData) { // template not found
|
||||||
res.status(400).json({status: 'Measurement template not available'});
|
res.status(400).json({status: 'Measurement template not available'});
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill not given values for new measurements
|
// fill not given values for new measurements
|
||||||
if (param === 'new') {
|
if (param === 'new') {
|
||||||
// get all template versions and check if given is latest
|
// get all template versions and check if given is latest
|
||||||
const templateVersions = await MeasurementTemplateModel.find({first_id: templateData.first_id}).sort({version: -1})
|
const templateVersions = await MeasurementTemplateModel.find({first_id: templateData.first_id}).sort({version: -1})
|
||||||
.lean().exec().catch(err => next(err)) as any;
|
.lean().exec().catch(err => next(err)) as any;
|
||||||
if (templateVersions instanceof Error) return false;
|
if (templateVersions instanceof Error) return false;
|
||||||
if (measurement.measurement_template !== templateVersions[0]._id.toString()) { // template not latest
|
if (measurement.measurement_template !== templateVersions[0]._id.toString()) { // template not latest
|
||||||
res.status(400).json({status: 'Old template version not allowed'});
|
res.status(400).json({status: 'Old template version not allowed'});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Object.keys(measurement.values).length === 0) {
|
if (Object.keys(measurement.values).length === 0) {
|
||||||
res.status(400).json({status: 'At least one value is required'});
|
res.status(400).json({status: 'At least one value is required'});
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const fillValues = {}; // initialize not given values with null
|
const fillValues = {}; // initialize not given values with null
|
||||||
templateData.parameters.forEach(parameter => {
|
templateData.parameters.forEach(parameter => {
|
||||||
fillValues[parameter.name] = null;
|
fillValues[parameter.name] = null;
|
||||||
});
|
});
|
||||||
measurement.values = _.assign({}, fillValues, measurement.values);
|
measurement.values = _.assign({}, fillValues, measurement.values);
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate values
|
// validate values
|
||||||
const {error, value} = ParametersValidate.input(measurement.values, templateData.parameters, 'null');
|
const {error, value} = ParametersValidate.input(measurement.values, templateData.parameters, 'null');
|
||||||
if (error) {res400(error, res); return false;}
|
if (error) {res400(error, res); return false;}
|
||||||
return value || true;
|
return value || true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setStatus (status, req, res, next) { // set measurement status
|
function setStatus (status, req, res, next) { // set measurement status
|
||||||
MeasurementModel.findByIdAndUpdate(req.params.id, {status: status}).log(req).lean().exec((err, data) => {
|
MeasurementModel.findByIdAndUpdate(req.params.id, {status: status}).log(req).lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
res.json({status: 'OK'});
|
res.json({status: 'OK'});
|
||||||
});
|
});
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
@ -13,162 +13,162 @@ import mongoose from "mongoose";
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.get('/model/groups', (req, res, next) => {
|
router.get('/model/groups', (req, res, next) => {
|
||||||
if (!req.auth(res, ['predict', 'read', 'write', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['predict', 'read', 'write', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
let conditions: any = [{}, {}];
|
let conditions: any = [{}, {}];
|
||||||
if (['dev', 'admin'].indexOf(req.authDetails.level) < 0) { // if not dev or admin, user has to possess model rights
|
if (['dev', 'admin'].indexOf(req.authDetails.level) < 0) { // if not dev or admin, user has to possess model rights
|
||||||
conditions = [
|
conditions = [
|
||||||
{'models._id': {$in: req.authDetails.models.map(e => mongoose.Types.ObjectId(e))}},
|
{'models._id': {$in: req.authDetails.models.map(e => mongoose.Types.ObjectId(e))}},
|
||||||
{group: true, 'models.$': true}
|
{group: true, 'models.$': true}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
ModelModel.find(...conditions).lean().exec((err, data) => {
|
ModelModel.find(...conditions).lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
// validate all and filter null values from validation errors
|
// validate all and filter null values from validation errors
|
||||||
res.json(_.compact(data.map(e => ModelValidate.output(e))));
|
res.json(_.compact(data.map(e => ModelValidate.output(e))));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/model/:group', (req, res, next) => {
|
router.post('/model/:group', (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
const {error, value: model} = ModelValidate.input(req.body);
|
const {error, value: model} = ModelValidate.input(req.body);
|
||||||
if (error) return res400(error, res);
|
if (error) return res400(error, res);
|
||||||
|
|
||||||
ModelModel.findOne({group: req.params.group}).lean().exec((err, data) => {
|
ModelModel.findOne({group: req.params.group}).lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
if (data) { // group exists
|
if (data) { // group exists
|
||||||
if (data.models.find(e => e.name === model.name)) { // name exists, overwrite
|
if (data.models.find(e => e.name === model.name)) { // name exists, overwrite
|
||||||
ModelModel.findOneAndUpdate(
|
ModelModel.findOneAndUpdate(
|
||||||
{$and: [{group: req.params.group}, {'models.name': model.name}]},
|
{$and: [{group: req.params.group}, {'models.name': model.name}]},
|
||||||
{'models.$': model},
|
{'models.$': model},
|
||||||
{upsert: true}).log(req).lean().exec(err => {
|
{upsert: true}).log(req).lean().exec(err => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
res.json({status: 'OK'})
|
res.json({status: 'OK'})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else { // create new
|
else { // create new
|
||||||
ModelModel.findOneAndUpdate(
|
ModelModel.findOneAndUpdate(
|
||||||
{group: req.params.group},
|
{group: req.params.group},
|
||||||
{$push: {models: model as never}}
|
{$push: {models: model as never}}
|
||||||
).log(req).lean().exec(err => {
|
).log(req).lean().exec(err => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
res.json({status: 'OK'});
|
res.json({status: 'OK'});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { // create new group
|
else { // create new group
|
||||||
new ModelModel({group: req.params.group, models: [model]}).save((err, data) => {
|
new ModelModel({group: req.params.group, models: [model]}).save((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
db.log(req, 'models', {_id: data._id}, data.toObject());
|
db.log(req, 'models', {_id: data._id}, data.toObject());
|
||||||
res.json({status: 'OK'});
|
res.json({status: 'OK'});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/model/:group(((?!file)[^\\/]+?))/:name', (req, res, next) => {
|
router.delete('/model/:group(((?!file)[^\\/]+?))/:name', (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
ModelModel.findOne({group: req.params.group}).lean().exec((err, data) => {
|
ModelModel.findOne({group: req.params.group}).lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
if (!data || !data.models.find(e => e.name === req.params.name)) {
|
if (!data || !data.models.find(e => e.name === req.params.name)) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
// delete all references in user.models
|
// delete all references in user.models
|
||||||
UserModel.updateMany({}, {$pull: {models: data.models.find(e => e.name === req.params.name)._id as never}},
|
UserModel.updateMany({}, {$pull: {models: data.models.find(e => e.name === req.params.name)._id as never}},
|
||||||
{ multi: true }).log(req).lean().exec(err => {
|
{ multi: true }).log(req).lean().exec(err => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (data.models.length > 1) { // only remove model
|
if (data.models.length > 1) { // only remove model
|
||||||
ModelModel.findOneAndUpdate(
|
ModelModel.findOneAndUpdate(
|
||||||
{group: req.params.group},
|
{group: req.params.group},
|
||||||
{$pull: {models: data.models.find(e => e.name === req.params.name) as never}}
|
{$pull: {models: data.models.find(e => e.name === req.params.name) as never}}
|
||||||
).log(req).lean().exec(err => {
|
).log(req).lean().exec(err => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
res.json({status: 'OK'})
|
res.json({status: 'OK'})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else { // remove document
|
else { // remove document
|
||||||
ModelModel.findOneAndDelete({group: req.params.group}).log(req).lean().exec(err => {
|
ModelModel.findOneAndDelete({group: req.params.group}).log(req).lean().exec(err => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
res.json({status: 'OK'})
|
res.json({status: 'OK'})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/model/files', (req, res, next) => {
|
router.get('/model/files', (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
ModelFileModel.find().exec((err, data) => {
|
ModelFileModel.find().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
res.json(data.map(e => ModelValidate.fileOutput(e)));
|
res.json(data.map(e => ModelValidate.fileOutput(e)));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/model/file/:name', (req, res, next) => {
|
router.get('/model/file/:name', (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'all')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'all')) return;
|
||||||
|
|
||||||
ModelFileModel.findOne({name: req.params.name}).lean().exec((err, data) => {
|
ModelFileModel.findOne({name: req.params.name}).lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (data) {
|
if (data) {
|
||||||
res.set('Content-Type', 'application/octet-stream');
|
res.set('Content-Type', 'application/octet-stream');
|
||||||
res.send(data.data.buffer);
|
res.send(data.data.buffer);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.status(404).json({status: 'Not found'});
|
res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/model/file/:name', bodyParser.raw({limit: '50mb'}), (req, res, next) => {
|
router.post('/model/file/:name', bodyParser.raw({limit: '50mb'}), (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'all')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'all')) return;
|
||||||
|
|
||||||
ModelFileModel.replaceOne({name: req.params.name}, {name: req.params.name, data: req.body}).setOptions({upsert: true})
|
ModelFileModel.replaceOne({name: req.params.name}, {name: req.params.name, data: req.body}).setOptions({upsert: true})
|
||||||
.lean().exec(err => {
|
.lean().exec(err => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
res.json({status: 'OK'});
|
res.json({status: 'OK'});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete('/model/file/:name', (req, res, next) => {
|
router.delete('/model/file/:name', (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
ModelFileModel.findOneAndDelete({name: req.params.name}).lean().exec((err, data) => {
|
ModelFileModel.findOneAndDelete({name: req.params.name}).lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (data) {
|
if (data) {
|
||||||
res.json({status: 'OK'});
|
res.json({status: 'OK'});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.status(404).json({status: 'Not found'});
|
res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/model/authorized/:url', (req, res, next) => {
|
router.get('/model/authorized/:url', (req, res, next) => {
|
||||||
if (!req.auth(res, ['predict', 'read', 'write', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['predict', 'read', 'write', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
if (['dev', 'admin'].indexOf(req.authDetails.level) < 0) { // if not dev or admin, user has to possess model rights
|
if (['dev', 'admin'].indexOf(req.authDetails.level) < 0) { // if not dev or admin, user has to possess model rights
|
||||||
ModelModel.findOne({models: { $elemMatch: {
|
ModelModel.findOne({models: { $elemMatch: {
|
||||||
url: decodeURIComponent(req.params.url),
|
url: decodeURIComponent(req.params.url),
|
||||||
'_id': {$in: req.authDetails.models.map(e => mongoose.Types.ObjectId(e))}
|
'_id': {$in: req.authDetails.models.map(e => mongoose.Types.ObjectId(e))}
|
||||||
}}}).lean().exec((err, data) => {
|
}}}).lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (data) {
|
if (data) {
|
||||||
res.json({status: 'OK'});
|
res.json({status: 'OK'});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.status(403).json({status: 'Forbidden'});
|
res.status(403).json({status: 'Forbidden'});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.json({status: 'OK'});
|
res.json({status: 'OK'});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
@ -4,254 +4,254 @@ import db from '../db';
|
|||||||
|
|
||||||
|
|
||||||
describe('/', () => {
|
describe('/', () => {
|
||||||
let server;
|
let server;
|
||||||
before(done => TestHelper.before(done));
|
before(done => TestHelper.before(done));
|
||||||
beforeEach(done => server = TestHelper.beforeEach(server, done));
|
beforeEach(done => server = TestHelper.beforeEach(server, done));
|
||||||
afterEach(done => TestHelper.afterEach(server, done));
|
afterEach(done => TestHelper.afterEach(server, done));
|
||||||
after(done => TestHelper.after(done));
|
after(done => TestHelper.after(done));
|
||||||
|
|
||||||
describe('GET /', () => {
|
describe('GET /', () => {
|
||||||
it('returns the root message', done => {
|
it('returns the root message', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/',
|
url: '/',
|
||||||
httpStatus: 200,
|
httpStatus: 200,
|
||||||
res: {status: 'API server up and running!'}
|
res: {status: 'API server up and running!'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /changelog/{timestamp}/{page}/{pagesize}', () => {
|
describe('GET /changelog/{timestamp}/{page}/{pagesize}', () => {
|
||||||
it('returns the first page', done => {
|
it('returns the first page', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/changelog/120000030000000000000000/0/2',
|
url: '/changelog/120000030000000000000000/0/2',
|
||||||
auth: {basic: 'admin'},
|
auth: {basic: 'admin'},
|
||||||
httpStatus: 200
|
httpStatus: 200
|
||||||
}).end((err, res) => {
|
}).end((err, res) => {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
should(res.body).have.lengthOf(2);
|
should(res.body).have.lengthOf(2);
|
||||||
should(res.body[0].date).be.eql('1979-07-28T06:04:51.000Z');
|
should(res.body[0].date).be.eql('1979-07-28T06:04:51.000Z');
|
||||||
should(res.body[1].date).be.eql('1979-07-28T06:04:50.000Z');
|
should(res.body[1].date).be.eql('1979-07-28T06:04:50.000Z');
|
||||||
should(res.body).matchEach(log => {
|
should(res.body).matchEach(log => {
|
||||||
should(log).have.only.keys('_id', 'date', 'action', 'collection', 'conditions', 'data');
|
should(log).have.only.keys('_id', 'date', 'action', 'collection', 'conditions', 'data');
|
||||||
should(log).have.property('_id').be.type('string');
|
should(log).have.property('_id').be.type('string');
|
||||||
should(log).have.property('action', 'PUT /sample/400000000000000000000001');
|
should(log).have.property('action', 'PUT /sample/400000000000000000000001');
|
||||||
should(log).have.property('collection', 'samples');
|
should(log).have.property('collection', 'samples');
|
||||||
should(log).have.property('conditions', {_id: '400000000000000000000001'});
|
should(log).have.property('conditions', {_id: '400000000000000000000001'});
|
||||||
should(log).have.property('data', {type: 'processed', status: 0});
|
should(log).have.property('data', {type: 'processed', status: 0});
|
||||||
});
|
});
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('returns another page', done => {
|
it('returns another page', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/changelog/120000030000000000000000/1/2',
|
url: '/changelog/120000030000000000000000/1/2',
|
||||||
auth: {basic: 'admin'},
|
auth: {basic: 'admin'},
|
||||||
httpStatus: 200
|
httpStatus: 200
|
||||||
}).end((err, res) => {
|
}).end((err, res) => {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
should(res.body).have.lengthOf(1);
|
should(res.body).have.lengthOf(1);
|
||||||
should(res.body[0].date).be.eql('1979-07-28T06:04:49.000Z');
|
should(res.body[0].date).be.eql('1979-07-28T06:04:49.000Z');
|
||||||
should(res.body).matchEach(log => {
|
should(res.body).matchEach(log => {
|
||||||
should(log).have.only.keys('_id', 'date', 'action', 'collection', 'conditions', 'data');
|
should(log).have.only.keys('_id', 'date', 'action', 'collection', 'conditions', 'data');
|
||||||
should(log).have.property('_id').be.type('string');
|
should(log).have.property('_id').be.type('string');
|
||||||
should(log).have.property('action', 'PUT /sample/400000000000000000000001');
|
should(log).have.property('action', 'PUT /sample/400000000000000000000001');
|
||||||
should(log).have.property('collection', 'samples');
|
should(log).have.property('collection', 'samples');
|
||||||
should(log).have.property('conditions', {_id: '400000000000000000000001'});
|
should(log).have.property('conditions', {_id: '400000000000000000000001'});
|
||||||
should(log).have.property('data', {type: 'processed', status: 0});
|
should(log).have.property('data', {type: 'processed', status: 0});
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('returns an empty array for a page with no results', done => {
|
it('returns an empty array for a page with no results', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/changelog/120000030000000000000000/10/2',
|
url: '/changelog/120000030000000000000000/10/2',
|
||||||
auth: {basic: 'admin'},
|
auth: {basic: 'admin'},
|
||||||
httpStatus: 200
|
httpStatus: 200
|
||||||
}).end((err, res) => {
|
}).end((err, res) => {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
should(res.body).have.lengthOf(0);
|
should(res.body).have.lengthOf(0);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects invalid ids', done => {
|
it('rejects invalid ids', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/changelog/12000003000000h000000000/10/2',
|
url: '/changelog/12000003000000h000000000/10/2',
|
||||||
auth: {basic: 'admin'},
|
auth: {basic: 'admin'},
|
||||||
httpStatus: 400,
|
httpStatus: 400,
|
||||||
res: {status: 'Invalid body format', details: 'Invalid object id'}
|
res: {status: 'Invalid body format', details: 'Invalid object id'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects negative page numbers', done => {
|
it('rejects negative page numbers', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/changelog/120000030000000000000000/-10/2',
|
url: '/changelog/120000030000000000000000/-10/2',
|
||||||
auth: {basic: 'admin'},
|
auth: {basic: 'admin'},
|
||||||
httpStatus: 400,
|
httpStatus: 400,
|
||||||
res: {status: 'Invalid body format', details: '"page" must be greater than or equal to 0'}
|
res: {status: 'Invalid body format', details: '"page" must be greater than or equal to 0'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects negative pagesizes', done => {
|
it('rejects negative pagesizes', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/changelog/120000030000000000000000/10/-2',
|
url: '/changelog/120000030000000000000000/10/-2',
|
||||||
auth: {basic: 'admin'},
|
auth: {basic: 'admin'},
|
||||||
httpStatus: 400,
|
httpStatus: 400,
|
||||||
res: {status: 'Invalid body format', details: '"pagesize" must be greater than or equal to 0'}
|
res: {status: 'Invalid body format', details: '"pagesize" must be greater than or equal to 0'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects request from a write user', done => {
|
it('rejects request from a write user', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/changelog/120000030000000000000000/10/2',
|
url: '/changelog/120000030000000000000000/10/2',
|
||||||
auth: {basic: 'janedoe'},
|
auth: {basic: 'janedoe'},
|
||||||
httpStatus: 403
|
httpStatus: 403
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects requests from an API key', done => {
|
it('rejects requests from an API key', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/changelog/120000030000000000000000/10/2',
|
url: '/changelog/120000030000000000000000/10/2',
|
||||||
auth: {key: 'admin'},
|
auth: {key: 'admin'},
|
||||||
httpStatus: 401
|
httpStatus: 401
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects unauthorized requests', done => {
|
it('rejects unauthorized requests', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/changelog/120000030000000000000000/10/2',
|
url: '/changelog/120000030000000000000000/10/2',
|
||||||
httpStatus: 401
|
httpStatus: 401
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Unknown routes', () => {
|
describe('Unknown routes', () => {
|
||||||
it('return a 404 message', done => {
|
it('return a 404 message', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/unknownroute',
|
url: '/unknownroute',
|
||||||
httpStatus: 404
|
httpStatus: 404
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('An unauthorized request', () => {
|
describe('An unauthorized request', () => {
|
||||||
it('returns a 401 message', done => {
|
it('returns a 401 message', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/authorized',
|
url: '/authorized',
|
||||||
httpStatus: 401
|
httpStatus: 401
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('does not work with correct username', done => {
|
it('does not work with correct username', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/authorized',
|
url: '/authorized',
|
||||||
auth: {basic: {name: 'admin', pass: 'Abc123!!'}},
|
auth: {basic: {name: 'admin', pass: 'Abc123!!'}},
|
||||||
httpStatus: 401
|
httpStatus: 401
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('does not work with incorrect username', done => {
|
it('does not work with incorrect username', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/authorized',
|
url: '/authorized',
|
||||||
auth: {basic: {name: 'adminxx', pass: 'Abc123!!'}},
|
auth: {basic: {name: 'adminxx', pass: 'Abc123!!'}},
|
||||||
httpStatus: 401
|
httpStatus: 401
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('does not work with a deleted user', done => {
|
it('does not work with a deleted user', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/authorized',
|
url: '/authorized',
|
||||||
auth: {basic: {name: 'customerold', pass: 'Xyz890*)'}},
|
auth: {basic: {name: 'customerold', pass: 'Xyz890*)'}},
|
||||||
httpStatus: 401
|
httpStatus: 401
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('An authorized request', () => {
|
describe('An authorized request', () => {
|
||||||
it('works with an API key', done => {
|
it('works with an API key', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/authorized',
|
url: '/authorized',
|
||||||
auth: {key: 'admin'},
|
auth: {key: 'admin'},
|
||||||
httpStatus: 200,
|
httpStatus: 200,
|
||||||
res: {status: 'Authorization successful', method: 'key', level: 'admin', user_id: '000000000000000000000003'}
|
res: {status: 'Authorization successful', method: 'key', level: 'admin', user_id: '000000000000000000000003'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('works with basic auth', done => {
|
it('works with basic auth', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/authorized',
|
url: '/authorized',
|
||||||
auth: {basic: 'admin'},
|
auth: {basic: 'admin'},
|
||||||
httpStatus: 200,
|
httpStatus: 200,
|
||||||
res: {status: 'Authorization successful', method: 'basic', level: 'admin', user_id: '000000000000000000000003'}
|
res: {status: 'Authorization successful', method: 'basic', level: 'admin', user_id: '000000000000000000000003'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('An invalid JSON body', () => {
|
describe('An invalid JSON body', () => {
|
||||||
it('is rejected', done => {
|
it('is rejected', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: '/',
|
url: '/',
|
||||||
httpStatus: 400,
|
httpStatus: 400,
|
||||||
reqType: 'json',
|
reqType: 'json',
|
||||||
req: '{"xxx"}',
|
req: '{"xxx"}',
|
||||||
res: {status: 'Invalid JSON body'}
|
res: {status: 'Invalid JSON body'}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// describe('A not connected database', () => { // RUN AS LAST OR RECONNECT DATABASE!!
|
// describe('A not connected database', () => { // RUN AS LAST OR RECONNECT DATABASE!!
|
||||||
// it('resolves to an 500 error', done => {
|
// it('resolves to an 500 error', done => {
|
||||||
// db.disconnect(() => {
|
// db.disconnect(() => {
|
||||||
// TestHelper.request(server, done, {
|
// TestHelper.request(server, done, {
|
||||||
// method: 'get',
|
// method: 'get',
|
||||||
// url: '/',
|
// url: '/',
|
||||||
// httpStatus: 500
|
// httpStatus: 500
|
||||||
// });
|
// });
|
||||||
// });
|
// });
|
||||||
// });
|
// });
|
||||||
// });
|
// });
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('The /api/{url} redirect', () => {
|
describe('The /api/{url} redirect', () => {
|
||||||
let server;
|
let server;
|
||||||
let counter = 0; // count number of current test method
|
let counter = 0; // count number of current test method
|
||||||
before(done => {
|
before(done => {
|
||||||
process.env.port = '2999';
|
process.env.port = '2999';
|
||||||
db.connect('test', done);
|
db.connect('test', done);
|
||||||
});
|
});
|
||||||
beforeEach(done => {
|
beforeEach(done => {
|
||||||
process.env.NODE_ENV = counter === 1 ? 'production' : 'test';
|
process.env.NODE_ENV = counter === 1 ? 'production' : 'test';
|
||||||
counter ++;
|
counter ++;
|
||||||
server = TestHelper.beforeEach(server, done);
|
server = TestHelper.beforeEach(server, done);
|
||||||
});
|
});
|
||||||
afterEach(done => TestHelper.afterEach(server, done));
|
afterEach(done => TestHelper.afterEach(server, done));
|
||||||
after(done => TestHelper.after(done));
|
after(done => TestHelper.after(done));
|
||||||
|
|
||||||
|
|
||||||
it('returns the right method', done => {
|
it('returns the right method', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: '/api/authorized',
|
url: '/api/authorized',
|
||||||
auth: {basic: 'admin'},
|
auth: {basic: 'admin'},
|
||||||
httpStatus: 200,
|
httpStatus: 200,
|
||||||
res: {status: 'Authorization successful', method: 'basic', level: 'admin', user_id: '000000000000000000000003'}
|
res: {status: 'Authorization successful', method: 'basic', level: 'admin', user_id: '000000000000000000000003'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// it('is disabled in production', done => {
|
// it('is disabled in production', done => {
|
||||||
// TestHelper.request(server, done, {
|
// TestHelper.request(server, done, {
|
||||||
// method: 'get',
|
// method: 'get',
|
||||||
// url: '/api/authorized',
|
// url: '/api/authorized',
|
||||||
// auth: {basic: 'admin'},
|
// auth: {basic: 'admin'},
|
||||||
// httpStatus: 404
|
// httpStatus: 404
|
||||||
// });
|
// });
|
||||||
// });
|
// });
|
||||||
});
|
});
|
@ -9,37 +9,37 @@ import _ from 'lodash';
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.get('/', (req, res) => {
|
router.get('/', (req, res) => {
|
||||||
res.json({status: 'API server up and running!'});
|
res.json({status: 'API server up and running!'});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/authorized', (req, res) => {
|
router.get('/authorized', (req, res) => {
|
||||||
if (!req.auth(res, Object.values(globals.levels))) return;
|
if (!req.auth(res, Object.values(globals.levels))) return;
|
||||||
res.json({
|
res.json({
|
||||||
status: 'Authorization successful',
|
status: 'Authorization successful',
|
||||||
method: req.authDetails.method,
|
method: req.authDetails.method,
|
||||||
level: req.authDetails.level,
|
level: req.authDetails.level,
|
||||||
user_id: req.authDetails.id
|
user_id: req.authDetails.id
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/changelog/:id/:page?/:pagesize?', (req, res, next) => {
|
router.get('/changelog/:id/:page?/:pagesize?', (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
const {error, value: options} = RootValidate.changelogParams({
|
const {error, value: options} = RootValidate.changelogParams({
|
||||||
id: req.params.id,
|
id: req.params.id,
|
||||||
page: req.params.page,
|
page: req.params.page,
|
||||||
pagesize: req.params.pagesize
|
pagesize: req.params.pagesize
|
||||||
});
|
});
|
||||||
if (error) return res400(error, res);
|
if (error) return res400(error, res);
|
||||||
|
|
||||||
ChangelogModel.find({_id: {$lte: mongoose.Types.ObjectId(options.id)}})
|
ChangelogModel.find({_id: {$lte: mongoose.Types.ObjectId(options.id)}})
|
||||||
.sort({_id: -1}).skip(options.page * options.pagesize).limit(options.pagesize)
|
.sort({_id: -1}).skip(options.page * options.pagesize).limit(options.pagesize)
|
||||||
.lean().exec((err, data) => {
|
.lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
// validate all and filter null values from validation errors
|
// validate all and filter null values from validation errors
|
||||||
res.json(_.compact(data.map(e => RootValidate.changelogOutput(e))));
|
res.json(_.compact(data.map(e => RootValidate.changelogOutput(e))));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
File diff suppressed because it is too large
Load Diff
1556
src/routes/sample.ts
1556
src/routes/sample.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -18,141 +18,141 @@ import db from '../db';
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.get('/template/:collection(measurements|conditions|materials)', (req, res, next) => {
|
router.get('/template/:collection(measurements|conditions|materials)', (req, res, next) => {
|
||||||
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
req.params.collection = req.params.collection.replace(/s$/g, ''); // remove trailing s
|
req.params.collection = req.params.collection.replace(/s$/g, ''); // remove trailing s
|
||||||
model(req).find({}).lean().exec((err, data) => {
|
model(req).find({}).lean().exec((err, data) => {
|
||||||
if (err) next (err);
|
if (err) next (err);
|
||||||
// validate all and filter null values from validation errors
|
// validate all and filter null values from validation errors
|
||||||
res.json(_.compact(data.map(e => TemplateValidate.output(e))));
|
res.json(_.compact(data.map(e => TemplateValidate.output(e))));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/template/:collection(measurement|condition|material)/' + IdValidate.parameter(), (req, res, next) => {
|
router.get('/template/:collection(measurement|condition|material)/' + IdValidate.parameter(), (req, res, next) => {
|
||||||
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
model(req).findById(req.params.id).lean().exec((err, data) => {
|
model(req).findById(req.params.id).lean().exec((err, data) => {
|
||||||
if (err) next (err);
|
if (err) next (err);
|
||||||
if (data) {
|
if (data) {
|
||||||
res.json(TemplateValidate.output(data));
|
res.json(TemplateValidate.output(data));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.status(404).json({status: 'Not found'});
|
res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/template/:collection(measurement|condition|material)/' + IdValidate.parameter(),
|
router.put('/template/:collection(measurement|condition|material)/' + IdValidate.parameter(),
|
||||||
async (req, res, next) => {
|
async (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
const {error, value: template} = TemplateValidate.input(req.body, 'change');
|
const {error, value: template} = TemplateValidate.input(req.body, 'change');
|
||||||
if (error) return res400(error, res);
|
if (error) return res400(error, res);
|
||||||
|
|
||||||
// find given template
|
// find given template
|
||||||
const templateRef = await model(req).findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
|
const templateRef = await model(req).findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
|
||||||
if (templateRef instanceof Error) return;
|
if (templateRef instanceof Error) return;
|
||||||
if (!templateRef) {
|
if (!templateRef) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
// find latest version
|
// find latest version
|
||||||
const templateData = await model(req).findOne({first_id: templateRef.first_id}).sort({version: -1})
|
const templateData = await model(req).findOne({first_id: templateRef.first_id}).sort({version: -1})
|
||||||
.lean().exec().catch(err => {next(err);}) as any;
|
.lean().exec().catch(err => {next(err);}) as any;
|
||||||
if (templateData instanceof Error) return;
|
if (templateData instanceof Error) return;
|
||||||
if (!templateData) {
|
if (!templateData) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_.isEqual(_.pick(templateData, _.keys(template)), template)) { // data was changed
|
if (!_.isEqual(_.pick(templateData, _.keys(template)), template)) { // data was changed
|
||||||
if (!template.parameters || _.isEqual(templateData.parameters, template.parameters)) { // only name was changed
|
if (!template.parameters || _.isEqual(templateData.parameters, template.parameters)) { // only name was changed
|
||||||
model(req).findByIdAndUpdate(req.params.id, {name: template.name}, {new: true})
|
model(req).findByIdAndUpdate(req.params.id, {name: template.name}, {new: true})
|
||||||
.log(req).lean().exec((err, data) => {
|
.log(req).lean().exec((err, data) => {
|
||||||
if (err) next (err);
|
if (err) next (err);
|
||||||
res.json(TemplateValidate.output(data));
|
res.json(TemplateValidate.output(data));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (template.parameters.filter((e, i) => _.isEqual(e.range, templateData.parameters[i].range)).length
|
else if (template.parameters.filter((e, i) => _.isEqual(e.range, templateData.parameters[i].range)).length
|
||||||
=== templateData.parameters.length) { // only names changed
|
=== templateData.parameters.length) { // only names changed
|
||||||
const changedParameterNames = template.parameters.map((e, i) => ( // list of new names
|
const changedParameterNames = template.parameters.map((e, i) => ( // list of new names
|
||||||
{name: e.name, index: i, oldName: templateData.parameters[i].name}
|
{name: e.name, index: i, oldName: templateData.parameters[i].name}
|
||||||
)).filter(e => e.name !== e.oldName);
|
)).filter(e => e.name !== e.oldName);
|
||||||
|
|
||||||
// custom mappings for different collections
|
// custom mappings for different collections
|
||||||
let targetModel; // model of the collection where the template is used
|
let targetModel; // model of the collection where the template is used
|
||||||
let pathPrefix; // path to the parameters in use
|
let pathPrefix; // path to the parameters in use
|
||||||
let templatePath; // complete path of the template property
|
let templatePath; // complete path of the template property
|
||||||
switch (req.params.collection) {
|
switch (req.params.collection) {
|
||||||
case 'condition':
|
case 'condition':
|
||||||
targetModel = SampleModel;
|
targetModel = SampleModel;
|
||||||
pathPrefix = 'condition.';
|
pathPrefix = 'condition.';
|
||||||
templatePath = 'condition.condition_template';
|
templatePath = 'condition.condition_template';
|
||||||
break;
|
break;
|
||||||
case 'measurement':
|
case 'measurement':
|
||||||
targetModel = MeasurementModel;
|
targetModel = MeasurementModel;
|
||||||
pathPrefix = 'values.';
|
pathPrefix = 'values.';
|
||||||
templatePath = 'measurement_template';
|
templatePath = 'measurement_template';
|
||||||
break;
|
break;
|
||||||
case 'material':
|
case 'material':
|
||||||
targetModel = MaterialModel;
|
targetModel = MaterialModel;
|
||||||
pathPrefix = 'properties.';
|
pathPrefix = 'properties.';
|
||||||
templatePath = 'properties.material_template';
|
templatePath = 'properties.material_template';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
targetModel.updateMany({[templatePath]: mongoose.Types.ObjectId(templateData._id)},
|
targetModel.updateMany({[templatePath]: mongoose.Types.ObjectId(templateData._id)},
|
||||||
{$rename:
|
{$rename:
|
||||||
changedParameterNames.reduce((s, e) => {s[pathPrefix + e.oldName] = pathPrefix + e.name; return s;}, {})
|
changedParameterNames.reduce((s, e) => {s[pathPrefix + e.oldName] = pathPrefix + e.name; return s;}, {})
|
||||||
}) .log(req).lean().exec(err => {
|
}) .log(req).lean().exec(err => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
model(req).findByIdAndUpdate(req.params.id,
|
model(req).findByIdAndUpdate(req.params.id,
|
||||||
{$set:
|
{$set:
|
||||||
changedParameterNames.reduce(
|
changedParameterNames.reduce(
|
||||||
(s, e) => {s[`parameters.${e.index}.name`] = e.name; return s;}, {name: template.name}
|
(s, e) => {s[`parameters.${e.index}.name`] = e.name; return s;}, {name: template.name}
|
||||||
),
|
),
|
||||||
},{new: true}).log(req).lean().exec((err, data) => {
|
},{new: true}).log(req).lean().exec((err, data) => {
|
||||||
if (err) next (err);
|
if (err) next (err);
|
||||||
res.json(TemplateValidate.output(data));
|
res.json(TemplateValidate.output(data));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
template.version = templateData.version + 1; // increase version
|
template.version = templateData.version + 1; // increase version
|
||||||
// save new template, fill with old properties
|
// save new template, fill with old properties
|
||||||
await new (model(req))(_.assign({}, _.omit(templateData, ['_id', '__v']), template)).save((err, data) => {
|
await new (model(req))(_.assign({}, _.omit(templateData, ['_id', '__v']), template)).save((err, data) => {
|
||||||
if (err) next (err);
|
if (err) next (err);
|
||||||
db.log(req, req.params.collection + '_templates', {_id: data._id}, data.toObject());
|
db.log(req, req.params.collection + '_templates', {_id: data._id}, data.toObject());
|
||||||
res.json(TemplateValidate.output(data.toObject()));
|
res.json(TemplateValidate.output(data.toObject()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.json(TemplateValidate.output(templateData));
|
res.json(TemplateValidate.output(templateData));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/template/:collection(measurement|condition|material)/new', async (req, res, next) => {
|
router.post('/template/:collection(measurement|condition|material)/new', async (req, res, next) => {
|
||||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
const {error, value: template} = TemplateValidate.input(req.body, 'new');
|
const {error, value: template} = TemplateValidate.input(req.body, 'new');
|
||||||
if (error) return res400(error, res);
|
if (error) return res400(error, res);
|
||||||
|
|
||||||
template._id = mongoose.Types.ObjectId(); // set reference to itself for first version of template
|
template._id = mongoose.Types.ObjectId(); // set reference to itself for first version of template
|
||||||
template.first_id = template._id;
|
template.first_id = template._id;
|
||||||
template.version = 1; // set template version
|
template.version = 1; // set template version
|
||||||
await new (model(req))(template).save((err, data) => {
|
await new (model(req))(template).save((err, data) => {
|
||||||
if (err) next (err);
|
if (err) next (err);
|
||||||
db.log(req, req.params.collection + '_templates', {_id: data._id}, data.toObject());
|
db.log(req, req.params.collection + '_templates', {_id: data._id}, data.toObject());
|
||||||
res.json(TemplateValidate.output(data.toObject()));
|
res.json(TemplateValidate.output(data.toObject()));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
||||||
function model (req) { // return right template model
|
function model (req) { // return right template model
|
||||||
switch (req.params.collection) {
|
switch (req.params.collection) {
|
||||||
case 'condition': return ConditionTemplateModel
|
case 'condition': return ConditionTemplateModel
|
||||||
case 'measurement': return MeasurementTemplateModel
|
case 'measurement': return MeasurementTemplateModel
|
||||||
case 'material': return MaterialTemplateModel
|
case 'material': return MaterialTemplateModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -15,206 +15,206 @@ const router = express.Router();
|
|||||||
|
|
||||||
|
|
||||||
router.get('/users', (req, res) => {
|
router.get('/users', (req, res) => {
|
||||||
if (!req.auth(res, ['admin'], 'basic')) return;
|
if (!req.auth(res, ['admin'], 'basic')) return;
|
||||||
|
|
||||||
UserModel.find({}).lean().exec( (err, data:any) => {
|
UserModel.find({}).lean().exec( (err, data:any) => {
|
||||||
// validate all and filter null values from validation errors
|
// validate all and filter null values from validation errors
|
||||||
res.json(_.compact(data.map(e => UserValidate.output(e, 'admin'))));
|
res.json(_.compact(data.map(e => UserValidate.output(e, 'admin'))));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// this path matches /user, /user/ and /user/xxx, but not /user/key or user/new.
|
// this path matches /user, /user/ and /user/xxx, but not /user/key or user/new.
|
||||||
// See https://forbeslindesay.github.io/express-route-tester/ for the generated regex
|
// See https://forbeslindesay.github.io/express-route-tester/ for the generated regex
|
||||||
router.get('/user:username([/](?!key|new).?*|/?)', (req, res, next) => {
|
router.get('/user:username([/](?!key|new).?*|/?)', (req, res, next) => {
|
||||||
if (!req.auth(res, ['predict', 'read', 'write', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['predict', 'read', 'write', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
const username = getUsername(req, res);
|
const username = getUsername(req, res);
|
||||||
if (!username) return;
|
if (!username) return;
|
||||||
UserModel.findOne({name: username}).lean().exec( (err, data:any) => {
|
UserModel.findOne({name: username}).lean().exec( (err, data:any) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (data) {
|
if (data) {
|
||||||
res.json(UserValidate.output(data)); // validate all and filter null values from validation errors
|
res.json(UserValidate.output(data)); // validate all and filter null values from validation errors
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.status(404).json({status: 'Not found'});
|
res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// this path matches /user, /user/ and /user/xxx, but not /user/key or user/new
|
// this path matches /user, /user/ and /user/xxx, but not /user/key or user/new
|
||||||
router.put('/user:username([/](?!key|new|restore).?*|/?)', async (req, res, next) => {
|
router.put('/user:username([/](?!key|new|restore).?*|/?)', async (req, res, next) => {
|
||||||
if (!req.auth(res, ['predict', 'read', 'write', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['predict', 'read', 'write', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
const username = getUsername(req, res);
|
const username = getUsername(req, res);
|
||||||
if (!username) return;
|
if (!username) return;
|
||||||
|
|
||||||
const {error, value: user} = UserValidate.input(req.body, 'change' +
|
const {error, value: user} = UserValidate.input(req.body, 'change' +
|
||||||
(req.authDetails.level === 'admin'? 'admin' : ''));
|
(req.authDetails.level === 'admin'? 'admin' : ''));
|
||||||
if (error) return res400(error, res);
|
if (error) return res400(error, res);
|
||||||
|
|
||||||
if (user.hasOwnProperty('pass')) {
|
if (user.hasOwnProperty('pass')) {
|
||||||
user.pass = bcrypt.hashSync(user.pass, 10);
|
user.pass = bcrypt.hashSync(user.pass, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that user does not already exist if new name was specified
|
// check that user does not already exist if new name was specified
|
||||||
if (user.hasOwnProperty('name') && user.name !== username) {
|
if (user.hasOwnProperty('name') && user.name !== username) {
|
||||||
if (!await usernameCheck(user.name, res, next)) return;
|
if (!await usernameCheck(user.name, res, next)) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.hasOwnProperty('models')) {
|
if (user.hasOwnProperty('models')) {
|
||||||
if (!await modelsCheck(user.models, res, next)) return;
|
if (!await modelsCheck(user.models, res, next)) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get current mail address to compare to given address
|
// get current mail address to compare to given address
|
||||||
const oldUserData = await UserModel.findOne({name: username}).lean().exec().catch(err => next(err));
|
const oldUserData = await UserModel.findOne({name: username}).lean().exec().catch(err => next(err));
|
||||||
|
|
||||||
await UserModel.findOneAndUpdate({name: username}, user, {new: true}).log(req).lean().exec( (err, data:any) => {
|
await UserModel.findOneAndUpdate({name: username}, user, {new: true}).log(req).lean().exec( (err, data:any) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (data) {
|
if (data) {
|
||||||
if (data.email !== oldUserData.email) { // mail address was changed, send notice to old address
|
if (data.email !== oldUserData.email) { // mail address was changed, send notice to old address
|
||||||
Mail.send(oldUserData.email, 'Email change in your DeFinMa database account',
|
Mail.send(oldUserData.email, 'Email change in your DeFinMa database account',
|
||||||
'Hi, <br><br> Your email address of your DeFinMa account was changed to ' + data.mail +
|
'Hi, <br><br> Your email address of your DeFinMa account was changed to ' + data.mail +
|
||||||
'<br><br>If you actually did this, just delete this email.' +
|
'<br><br>If you actually did this, just delete this email.' +
|
||||||
'<br><br>If you did not change your email, someone might be messing around with your account, ' +
|
'<br><br>If you did not change your email, someone might be messing around with your account, ' +
|
||||||
'so talk to the sysadmin quickly!<br><br>Have a nice day.' +
|
'so talk to the sysadmin quickly!<br><br>Have a nice day.' +
|
||||||
'<br><br>The DeFinMa team');
|
'<br><br>The DeFinMa team');
|
||||||
}
|
}
|
||||||
res.json(UserValidate.output(data));
|
res.json(UserValidate.output(data));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.status(404).json({status: 'Not found'});
|
res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// this path matches /user, /user/ and /user/xxx, but not /user/key or user/new.
|
// this path matches /user, /user/ and /user/xxx, but not /user/key or user/new.
|
||||||
// See https://forbeslindesay.github.io/express-route-tester/ for the generated regex
|
// See https://forbeslindesay.github.io/express-route-tester/ for the generated regex
|
||||||
router.delete('/user:username([/](?!key|new).?*|/?)', (req, res, next) => {
|
router.delete('/user:username([/](?!key|new).?*|/?)', (req, res, next) => {
|
||||||
if (!req.auth(res, ['predict', 'read', 'write', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['predict', 'read', 'write', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
const username = getUsername(req, res);
|
const username = getUsername(req, res);
|
||||||
if (!username) return;
|
if (!username) return;
|
||||||
|
|
||||||
UserModel.findOneAndUpdate({name: username}, {status: globals.status.del}).log(req).lean().exec( (err, data:any) => {
|
UserModel.findOneAndUpdate({name: username}, {status: globals.status.del}).log(req).lean().exec( (err, data:any) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (data) {
|
if (data) {
|
||||||
res.json({status: 'OK'})
|
res.json({status: 'OK'})
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.status(404).json({status: 'Not found'});
|
res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.put('/user/restore/:username', (req, res, next) => {
|
router.put('/user/restore/:username', (req, res, next) => {
|
||||||
if (!req.auth(res, ['admin'], 'basic')) return;
|
if (!req.auth(res, ['admin'], 'basic')) return;
|
||||||
|
|
||||||
UserModel.findOneAndUpdate({name: req.params.username}, {status: globals.status.new})
|
UserModel.findOneAndUpdate({name: req.params.username}, {status: globals.status.new})
|
||||||
.log(req).lean().exec((err, data) => {
|
.log(req).lean().exec((err, data) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
res.json({status: 'OK'});
|
res.json({status: 'OK'});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/user/key', (req, res, next) => {
|
router.get('/user/key', (req, res, next) => {
|
||||||
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
UserModel.findOne({name: req.authDetails.username}).lean().exec( (err, data:any) => {
|
UserModel.findOne({name: req.authDetails.username}).lean().exec( (err, data:any) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
res.json({key: data.key});
|
res.json({key: data.key});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/user/new', async (req, res, next) => {
|
router.post('/user/new', async (req, res, next) => {
|
||||||
if (!req.auth(res, ['admin'], 'basic')) return;
|
if (!req.auth(res, ['admin'], 'basic')) return;
|
||||||
|
|
||||||
// validate input
|
// validate input
|
||||||
const {error, value: user} = UserValidate.input(req.body, 'new');
|
const {error, value: user} = UserValidate.input(req.body, 'new');
|
||||||
if (error) return res400(error, res);
|
if (error) return res400(error, res);
|
||||||
|
|
||||||
// check that user does not already exist
|
// check that user does not already exist
|
||||||
if (!await usernameCheck(user.name, res, next)) return;
|
if (!await usernameCheck(user.name, res, next)) return;
|
||||||
if (!await modelsCheck(user.models, res, next)) return;
|
if (!await modelsCheck(user.models, res, next)) return;
|
||||||
|
|
||||||
user.key = mongoose.Types.ObjectId(); // use object id as unique API key
|
user.key = mongoose.Types.ObjectId(); // use object id as unique API key
|
||||||
user.status = globals.status.new;
|
user.status = globals.status.new;
|
||||||
bcrypt.hash(user.pass, 10, (err, hash) => { // password hashing
|
bcrypt.hash(user.pass, 10, (err, hash) => { // password hashing
|
||||||
user.pass = hash;
|
user.pass = hash;
|
||||||
new UserModel(user).save((err, data) => { // store user
|
new UserModel(user).save((err, data) => { // store user
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
db.log(req, 'users', {_id: data._id}, data.toObject());
|
db.log(req, 'users', {_id: data._id}, data.toObject());
|
||||||
res.json(UserValidate.output(data.toObject()));
|
res.json(UserValidate.output(data.toObject()));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/user/passreset', (req, res, next) => {
|
router.post('/user/passreset', (req, res, next) => {
|
||||||
// check if user/email combo exists
|
// check if user/email combo exists
|
||||||
UserModel.find({name: req.body.name, email: req.body.email}).lean().exec( (err, data: any) => {
|
UserModel.find({name: req.body.name, email: req.body.email}).lean().exec( (err, data: any) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (data.length === 1) { // it exists
|
if (data.length === 1) { // it exists
|
||||||
const newPass = Math.random().toString(36).substring(2); // generate temporary password
|
const newPass = Math.random().toString(36).substring(2); // generate temporary password
|
||||||
bcrypt.hash(newPass, 10, (err, hash) => { // password hashing
|
bcrypt.hash(newPass, 10, (err, hash) => { // password hashing
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
UserModel.findByIdAndUpdate(data[0]._id, {pass: hash}).log(req).exec(err => { // write new password
|
UserModel.findByIdAndUpdate(data[0]._id, {pass: hash}).log(req).exec(err => { // write new password
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
// send email
|
// send email
|
||||||
Mail.send(data[0].email, 'Your new password for the DeFinMa database',
|
Mail.send(data[0].email, 'Your new password for the DeFinMa database',
|
||||||
'Hi, <br><br> You requested to reset your password.<br>Your new password is:<br><br>' + newPass + '' +
|
'Hi, <br><br> You requested to reset your password.<br>Your new password is:<br><br>' + newPass + '' +
|
||||||
'<br><br>If you did not request a password reset, talk to the sysadmin quickly!<br><br>Have a nice day.' +
|
'<br><br>If you did not request a password reset, talk to the sysadmin quickly!<br><br>Have a nice day.' +
|
||||||
'<br><br>The DeFinMa team', err => {
|
'<br><br>The DeFinMa team', err => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
res.json({status: 'OK'});
|
res.json({status: 'OK'});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.status(404).json({status: 'Not found'});
|
res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
||||||
function getUsername (req, res) { // returns username or false if action is not allowed
|
function getUsername (req, res) { // returns username or false if action is not allowed
|
||||||
req.params.username = req.params[0]; // because of path regex
|
req.params.username = req.params[0]; // because of path regex
|
||||||
if (req.params.username !== undefined) { // different username than request user
|
if (req.params.username !== undefined) { // different username than request user
|
||||||
if (!req.auth(res, ['admin'], 'basic')) return false;
|
if (!req.auth(res, ['admin'], 'basic')) return false;
|
||||||
return req.params.username;
|
return req.params.username;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return req.authDetails.username;
|
return req.authDetails.username;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function usernameCheck (name, res, next) { // check if username is already taken
|
async function usernameCheck (name, res, next) { // check if username is already taken
|
||||||
const userData = await UserModel.findOne({name: name}).lean().exec().catch(err => next(err)) as any;
|
const userData = await UserModel.findOne({name: name}).lean().exec().catch(err => next(err)) as any;
|
||||||
if (userData instanceof Error) return false;
|
if (userData instanceof Error) return false;
|
||||||
if (userData || UserValidate.isSpecialName(name)) {
|
if (userData || UserValidate.isSpecialName(name)) {
|
||||||
res.status(400).json({status: 'Username already taken'});
|
res.status(400).json({status: 'Username already taken'});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function modelsCheck (models, res, next) { // check if model ids exist, returns false on error
|
async function modelsCheck (models, res, next) { // check if model ids exist, returns false on error
|
||||||
let result = true;
|
let result = true;
|
||||||
for (let i in models) {
|
for (let i in models) {
|
||||||
const model = await ModelModel.findOne({'models._id': mongoose.Types.ObjectId(models[i])})
|
const model = await ModelModel.findOne({'models._id': mongoose.Types.ObjectId(models[i])})
|
||||||
.lean().exec().catch(err => next(err)) as any;
|
.lean().exec().catch(err => next(err)) as any;
|
||||||
if(!model) {
|
if(!model) {
|
||||||
res.status(400).json({status: 'Invalid model id'});
|
res.status(400).json({status: 'Invalid model id'});
|
||||||
result = false;
|
result = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
@ -2,33 +2,33 @@ import Joi from 'joi';
|
|||||||
import globals from '../../globals';
|
import globals from '../../globals';
|
||||||
|
|
||||||
export default class HelpValidate {
|
export default class HelpValidate {
|
||||||
private static help = {
|
private static help = {
|
||||||
text: Joi.string()
|
text: Joi.string()
|
||||||
.allow('')
|
.allow('')
|
||||||
.max(8192),
|
.max(8192),
|
||||||
|
|
||||||
level: Joi.string()
|
level: Joi.string()
|
||||||
.valid('none', ...Object.values(globals.levels))
|
.valid('none', ...Object.values(globals.levels))
|
||||||
}
|
}
|
||||||
|
|
||||||
static input (data) {
|
static input (data) {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
text: this.help.text.required(),
|
text: this.help.text.required(),
|
||||||
level: this.help.level.required()
|
level: this.help.level.required()
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static output (data) {
|
static output (data) {
|
||||||
const {value, error} = Joi.object({
|
const {value, error} = Joi.object({
|
||||||
text: this.help.text,
|
text: this.help.text,
|
||||||
level: this.help.level
|
level: this.help.level
|
||||||
}).validate(data, {stripUnknown: true});
|
}).validate(data, {stripUnknown: true});
|
||||||
return error !== undefined? null : value;
|
return error !== undefined? null : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static params(data) {
|
static params(data) {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
key: Joi.string().min(1).max(128)
|
key: Joi.string().min(1).max(128)
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,33 +1,33 @@
|
|||||||
import Joi from 'joi';
|
import Joi from 'joi';
|
||||||
|
|
||||||
export default class IdValidate {
|
export default class IdValidate {
|
||||||
private static id = Joi.string()
|
private static id = Joi.string()
|
||||||
.pattern(new RegExp('[0-9a-f]{24}'))
|
.pattern(new RegExp('[0-9a-f]{24}'))
|
||||||
.length(24)
|
.length(24)
|
||||||
.messages({'string.pattern.base': 'Invalid object id'});
|
.messages({'string.pattern.base': 'Invalid object id'});
|
||||||
|
|
||||||
static get () { // return joi validation
|
static get () { // return joi validation
|
||||||
return this.id;
|
return this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static valid (id) { // validate id
|
static valid (id) { // validate id
|
||||||
return this.id.validate(id).error === undefined;
|
return this.id.validate(id).error === undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
static parameter () { // :id url parameter
|
static parameter () { // :id url parameter
|
||||||
return ':id([0-9a-f]{24})';
|
return ':id([0-9a-f]{24})';
|
||||||
}
|
}
|
||||||
|
|
||||||
static stringify (data) { // convert all ObjectID objects to plain strings
|
static stringify (data) { // convert all ObjectID objects to plain strings
|
||||||
Object.keys(data).forEach(key => {
|
Object.keys(data).forEach(key => {
|
||||||
// stringify id
|
// stringify id
|
||||||
if (data[key] !== null && data[key].hasOwnProperty('_bsontype') && data[key]._bsontype === 'ObjectID') {
|
if (data[key] !== null && data[key].hasOwnProperty('_bsontype') && data[key]._bsontype === 'ObjectID') {
|
||||||
data[key] = data[key].toString();
|
data[key] = data[key].toString();
|
||||||
}
|
}
|
||||||
else if (typeof data[key] === 'object' && data[key] !== null) { // deeper into recursion
|
else if (typeof data[key] === 'object' && data[key] !== null) { // deeper into recursion
|
||||||
data[key] = this.stringify(data[key]);
|
data[key] = this.stringify(data[key]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,99 +4,99 @@ import IdValidate from './id';
|
|||||||
import globals from '../../globals';
|
import globals from '../../globals';
|
||||||
|
|
||||||
export default class MaterialValidate { // validate input for material
|
export default class MaterialValidate { // validate input for material
|
||||||
private static material = {
|
private static material = {
|
||||||
name: Joi.string()
|
name: Joi.string()
|
||||||
.max(128),
|
.max(128),
|
||||||
|
|
||||||
supplier: Joi.string()
|
supplier: Joi.string()
|
||||||
.max(128),
|
.max(128),
|
||||||
|
|
||||||
group: Joi.string()
|
group: Joi.string()
|
||||||
.max(128),
|
.max(128),
|
||||||
|
|
||||||
properties: Joi.object(),
|
properties: Joi.object(),
|
||||||
|
|
||||||
numbers: Joi.array()
|
numbers: Joi.array()
|
||||||
.items(
|
.items(
|
||||||
Joi.string()
|
Joi.string()
|
||||||
.max(64)
|
.max(64)
|
||||||
),
|
),
|
||||||
|
|
||||||
status: Joi.string()
|
status: Joi.string()
|
||||||
.valid(...Object.values(globals.status))
|
.valid(...Object.values(globals.status))
|
||||||
};
|
};
|
||||||
|
|
||||||
static input (data, param) { // validate input, set param to 'new' to make all attributes required
|
static input (data, param) { // validate input, set param to 'new' to make all attributes required
|
||||||
if (param === 'new') {
|
if (param === 'new') {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
name: this.material.name.required(),
|
name: this.material.name.required(),
|
||||||
supplier: this.material.supplier.required(),
|
supplier: this.material.supplier.required(),
|
||||||
group: this.material.group.required(),
|
group: this.material.group.required(),
|
||||||
properties: this.material.properties.required(),
|
properties: this.material.properties.required(),
|
||||||
numbers: this.material.numbers.required()
|
numbers: this.material.numbers.required()
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
else if (param === 'change') {
|
else if (param === 'change') {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
name: this.material.name,
|
name: this.material.name,
|
||||||
supplier: this.material.supplier,
|
supplier: this.material.supplier,
|
||||||
group: this.material.group,
|
group: this.material.group,
|
||||||
properties: this.material.properties,
|
properties: this.material.properties,
|
||||||
numbers: this.material.numbers
|
numbers: this.material.numbers
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return{error: 'No parameter specified!', value: {}};
|
return{error: 'No parameter specified!', value: {}};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static output (data, status = false) { // validate output and strip unwanted properties, returns null if not valid
|
static output (data, status = false) { // validate output and strip unwanted properties, returns null if not valid
|
||||||
data = IdValidate.stringify(data);
|
data = IdValidate.stringify(data);
|
||||||
data.group = data.group_id.name;
|
data.group = data.group_id.name;
|
||||||
data.supplier = data.supplier_id.name;
|
data.supplier = data.supplier_id.name;
|
||||||
const validate: any = {
|
const validate: any = {
|
||||||
_id: IdValidate.get(),
|
_id: IdValidate.get(),
|
||||||
name: this.material.name,
|
name: this.material.name,
|
||||||
supplier: this.material.supplier,
|
supplier: this.material.supplier,
|
||||||
group: this.material.group,
|
group: this.material.group,
|
||||||
properties: this.material.properties,
|
properties: this.material.properties,
|
||||||
numbers: this.material.numbers
|
numbers: this.material.numbers
|
||||||
};
|
};
|
||||||
if (status) {
|
if (status) {
|
||||||
validate.status = this.material.status;
|
validate.status = this.material.status;
|
||||||
}
|
}
|
||||||
const {value, error} = Joi.object(validate).validate(data, {stripUnknown: true});
|
const {value, error} = Joi.object(validate).validate(data, {stripUnknown: true});
|
||||||
return error !== undefined? null : value;
|
return error !== undefined? null : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static outputGroups (data) {// validate groups output and strip unwanted properties, returns null if not valid
|
static outputGroups (data) {// validate groups output and strip unwanted properties, returns null if not valid
|
||||||
const {value, error} = this.material.group.validate(data, {stripUnknown: true});
|
const {value, error} = this.material.group.validate(data, {stripUnknown: true});
|
||||||
return error !== undefined? null : value;
|
return error !== undefined? null : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static outputSuppliers (data) {// validate suppliers output and strip unwanted properties, returns null if not valid
|
static outputSuppliers (data) {// validate suppliers output and strip unwanted properties, returns null if not valid
|
||||||
const {value, error} = this.material.supplier.validate(data, {stripUnknown: true});
|
const {value, error} = this.material.supplier.validate(data, {stripUnknown: true});
|
||||||
return error !== undefined? null : value;
|
return error !== undefined? null : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static outputV() { // return output validator
|
static outputV() { // return output validator
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
_id: IdValidate.get(),
|
_id: IdValidate.get(),
|
||||||
name: this.material.name,
|
name: this.material.name,
|
||||||
supplier: this.material.supplier,
|
supplier: this.material.supplier,
|
||||||
group: this.material.group,
|
group: this.material.group,
|
||||||
properties: this.material.properties,
|
properties: this.material.properties,
|
||||||
numbers: this.material.numbers
|
numbers: this.material.numbers
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static query (data, dev = false) {
|
static query (data, dev = false) {
|
||||||
const acceptedStatuses = [globals.status.val, globals.status.new];
|
const acceptedStatuses = [globals.status.val, globals.status.new];
|
||||||
if (dev) { // dev and admin can also access deleted samples
|
if (dev) { // dev and admin can also access deleted samples
|
||||||
acceptedStatuses.push(globals.status.del)
|
acceptedStatuses.push(globals.status.del)
|
||||||
}
|
}
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
status: Joi.array().items(Joi.string().valid(...acceptedStatuses)).default([globals.status.val])
|
status: Joi.array().items(Joi.string().valid(...acceptedStatuses)).default([globals.status.val])
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,64 +4,64 @@ import IdValidate from './id';
|
|||||||
import globals from '../../globals';
|
import globals from '../../globals';
|
||||||
|
|
||||||
export default class MeasurementValidate {
|
export default class MeasurementValidate {
|
||||||
private static measurement = {
|
private static measurement = {
|
||||||
values: Joi.object()
|
values: Joi.object()
|
||||||
.pattern(/.*/, Joi.alternatives()
|
.pattern(/.*/, Joi.alternatives()
|
||||||
.try(
|
.try(
|
||||||
Joi.string().max(128),
|
Joi.string().max(128),
|
||||||
Joi.number(),
|
Joi.number(),
|
||||||
Joi.boolean(),
|
Joi.boolean(),
|
||||||
Joi.array().items(Joi.array().items(Joi.number())), // for spectra
|
Joi.array().items(Joi.array().items(Joi.number())), // for spectra
|
||||||
Joi.array()
|
Joi.array()
|
||||||
)
|
)
|
||||||
.allow(null)
|
.allow(null)
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
static input (data, param) { // validate input, set param to 'new' to make all attributes required
|
static input (data, param) { // validate input, set param to 'new' to make all attributes required
|
||||||
if (param === 'new') {
|
if (param === 'new') {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
sample_id: IdValidate.get().required(),
|
sample_id: IdValidate.get().required(),
|
||||||
values: this.measurement.values.required(),
|
values: this.measurement.values.required(),
|
||||||
measurement_template: IdValidate.get().required()
|
measurement_template: IdValidate.get().required()
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
else if (param === 'change') {
|
else if (param === 'change') {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
values: this.measurement.values
|
values: this.measurement.values
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return{error: 'No parameter specified!', value: {}};
|
return{error: 'No parameter specified!', value: {}};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate output and strip unwanted properties, returns null if not valid
|
// validate output and strip unwanted properties, returns null if not valid
|
||||||
static output (data, req, status = false) {
|
static output (data, req, status = false) {
|
||||||
data = IdValidate.stringify(data);
|
data = IdValidate.stringify(data);
|
||||||
// spectral data not allowed for read/write users
|
// spectral data not allowed for read/write users
|
||||||
if (['dev', 'admin'].indexOf(req.authDetails.level) < 0 && data.values[globals.spectrum.dpt]) {
|
if (['dev', 'admin'].indexOf(req.authDetails.level) < 0 && data.values[globals.spectrum.dpt]) {
|
||||||
delete data.values[globals.spectrum.dpt];
|
delete data.values[globals.spectrum.dpt];
|
||||||
}
|
}
|
||||||
const validation: any = {
|
const validation: any = {
|
||||||
_id: IdValidate.get(),
|
_id: IdValidate.get(),
|
||||||
sample_id: IdValidate.get(),
|
sample_id: IdValidate.get(),
|
||||||
values: this.measurement.values,
|
values: this.measurement.values,
|
||||||
measurement_template: IdValidate.get()
|
measurement_template: IdValidate.get()
|
||||||
};
|
};
|
||||||
if (status) {
|
if (status) {
|
||||||
validation.status = Joi.string().valid(...Object.values(globals.status));
|
validation.status = Joi.string().valid(...Object.values(globals.status));
|
||||||
}
|
}
|
||||||
const {value, error} = Joi.object(validation).validate(data, {stripUnknown: true});
|
const {value, error} = Joi.object(validation).validate(data, {stripUnknown: true});
|
||||||
return error !== undefined? null : value;
|
return error !== undefined? null : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static outputV() { // return output validator
|
static outputV() { // return output validator
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
_id: IdValidate.get(),
|
_id: IdValidate.get(),
|
||||||
sample_id: IdValidate.get(),
|
sample_id: IdValidate.get(),
|
||||||
values: this.measurement.values,
|
values: this.measurement.values,
|
||||||
measurement_template: IdValidate.get()
|
measurement_template: IdValidate.get()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,40 +3,40 @@ import IdValidate from './id';
|
|||||||
|
|
||||||
|
|
||||||
export default class ModelValidate { // validate input for model
|
export default class ModelValidate { // validate input for model
|
||||||
private static model = {
|
private static model = {
|
||||||
group: Joi.string()
|
group: Joi.string()
|
||||||
.disallow('file')
|
.disallow('file')
|
||||||
.max(128),
|
.max(128),
|
||||||
|
|
||||||
model: Joi.object({
|
model: Joi.object({
|
||||||
name: Joi.string()
|
name: Joi.string()
|
||||||
.max(128)
|
.max(128)
|
||||||
.required(),
|
.required(),
|
||||||
|
|
||||||
url: Joi.string()
|
url: Joi.string()
|
||||||
.uri()
|
.uri()
|
||||||
.max(512)
|
.max(512)
|
||||||
.required()
|
.required()
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
static input (data) { // validate input
|
static input (data) { // validate input
|
||||||
return this.model.model.required().validate(data);
|
return this.model.model.required().validate(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static output (data) { // validate output and strip unwanted properties, returns null if not valid
|
static output (data) { // validate output and strip unwanted properties, returns null if not valid
|
||||||
data = IdValidate.stringify(data);
|
data = IdValidate.stringify(data);
|
||||||
const {value, error} = Joi.object({
|
const {value, error} = Joi.object({
|
||||||
group: this.model.group,
|
group: this.model.group,
|
||||||
models: Joi.array().items(this.model.model.append({_id: IdValidate.get()}))
|
models: Joi.array().items(this.model.model.append({_id: IdValidate.get()}))
|
||||||
}).validate(data, {stripUnknown: true});
|
}).validate(data, {stripUnknown: true});
|
||||||
return error !== undefined? null : value;
|
return error !== undefined? null : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fileOutput (data) {
|
static fileOutput (data) {
|
||||||
return {
|
return {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
size: data.data.length
|
size: data.data.length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,18 +1,18 @@
|
|||||||
import Joi from 'joi';
|
import Joi from 'joi';
|
||||||
|
|
||||||
export default class NoteFieldValidate {
|
export default class NoteFieldValidate {
|
||||||
private static note_field = {
|
private static note_field = {
|
||||||
name: Joi.string()
|
name: Joi.string()
|
||||||
.max(128),
|
.max(128),
|
||||||
|
|
||||||
qty: Joi.number()
|
qty: Joi.number()
|
||||||
};
|
};
|
||||||
|
|
||||||
static output (data) { // validate output and strip unwanted properties, returns null if not valid
|
static output (data) { // validate output and strip unwanted properties, returns null if not valid
|
||||||
const {value, error} = Joi.object({
|
const {value, error} = Joi.object({
|
||||||
name: this.note_field.name,
|
name: this.note_field.name,
|
||||||
qty: this.note_field.qty
|
qty: this.note_field.qty
|
||||||
}).validate(data, {stripUnknown: true});
|
}).validate(data, {stripUnknown: true});
|
||||||
return error !== undefined? null : value;
|
return error !== undefined? null : value;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,42 +1,42 @@
|
|||||||
import Joi from 'joi';
|
import Joi from 'joi';
|
||||||
|
|
||||||
export default class ParametersValidate {
|
export default class ParametersValidate {
|
||||||
// data to validate, parameters from template, param: 'new', 'change', 'null'(null values are allowed)
|
// data to validate, parameters from template, param: 'new', 'change', 'null'(null values are allowed)
|
||||||
static input (data, parameters, param) {
|
static input (data, parameters, param) {
|
||||||
let joiObject = {};
|
let joiObject = {};
|
||||||
parameters.forEach(parameter => {
|
parameters.forEach(parameter => {
|
||||||
switch (parameter.range.type) {
|
switch (parameter.range.type) {
|
||||||
case 'number': joiObject[parameter.name] = Joi.number();
|
case 'number': joiObject[parameter.name] = Joi.number();
|
||||||
break;
|
break;
|
||||||
case 'boolean': joiObject[parameter.name] = Joi.boolean();
|
case 'boolean': joiObject[parameter.name] = Joi.boolean();
|
||||||
break;
|
break;
|
||||||
case 'array': joiObject[parameter.name] = Joi.array();
|
case 'array': joiObject[parameter.name] = Joi.array();
|
||||||
break;
|
break;
|
||||||
case 'string': joiObject[parameter.name] = Joi.string().max(128);
|
case 'string': joiObject[parameter.name] = Joi.string().max(128);
|
||||||
break; // min or max implicitly define the value to be a number
|
break; // min or max implicitly define the value to be a number
|
||||||
default: if (parameter.range.hasOwnProperty('min') || parameter.range.hasOwnProperty('max')) {
|
default: if (parameter.range.hasOwnProperty('min') || parameter.range.hasOwnProperty('max')) {
|
||||||
joiObject[parameter.name] = Joi.number();
|
joiObject[parameter.name] = Joi.number();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
joiObject[parameter.name] = Joi.string().max(128);
|
joiObject[parameter.name] = Joi.string().max(128);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (parameter.range.hasOwnProperty('min')) {
|
if (parameter.range.hasOwnProperty('min')) {
|
||||||
joiObject[parameter.name] = joiObject[parameter.name].min(parameter.range.min)
|
joiObject[parameter.name] = joiObject[parameter.name].min(parameter.range.min)
|
||||||
}
|
}
|
||||||
if (parameter.range.hasOwnProperty('max')) {
|
if (parameter.range.hasOwnProperty('max')) {
|
||||||
joiObject[parameter.name] = joiObject[parameter.name].max(parameter.range.max)
|
joiObject[parameter.name] = joiObject[parameter.name].max(parameter.range.max)
|
||||||
}
|
}
|
||||||
if (parameter.range.hasOwnProperty('values')) {
|
if (parameter.range.hasOwnProperty('values')) {
|
||||||
joiObject[parameter.name] = joiObject[parameter.name].valid(...parameter.range.values);
|
joiObject[parameter.name] = joiObject[parameter.name].valid(...parameter.range.values);
|
||||||
}
|
}
|
||||||
if (parameter.range.hasOwnProperty('required') && parameter.range.required) {
|
if (parameter.range.hasOwnProperty('required') && parameter.range.required) {
|
||||||
joiObject[parameter.name] = joiObject[parameter.name].required();
|
joiObject[parameter.name] = joiObject[parameter.name].required();
|
||||||
}
|
}
|
||||||
if (param === 'null') {
|
if (param === 'null') {
|
||||||
joiObject[parameter.name] = joiObject[parameter.name].allow(null)
|
joiObject[parameter.name] = joiObject[parameter.name].allow(null)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return Joi.object(joiObject).validate(data);
|
return Joi.object(joiObject).validate(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
// respond with 400 and include error details from the joi validation
|
// respond with 400 and include error details from the joi validation
|
||||||
|
|
||||||
export default function res400 (error, res) {
|
export default function res400 (error, res) {
|
||||||
res.status(400).json({status: 'Invalid body format', details: error.details[0].message});
|
res.status(400).json({status: 'Invalid body format', details: error.details[0].message});
|
||||||
}
|
}
|
@ -2,50 +2,50 @@ import Joi from 'joi';
|
|||||||
import IdValidate from './id';
|
import IdValidate from './id';
|
||||||
|
|
||||||
export default class RootValidate { // validate input for root methods
|
export default class RootValidate { // validate input for root methods
|
||||||
private static changelog = {
|
private static changelog = {
|
||||||
timestamp: Joi.date()
|
timestamp: Joi.date()
|
||||||
.iso()
|
.iso()
|
||||||
.min('1970-01-01T00:00:00.000Z'),
|
.min('1970-01-01T00:00:00.000Z'),
|
||||||
|
|
||||||
page: Joi.number()
|
page: Joi.number()
|
||||||
.integer()
|
.integer()
|
||||||
.min(0)
|
.min(0)
|
||||||
.default(0),
|
.default(0),
|
||||||
|
|
||||||
pagesize: Joi.number()
|
pagesize: Joi.number()
|
||||||
.integer()
|
.integer()
|
||||||
.min(0)
|
.min(0)
|
||||||
.default(25),
|
.default(25),
|
||||||
|
|
||||||
action: Joi.string(),
|
action: Joi.string(),
|
||||||
|
|
||||||
collection: Joi.string(),
|
collection: Joi.string(),
|
||||||
|
|
||||||
conditions: Joi.object(),
|
conditions: Joi.object(),
|
||||||
|
|
||||||
data: Joi.object()
|
data: Joi.object()
|
||||||
};
|
};
|
||||||
|
|
||||||
static changelogParams (data) {
|
static changelogParams (data) {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
id: IdValidate.get(),
|
id: IdValidate.get(),
|
||||||
page: this.changelog.page,
|
page: this.changelog.page,
|
||||||
pagesize: this.changelog.pagesize
|
pagesize: this.changelog.pagesize
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static changelogOutput (data) {
|
static changelogOutput (data) {
|
||||||
data.date = data._id.getTimestamp();
|
data.date = data._id.getTimestamp();
|
||||||
data.collection = data.collection_name;
|
data.collection = data.collection_name;
|
||||||
data = IdValidate.stringify(data);
|
data = IdValidate.stringify(data);
|
||||||
const {value, error} = Joi.object({
|
const {value, error} = Joi.object({
|
||||||
_id: IdValidate.get(),
|
_id: IdValidate.get(),
|
||||||
date: this.changelog.timestamp,
|
date: this.changelog.timestamp,
|
||||||
action: this.changelog.action,
|
action: this.changelog.action,
|
||||||
collection: this.changelog.collection,
|
collection: this.changelog.collection,
|
||||||
conditions: this.changelog.conditions,
|
conditions: this.changelog.conditions,
|
||||||
data: this.changelog.data,
|
data: this.changelog.data,
|
||||||
}).validate(data, {stripUnknown: true});
|
}).validate(data, {stripUnknown: true});
|
||||||
return error !== undefined? null : value;
|
return error !== undefined? null : value;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,273 +8,273 @@ import globals from '../../globals';
|
|||||||
|
|
||||||
|
|
||||||
export default class SampleValidate {
|
export default class SampleValidate {
|
||||||
private static sample = {
|
private static sample = {
|
||||||
number: Joi.string()
|
number: Joi.string()
|
||||||
.max(128),
|
.max(128),
|
||||||
|
|
||||||
color: Joi.string()
|
color: Joi.string()
|
||||||
.max(128)
|
.max(128)
|
||||||
.allow(''),
|
.allow(''),
|
||||||
|
|
||||||
type: Joi.string()
|
type: Joi.string()
|
||||||
.valid('as-delivered/raw', 'processed'),
|
.valid('as-delivered/raw', 'processed'),
|
||||||
|
|
||||||
batch: Joi.string()
|
batch: Joi.string()
|
||||||
.max(128)
|
.max(128)
|
||||||
.allow(''),
|
.allow(''),
|
||||||
|
|
||||||
condition: Joi.object(),
|
condition: Joi.object(),
|
||||||
|
|
||||||
notes: Joi.object({
|
notes: Joi.object({
|
||||||
comment: Joi.string()
|
comment: Joi.string()
|
||||||
.max(512)
|
.max(512)
|
||||||
.allow('')
|
.allow('')
|
||||||
.allow(null),
|
.allow(null),
|
||||||
|
|
||||||
sample_references: Joi.array()
|
sample_references: Joi.array()
|
||||||
.items(Joi.object({
|
.items(Joi.object({
|
||||||
sample_id: IdValidate.get(),
|
sample_id: IdValidate.get(),
|
||||||
|
|
||||||
relation: Joi.string()
|
relation: Joi.string()
|
||||||
.max(128)
|
.max(128)
|
||||||
})),
|
})),
|
||||||
|
|
||||||
custom_fields: Joi.object()
|
custom_fields: Joi.object()
|
||||||
.pattern(/.*/, Joi.alternatives()
|
.pattern(/.*/, Joi.alternatives()
|
||||||
.try(
|
.try(
|
||||||
Joi.string().max(128),
|
Joi.string().max(128),
|
||||||
Joi.number(),
|
Joi.number(),
|
||||||
Joi.boolean(),
|
Joi.boolean(),
|
||||||
Joi.date()
|
Joi.date()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
added: Joi.date()
|
added: Joi.date()
|
||||||
.iso()
|
.iso()
|
||||||
.min('1970-01-01T00:00:00.000Z'),
|
.min('1970-01-01T00:00:00.000Z'),
|
||||||
|
|
||||||
status: Joi.string()
|
status: Joi.string()
|
||||||
.valid(...Object.values(globals.status))
|
.valid(...Object.values(globals.status))
|
||||||
};
|
};
|
||||||
|
|
||||||
static readonly sampleKeys = [ // keys which can be found in the sample directly
|
static readonly sampleKeys = [ // keys which can be found in the sample directly
|
||||||
'_id',
|
'_id',
|
||||||
'color',
|
'color',
|
||||||
'number',
|
'number',
|
||||||
'type',
|
'type',
|
||||||
'batch',
|
'batch',
|
||||||
'added',
|
'added',
|
||||||
'condition',
|
'condition',
|
||||||
'material_id',
|
'material_id',
|
||||||
'note_id',
|
'note_id',
|
||||||
'user_id'
|
'user_id'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static sortKeys = [
|
private static sortKeys = [
|
||||||
'_id',
|
'_id',
|
||||||
'color',
|
'color',
|
||||||
'number',
|
'number',
|
||||||
'type',
|
'type',
|
||||||
'batch',
|
'batch',
|
||||||
'added',
|
'added',
|
||||||
'status',
|
'status',
|
||||||
'notes.comment',
|
'notes.comment',
|
||||||
'material.name',
|
'material.name',
|
||||||
'material.supplier',
|
'material.supplier',
|
||||||
'material.group',
|
'material.group',
|
||||||
'material.properties.*',
|
'material.properties.*',
|
||||||
'condition.*',
|
'condition.*',
|
||||||
`measurements.(?!${globals.spectrum.spectrum}.${globals.spectrum.dpt})*`
|
`measurements.(?!${globals.spectrum.spectrum}.${globals.spectrum.dpt})*`
|
||||||
];
|
];
|
||||||
|
|
||||||
private static fieldKeys = [
|
private static fieldKeys = [
|
||||||
...SampleValidate.sortKeys,
|
...SampleValidate.sortKeys,
|
||||||
'condition',
|
'condition',
|
||||||
'notes',
|
'notes',
|
||||||
'material_id',
|
'material_id',
|
||||||
'material',
|
'material',
|
||||||
'note_id',
|
'note_id',
|
||||||
'user_id',
|
'user_id',
|
||||||
'material._id',
|
'material._id',
|
||||||
'material.numbers',
|
'material.numbers',
|
||||||
'measurements',
|
'measurements',
|
||||||
`measurements.${globals.spectrum.spectrum}.${globals.spectrum.dpt}`,
|
`measurements.${globals.spectrum.spectrum}.${globals.spectrum.dpt}`,
|
||||||
];
|
];
|
||||||
|
|
||||||
static input (data, param) { // validate input, set param to 'new' to make all attributes required
|
static input (data, param) { // validate input, set param to 'new' to make all attributes required
|
||||||
if (param === 'new') {
|
if (param === 'new') {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
color: this.sample.color.required(),
|
color: this.sample.color.required(),
|
||||||
type: this.sample.type.required(),
|
type: this.sample.type.required(),
|
||||||
batch: this.sample.batch.required(),
|
batch: this.sample.batch.required(),
|
||||||
condition: this.sample.condition.required(),
|
condition: this.sample.condition.required(),
|
||||||
material_id: IdValidate.get().required(),
|
material_id: IdValidate.get().required(),
|
||||||
notes: this.sample.notes.required()
|
notes: this.sample.notes.required()
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
else if (param === 'change') {
|
else if (param === 'change') {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
color: this.sample.color,
|
color: this.sample.color,
|
||||||
type: this.sample.type,
|
type: this.sample.type,
|
||||||
batch: this.sample.batch,
|
batch: this.sample.batch,
|
||||||
condition: this.sample.condition,
|
condition: this.sample.condition,
|
||||||
material_id: IdValidate.get(),
|
material_id: IdValidate.get(),
|
||||||
notes: this.sample.notes,
|
notes: this.sample.notes,
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
else if (param === 'new-admin') {
|
else if (param === 'new-admin') {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
number: this.sample.number,
|
number: this.sample.number,
|
||||||
color: this.sample.color.required(),
|
color: this.sample.color.required(),
|
||||||
type: this.sample.type.required(),
|
type: this.sample.type.required(),
|
||||||
batch: this.sample.batch.required(),
|
batch: this.sample.batch.required(),
|
||||||
condition: this.sample.condition.required(),
|
condition: this.sample.condition.required(),
|
||||||
material_id: IdValidate.get().required(),
|
material_id: IdValidate.get().required(),
|
||||||
notes: this.sample.notes.required()
|
notes: this.sample.notes.required()
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return{error: 'No parameter specified!', value: {}};
|
return{error: 'No parameter specified!', value: {}};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate output and strip unwanted properties, returns null if not valid
|
// validate output and strip unwanted properties, returns null if not valid
|
||||||
static output (data, param = 'refs+added', additionalParams = []) {
|
static output (data, param = 'refs+added', additionalParams = []) {
|
||||||
if (param === 'refs+added') {
|
if (param === 'refs+added') {
|
||||||
param = 'refs';
|
param = 'refs';
|
||||||
data.added = data._id.getTimestamp();
|
data.added = data._id.getTimestamp();
|
||||||
}
|
}
|
||||||
data = IdValidate.stringify(data);
|
data = IdValidate.stringify(data);
|
||||||
let joiObject;
|
let joiObject;
|
||||||
if (param === 'refs') {
|
if (param === 'refs') {
|
||||||
joiObject = {
|
joiObject = {
|
||||||
_id: IdValidate.get(),
|
_id: IdValidate.get(),
|
||||||
number: this.sample.number,
|
number: this.sample.number,
|
||||||
color: this.sample.color,
|
color: this.sample.color,
|
||||||
type: this.sample.type,
|
type: this.sample.type,
|
||||||
batch: this.sample.batch,
|
batch: this.sample.batch,
|
||||||
condition: this.sample.condition,
|
condition: this.sample.condition,
|
||||||
material_id: IdValidate.get(),
|
material_id: IdValidate.get(),
|
||||||
material: MaterialValidate.outputV().append({number: Joi.string().max(128).allow('')}),
|
material: MaterialValidate.outputV().append({number: Joi.string().max(128).allow('')}),
|
||||||
note_id: IdValidate.get().allow(null),
|
note_id: IdValidate.get().allow(null),
|
||||||
notes: this.sample.notes,
|
notes: this.sample.notes,
|
||||||
user_id: IdValidate.get(),
|
user_id: IdValidate.get(),
|
||||||
added: this.sample.added,
|
added: this.sample.added,
|
||||||
status: this.sample.status
|
status: this.sample.status
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if(param === 'details') {
|
else if(param === 'details') {
|
||||||
joiObject = {
|
joiObject = {
|
||||||
_id: IdValidate.get(),
|
_id: IdValidate.get(),
|
||||||
number: this.sample.number,
|
number: this.sample.number,
|
||||||
color: this.sample.color,
|
color: this.sample.color,
|
||||||
type: this.sample.type,
|
type: this.sample.type,
|
||||||
batch: this.sample.batch,
|
batch: this.sample.batch,
|
||||||
condition: this.sample.condition,
|
condition: this.sample.condition,
|
||||||
material: MaterialValidate.outputV(),
|
material: MaterialValidate.outputV(),
|
||||||
measurements: Joi.array().items(MeasurementValidate.outputV()),
|
measurements: Joi.array().items(MeasurementValidate.outputV()),
|
||||||
notes: this.sample.notes,
|
notes: this.sample.notes,
|
||||||
user: UserValidate.username(),
|
user: UserValidate.username(),
|
||||||
status: this.sample.status
|
status: this.sample.status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
additionalParams.forEach(param => {
|
additionalParams.forEach(param => {
|
||||||
joiObject[param] = Joi.any();
|
joiObject[param] = Joi.any();
|
||||||
});
|
});
|
||||||
const {value, error} = Joi.object(joiObject).validate(data, {stripUnknown: true});
|
const {value, error} = Joi.object(joiObject).validate(data, {stripUnknown: true});
|
||||||
return error !== undefined? null : value;
|
return error !== undefined? null : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static query (data, dev = false) {
|
static query (data, dev = false) {
|
||||||
if (data.filters && data.filters.length) {
|
if (data.filters && data.filters.length) {
|
||||||
const filterValidation = Joi.array().items(Joi.string()).validate(data.filters);
|
const filterValidation = Joi.array().items(Joi.string()).validate(data.filters);
|
||||||
if (filterValidation.error) return filterValidation;
|
if (filterValidation.error) return filterValidation;
|
||||||
try {
|
try {
|
||||||
for (let i in data.filters) {
|
for (let i in data.filters) {
|
||||||
try {
|
try {
|
||||||
data.filters[i] = decodeURIComponent(data.filters[i]);
|
data.filters[i] = decodeURIComponent(data.filters[i]);
|
||||||
}
|
}
|
||||||
catch (ignore) {}
|
catch (ignore) {}
|
||||||
data.filters[i] = JSON.parse(data.filters[i]);
|
data.filters[i] = JSON.parse(data.filters[i]);
|
||||||
data.filters[i].values = data.filters[i].values.map(e => { // validate filter values
|
data.filters[i].values = data.filters[i].values.map(e => { // validate filter values
|
||||||
if (e === null) { // null values are always allowed
|
if (e === null) { // null values are always allowed
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let validator;
|
let validator;
|
||||||
let field = data.filters[i].field;
|
let field = data.filters[i].field;
|
||||||
if (/material\./.test(field)) { // select right validation model
|
if (/material\./.test(field)) { // select right validation model
|
||||||
validator = MaterialValidate.outputV().append({
|
validator = MaterialValidate.outputV().append({
|
||||||
number: Joi.string().max(128).allow(''),
|
number: Joi.string().max(128).allow(''),
|
||||||
properties: Joi.alternatives().try(Joi.number(), Joi.string().max(128).allow(''))
|
properties: Joi.alternatives().try(Joi.number(), Joi.string().max(128).allow(''))
|
||||||
});
|
});
|
||||||
field = field.replace('material.', '').split('.')[0];
|
field = field.replace('material.', '').split('.')[0];
|
||||||
}
|
}
|
||||||
else if (/measurements\./.test(field) || /condition\./.test(field)) {
|
else if (/measurements\./.test(field) || /condition\./.test(field)) {
|
||||||
validator = Joi.object({
|
validator = Joi.object({
|
||||||
value: Joi.alternatives()
|
value: Joi.alternatives()
|
||||||
.try(
|
.try(
|
||||||
Joi.number(),
|
Joi.number(),
|
||||||
Joi.string().max(128).allow(''),
|
Joi.string().max(128).allow(''),
|
||||||
Joi.boolean(),
|
Joi.boolean(),
|
||||||
Joi.array()
|
Joi.array()
|
||||||
)
|
)
|
||||||
.allow(null)
|
.allow(null)
|
||||||
});
|
});
|
||||||
field = 'value';
|
field = 'value';
|
||||||
}
|
}
|
||||||
else if (field === 'measurements') {
|
else if (field === 'measurements') {
|
||||||
validator = Joi.object({
|
validator = Joi.object({
|
||||||
value: Joi.object({}).allow(null).disallow({})
|
value: Joi.object({}).allow(null).disallow({})
|
||||||
});
|
});
|
||||||
field = 'value';
|
field = 'value';
|
||||||
}
|
}
|
||||||
else if (field === 'notes.comment') {
|
else if (field === 'notes.comment') {
|
||||||
field = 'comment';
|
field = 'comment';
|
||||||
validator = this.sample.notes
|
validator = this.sample.notes
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
validator = Joi.object(this.sample);
|
validator = Joi.object(this.sample);
|
||||||
}
|
}
|
||||||
const {value, error} = validator.validate({[field]: e});
|
const {value, error} = validator.validate({[field]: e});
|
||||||
if (error) throw error; // reject invalid values
|
if (error) throw error; // reject invalid values
|
||||||
return value[field];
|
return value[field];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
return {error: {details: [{message: 'Invalid JSON string for filter parameter'}]}, value: null}
|
return {error: {details: [{message: 'Invalid JSON string for filter parameter'}]}, value: null}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const acceptedStatuses = [globals.status.val, globals.status.new];
|
const acceptedStatuses = [globals.status.val, globals.status.new];
|
||||||
if (dev) { // dev and admin can also access deleted samples
|
if (dev) { // dev and admin can also access deleted samples
|
||||||
acceptedStatuses.push(globals.status.del)
|
acceptedStatuses.push(globals.status.del)
|
||||||
}
|
}
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
status: Joi.array().items(Joi.string().valid(...acceptedStatuses)).default([globals.status.val]),
|
status: Joi.array().items(Joi.string().valid(...acceptedStatuses)).default([globals.status.val]),
|
||||||
'from-id': IdValidate.get(),
|
'from-id': IdValidate.get(),
|
||||||
'to-page': Joi.number().integer(),
|
'to-page': Joi.number().integer(),
|
||||||
'page-size': Joi.number().integer().min(1),
|
'page-size': Joi.number().integer().min(1),
|
||||||
sort: Joi.string().pattern(
|
sort: Joi.string().pattern(
|
||||||
new RegExp('^(' + this.sortKeys.join('|').replace(/\./g, '\\.').replace(/\*/g, '.+') + ')-(asc|desc)$', 'm')
|
new RegExp('^(' + this.sortKeys.join('|').replace(/\./g, '\\.').replace(/\*/g, '.+') + ')-(asc|desc)$', 'm')
|
||||||
).default('_id-asc'),
|
).default('_id-asc'),
|
||||||
output: Joi.string().valid('json', 'flatten', 'csv').default('json'),
|
output: Joi.string().valid('json', 'flatten', 'csv').default('json'),
|
||||||
fields: Joi.array().items(Joi.string().pattern(
|
fields: Joi.array().items(Joi.string().pattern(
|
||||||
new RegExp('^(' + this.fieldKeys.join('|').replace(/\./g, '\\.').replace(/\*/g, '.+') + ')$', 'm')
|
new RegExp('^(' + this.fieldKeys.join('|').replace(/\./g, '\\.').replace(/\*/g, '.+') + ')$', 'm')
|
||||||
)).default(['_id','number','type','batch','material_id','color','condition','note_id','user_id','added'])
|
)).default(['_id','number','type','batch','material_id','color','condition','note_id','user_id','added'])
|
||||||
.messages({'string.pattern.base': 'Invalid field name'}),
|
.messages({'string.pattern.base': 'Invalid field name'}),
|
||||||
filters: Joi.array().items(Joi.object({
|
filters: Joi.array().items(Joi.object({
|
||||||
mode: Joi.string().valid('eq', 'ne', 'lt', 'lte', 'gt', 'gte', 'in', 'nin', 'stringin'),
|
mode: Joi.string().valid('eq', 'ne', 'lt', 'lte', 'gt', 'gte', 'in', 'nin', 'stringin'),
|
||||||
field: Joi.string().pattern(
|
field: Joi.string().pattern(
|
||||||
new RegExp('^(' + this.fieldKeys.join('|').replace(/\./g, '\\.').replace(/\*/g, '.+') + ')$', 'm')
|
new RegExp('^(' + this.fieldKeys.join('|').replace(/\./g, '\\.').replace(/\*/g, '.+') + ')$', 'm')
|
||||||
).messages({'string.pattern.base': 'Invalid filter field name'}),
|
).messages({'string.pattern.base': 'Invalid filter field name'}),
|
||||||
values: Joi.array().items(Joi.alternatives().try(
|
values: Joi.array().items(Joi.alternatives().try(
|
||||||
Joi.string().max(128).allow(''), Joi.number(), Joi.boolean(), Joi.date().iso(), Joi.object(), null
|
Joi.string().max(128).allow(''), Joi.number(), Joi.boolean(), Joi.date().iso(), Joi.object(), null
|
||||||
)).min(1)
|
)).min(1)
|
||||||
})).default([])
|
})).default([])
|
||||||
}).with('to-page', 'page-size').validate(data);
|
}).with('to-page', 'page-size').validate(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,70 +2,70 @@ import Joi from 'joi';
|
|||||||
import IdValidate from './id';
|
import IdValidate from './id';
|
||||||
|
|
||||||
export default class TemplateValidate {
|
export default class TemplateValidate {
|
||||||
private static template = {
|
private static template = {
|
||||||
name: Joi.string()
|
name: Joi.string()
|
||||||
.max(128),
|
.max(128),
|
||||||
|
|
||||||
version: Joi.number()
|
version: Joi.number()
|
||||||
.min(1),
|
.min(1),
|
||||||
|
|
||||||
parameters: Joi.array()
|
parameters: Joi.array()
|
||||||
.items(
|
.items(
|
||||||
Joi.object({
|
Joi.object({
|
||||||
name: Joi.string()
|
name: Joi.string()
|
||||||
.max(128)
|
.max(128)
|
||||||
.invalid('condition_template', 'material_template')
|
.invalid('condition_template', 'material_template')
|
||||||
.pattern(/^[^.]+$/)
|
.pattern(/^[^.]+$/)
|
||||||
.required()
|
.required()
|
||||||
.messages({'string.pattern.base': 'name must not contain a dot'}),
|
.messages({'string.pattern.base': 'name must not contain a dot'}),
|
||||||
|
|
||||||
range: Joi.object({
|
range: Joi.object({
|
||||||
values: Joi.array()
|
values: Joi.array()
|
||||||
.min(1),
|
.min(1),
|
||||||
|
|
||||||
min: Joi.number(),
|
min: Joi.number(),
|
||||||
|
|
||||||
max: Joi.number(),
|
max: Joi.number(),
|
||||||
|
|
||||||
type: Joi.string()
|
type: Joi.string()
|
||||||
.valid('string', 'number', 'boolean', 'array'),
|
.valid('string', 'number', 'boolean', 'array'),
|
||||||
|
|
||||||
required: Joi.boolean()
|
required: Joi.boolean()
|
||||||
})
|
})
|
||||||
.oxor('values', 'min')
|
.oxor('values', 'min')
|
||||||
.oxor('values', 'max')
|
.oxor('values', 'max')
|
||||||
.required()
|
.required()
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
static input (data, param) { // validate input, set param to 'new' to make all attributes required
|
static input (data, param) { // validate input, set param to 'new' to make all attributes required
|
||||||
if (param === 'new') {
|
if (param === 'new') {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
name: this.template.name.required(),
|
name: this.template.name.required(),
|
||||||
parameters: this.template.parameters.required()
|
parameters: this.template.parameters.required()
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
else if (param === 'change') {
|
else if (param === 'change') {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
name: this.template.name,
|
name: this.template.name,
|
||||||
parameters: this.template.parameters
|
parameters: this.template.parameters
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return{error: 'No parameter specified!', value: {}};
|
return{error: 'No parameter specified!', value: {}};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static output (data) { // validate output and strip unwanted properties, returns null if not valid
|
static output (data) { // validate output and strip unwanted properties, returns null if not valid
|
||||||
data = IdValidate.stringify(data);
|
data = IdValidate.stringify(data);
|
||||||
const {value, error} = Joi.object({
|
const {value, error} = Joi.object({
|
||||||
_id: IdValidate.get(),
|
_id: IdValidate.get(),
|
||||||
name: this.template.name,
|
name: this.template.name,
|
||||||
version: this.template.version,
|
version: this.template.version,
|
||||||
first_id: IdValidate.get(),
|
first_id: IdValidate.get(),
|
||||||
parameters: this.template.parameters
|
parameters: this.template.parameters
|
||||||
}).validate(data, {stripUnknown: true});
|
}).validate(data, {stripUnknown: true});
|
||||||
return error !== undefined? null : value;
|
return error !== undefined? null : value;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,104 +4,104 @@ import globals from '../../globals';
|
|||||||
import IdValidate from './id';
|
import IdValidate from './id';
|
||||||
|
|
||||||
export default class UserValidate { // validate input for user
|
export default class UserValidate { // validate input for user
|
||||||
private static user = {
|
private static user = {
|
||||||
name: Joi.string()
|
name: Joi.string()
|
||||||
.lowercase()
|
.lowercase()
|
||||||
.pattern(new RegExp('^[a-z0-9-_.]+$'))
|
.pattern(new RegExp('^[a-z0-9-_.]+$'))
|
||||||
.max(128)
|
.max(128)
|
||||||
.messages({'string.pattern.base': 'name must only contain a-z0-9_.'}),
|
.messages({'string.pattern.base': 'name must only contain a-z0-9_.'}),
|
||||||
|
|
||||||
email: Joi.string()
|
email: Joi.string()
|
||||||
.email({minDomainSegments: 2})
|
.email({minDomainSegments: 2})
|
||||||
.lowercase()
|
.lowercase()
|
||||||
.max(128),
|
.max(128),
|
||||||
|
|
||||||
pass: Joi.string()
|
pass: Joi.string()
|
||||||
.min(8)
|
.min(8)
|
||||||
.max(128),
|
.max(128),
|
||||||
|
|
||||||
level: Joi.string()
|
level: Joi.string()
|
||||||
.valid(...Object.values(globals.levels)),
|
.valid(...Object.values(globals.levels)),
|
||||||
|
|
||||||
location: Joi.string()
|
location: Joi.string()
|
||||||
.alphanum()
|
.alphanum()
|
||||||
.max(128),
|
.max(128),
|
||||||
|
|
||||||
devices: Joi.array()
|
devices: Joi.array()
|
||||||
.items(Joi.string()
|
.items(Joi.string()
|
||||||
.allow('')
|
.allow('')
|
||||||
.max(128)
|
.max(128)
|
||||||
),
|
),
|
||||||
|
|
||||||
models: Joi.array()
|
models: Joi.array()
|
||||||
.items(IdValidate.get()),
|
.items(IdValidate.get()),
|
||||||
|
|
||||||
status: Joi.string()
|
status: Joi.string()
|
||||||
.valid(...Object.values(globals.status))
|
.valid(...Object.values(globals.status))
|
||||||
};
|
};
|
||||||
|
|
||||||
private static specialUsernames: string[] = ['admin', 'user', 'key', 'new', 'passreset']; // names a user cannot take
|
private static specialUsernames: string[] = ['admin', 'user', 'key', 'new', 'passreset']; // names a user cannot take
|
||||||
|
|
||||||
static input (data, param) { // validate input, set param to 'new' to make all attributes required
|
static input (data, param) { // validate input, set param to 'new' to make all attributes required
|
||||||
if (param === 'new') {
|
if (param === 'new') {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
name: this.user.name.required(),
|
name: this.user.name.required(),
|
||||||
email: this.user.email.required(),
|
email: this.user.email.required(),
|
||||||
pass: this.user.pass.required(),
|
pass: this.user.pass.required(),
|
||||||
level: this.user.level.required(),
|
level: this.user.level.required(),
|
||||||
location: this.user.location.required(),
|
location: this.user.location.required(),
|
||||||
devices: this.user.devices.required(),
|
devices: this.user.devices.required(),
|
||||||
models: this.user.models.required()
|
models: this.user.models.required()
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
else if (param === 'change') {
|
else if (param === 'change') {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
name: this.user.name,
|
name: this.user.name,
|
||||||
email: this.user.email,
|
email: this.user.email,
|
||||||
pass: this.user.pass,
|
pass: this.user.pass,
|
||||||
location: this.user.location,
|
location: this.user.location,
|
||||||
devices: this.user.devices
|
devices: this.user.devices
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
else if (param === 'changeadmin') {
|
else if (param === 'changeadmin') {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
name: this.user.name,
|
name: this.user.name,
|
||||||
email: this.user.email,
|
email: this.user.email,
|
||||||
pass: this.user.pass,
|
pass: this.user.pass,
|
||||||
level: this.user.level,
|
level: this.user.level,
|
||||||
location: this.user.location,
|
location: this.user.location,
|
||||||
devices: this.user.devices,
|
devices: this.user.devices,
|
||||||
models: this.user.models
|
models: this.user.models
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return {error: 'No parameter specified!', value: {}};
|
return {error: 'No parameter specified!', value: {}};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static output (data, param = '') { // validate output and strip unwanted properties, returns null if not valid
|
static output (data, param = '') { // validate output and strip unwanted properties, returns null if not valid
|
||||||
data = IdValidate.stringify(data);
|
data = IdValidate.stringify(data);
|
||||||
const validate: {[key: string]: object} = {
|
const validate: {[key: string]: object} = {
|
||||||
_id: IdValidate.get(),
|
_id: IdValidate.get(),
|
||||||
name: this.user.name,
|
name: this.user.name,
|
||||||
email: this.user.email,
|
email: this.user.email,
|
||||||
level: this.user.level,
|
level: this.user.level,
|
||||||
location: this.user.location,
|
location: this.user.location,
|
||||||
devices: this.user.devices,
|
devices: this.user.devices,
|
||||||
models: this.user.models
|
models: this.user.models
|
||||||
}
|
}
|
||||||
if (param === 'admin') {
|
if (param === 'admin') {
|
||||||
validate.status = this.user.status;
|
validate.status = this.user.status;
|
||||||
}
|
}
|
||||||
const {value, error} = Joi.object(validate).validate(data, {stripUnknown: true});
|
const {value, error} = Joi.object(validate).validate(data, {stripUnknown: true});
|
||||||
return error !== undefined? null : value;
|
return error !== undefined? null : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static isSpecialName (name) { // true if name belongs to special names
|
static isSpecialName (name) { // true if name belongs to special names
|
||||||
return this.specialUsernames.indexOf(name) > -1;
|
return this.specialUsernames.indexOf(name) > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static username() {
|
static username() {
|
||||||
return this.user.name;
|
return this.user.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,141 +7,141 @@ import IdValidate from '../routes/validate/id';
|
|||||||
|
|
||||||
|
|
||||||
export default class TestHelper {
|
export default class TestHelper {
|
||||||
public static auth = { // test user credentials
|
public static auth = { // test user credentials
|
||||||
admin: {pass: 'Abc123!#', key: '000000000000000000001003', id: '000000000000000000000003'},
|
admin: {pass: 'Abc123!#', key: '000000000000000000001003', id: '000000000000000000000003'},
|
||||||
janedoe: {pass: 'Xyz890*)', key: '000000000000000000001002', id: '000000000000000000000002'},
|
janedoe: {pass: 'Xyz890*)', key: '000000000000000000001002', id: '000000000000000000000002'},
|
||||||
user: {pass: 'Xyz890*)', key: '000000000000000000001001', id: '000000000000000000000001'},
|
user: {pass: 'Xyz890*)', key: '000000000000000000001001', id: '000000000000000000000001'},
|
||||||
johnnydoe: {pass: 'Xyz890*)', key: '000000000000000000001004', id: '000000000000000000000004'},
|
johnnydoe: {pass: 'Xyz890*)', key: '000000000000000000001004', id: '000000000000000000000004'},
|
||||||
customer: {pass: 'Xyz890*)', key: '000000000000000000001005', id: '000000000000000000000005'}
|
customer: {pass: 'Xyz890*)', key: '000000000000000000001005', id: '000000000000000000000005'}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static res = { // default responses
|
public static res = { // default responses
|
||||||
400: {status: 'Bad request'},
|
400: {status: 'Bad request'},
|
||||||
401: {status: 'Unauthorized'},
|
401: {status: 'Unauthorized'},
|
||||||
403: {status: 'Forbidden'},
|
403: {status: 'Forbidden'},
|
||||||
404: {status: 'Not found'},
|
404: {status: 'Not found'},
|
||||||
500: {status: 'Internal server error'}
|
500: {status: 'Internal server error'}
|
||||||
}
|
}
|
||||||
|
|
||||||
static before (done) {
|
static before (done) {
|
||||||
process.env.port = '2999';
|
process.env.port = '2999';
|
||||||
process.env.NODE_ENV = 'test';
|
process.env.NODE_ENV = 'test';
|
||||||
db.connect('test', done);
|
db.connect('test', done);
|
||||||
}
|
}
|
||||||
|
|
||||||
static beforeEach (server, done) {
|
static beforeEach (server, done) {
|
||||||
// delete cached server code except models as these are needed in the testing files as well
|
// delete cached server code except models as these are needed in the testing files as well
|
||||||
Object.keys(require.cache).filter(e => /API\\dist\\(?!(models|db|test))/.test(e)).forEach(key => {
|
Object.keys(require.cache).filter(e => /API\\dist\\(?!(models|db|test))/.test(e)).forEach(key => {
|
||||||
delete require.cache[key]; // prevent loading from cache
|
delete require.cache[key]; // prevent loading from cache
|
||||||
});
|
});
|
||||||
server = require('../index');
|
server = require('../index');
|
||||||
db.drop(err => { // reset database
|
db.drop(err => { // reset database
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
db.loadJson(require('./db.json'), done);
|
db.loadJson(require('./db.json'), done);
|
||||||
});
|
});
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
// options in form: {method, url, contentType, auth: {key/basic: 'name' or 'key'/{name, pass}}, httpStatus, req, res,
|
// options in form: {method, url, contentType, auth: {key/basic: 'name' or 'key'/{name, pass}}, httpStatus, req, res,
|
||||||
// default (set to false if you want to dismiss default .end handling)}
|
// default (set to false if you want to dismiss default .end handling)}
|
||||||
static request (server, done, options) {
|
static request (server, done, options) {
|
||||||
let st = supertest(server);
|
let st = supertest(server);
|
||||||
if (options.hasOwnProperty('auth') && options.auth.hasOwnProperty('key')) { // resolve API key
|
if (options.hasOwnProperty('auth') && options.auth.hasOwnProperty('key')) { // resolve API key
|
||||||
options.url += '?key=' +
|
options.url += '?key=' +
|
||||||
(this.auth.hasOwnProperty(options.auth.key)? this.auth[options.auth.key].key : options.auth.key);
|
(this.auth.hasOwnProperty(options.auth.key)? this.auth[options.auth.key].key : options.auth.key);
|
||||||
}
|
}
|
||||||
switch (options.method) { // http method
|
switch (options.method) { // http method
|
||||||
case 'get':
|
case 'get':
|
||||||
st = st.get(options.url)
|
st = st.get(options.url)
|
||||||
break;
|
break;
|
||||||
case 'post':
|
case 'post':
|
||||||
st = st.post(options.url)
|
st = st.post(options.url)
|
||||||
break;
|
break;
|
||||||
case 'put':
|
case 'put':
|
||||||
st = st.put(options.url)
|
st = st.put(options.url)
|
||||||
break;
|
break;
|
||||||
case 'delete':
|
case 'delete':
|
||||||
st = st.delete(options.url)
|
st = st.delete(options.url)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (options.hasOwnProperty('reqType')) { // request body
|
if (options.hasOwnProperty('reqType')) { // request body
|
||||||
st = st.type(options.reqType);
|
st = st.type(options.reqType);
|
||||||
}
|
}
|
||||||
if (options.hasOwnProperty('req')) { // request body
|
if (options.hasOwnProperty('req')) { // request body
|
||||||
st = st.send(options.req);
|
st = st.send(options.req);
|
||||||
}
|
}
|
||||||
if (options.hasOwnProperty('reqContentType')) { // request body
|
if (options.hasOwnProperty('reqContentType')) { // request body
|
||||||
st = st.set('Content-Type', options.reqContentType);
|
st = st.set('Content-Type', options.reqContentType);
|
||||||
}
|
}
|
||||||
if (options.hasOwnProperty('auth') && options.auth.hasOwnProperty('basic')) { // resolve basic auth
|
if (options.hasOwnProperty('auth') && options.auth.hasOwnProperty('basic')) { // resolve basic auth
|
||||||
if (this.auth.hasOwnProperty(options.auth.basic)) {
|
if (this.auth.hasOwnProperty(options.auth.basic)) {
|
||||||
st = st.auth(options.auth.basic, this.auth[options.auth.basic].pass)
|
st = st.auth(options.auth.basic, this.auth[options.auth.basic].pass)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
st = st.auth(options.auth.basic.name, options.auth.basic.pass)
|
st = st.auth(options.auth.basic.name, options.auth.basic.pass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (options.hasOwnProperty('contentType')) {
|
if (options.hasOwnProperty('contentType')) {
|
||||||
st = st.expect('Content-type', options.contentType).expect(options.httpStatus);
|
st = st.expect('Content-type', options.contentType).expect(options.httpStatus);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
st = st.expect('Content-type', /json/).expect(options.httpStatus);
|
st = st.expect('Content-type', /json/).expect(options.httpStatus);
|
||||||
}
|
}
|
||||||
if (options.hasOwnProperty('res')) { // evaluate result
|
if (options.hasOwnProperty('res')) { // evaluate result
|
||||||
return st.end((err, res) => {
|
return st.end((err, res) => {
|
||||||
if (err) return done (err);
|
if (err) return done (err);
|
||||||
should(res.body).be.eql(options.res);
|
should(res.body).be.eql(options.res);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (this.res.hasOwnProperty(options.httpStatus) && options.default !== false) { // evaluate default results
|
else if (this.res.hasOwnProperty(options.httpStatus) && options.default !== false) { // evaluate default results
|
||||||
return st.end((err, res) => {
|
return st.end((err, res) => {
|
||||||
if (err) return done (err);
|
if (err) return done (err);
|
||||||
should(res.body).be.eql(this.res[options.httpStatus]);
|
should(res.body).be.eql(this.res[options.httpStatus]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// check changelog, takes log: {collection, skip, data/(dataAdd, dataIgn)}
|
// check changelog, takes log: {collection, skip, data/(dataAdd, dataIgn)}
|
||||||
else if (options.hasOwnProperty('log')) {
|
else if (options.hasOwnProperty('log')) {
|
||||||
return st.end(err => {
|
return st.end(err => {
|
||||||
if (err) return done (err);
|
if (err) return done (err);
|
||||||
ChangelogModel.findOne({}).sort({_id: -1}).skip(options.log.skip? options.log.skip : 0)
|
ChangelogModel.findOne({}).sort({_id: -1}).skip(options.log.skip? options.log.skip : 0)
|
||||||
.lean().exec((err, data) => { // latest entry
|
.lean().exec((err, data) => { // latest entry
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
should(data).have.only.keys('_id', 'action', 'collection_name', 'conditions', 'data', 'user_id', '__v');
|
should(data).have.only.keys('_id', 'action', 'collection_name', 'conditions', 'data', 'user_id', '__v');
|
||||||
should(data).have.property('action', options.method.toUpperCase() + ' ' + options.url);
|
should(data).have.property('action', options.method.toUpperCase() + ' ' + options.url);
|
||||||
should(data).have.property('collection_name', options.log.collection);
|
should(data).have.property('collection_name', options.log.collection);
|
||||||
if (options.log.hasOwnProperty('data')) {
|
if (options.log.hasOwnProperty('data')) {
|
||||||
should(data).have.property('data', options.log.data);
|
should(data).have.property('data', options.log.data);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const ignore = ['_id', '__v'];
|
const ignore = ['_id', '__v'];
|
||||||
if (options.log.hasOwnProperty('dataIgn')) {
|
if (options.log.hasOwnProperty('dataIgn')) {
|
||||||
ignore.push(...options.log.dataIgn);
|
ignore.push(...options.log.dataIgn);
|
||||||
}
|
}
|
||||||
let tmp = options.req ? options.req : {};
|
let tmp = options.req ? options.req : {};
|
||||||
if (options.log.hasOwnProperty('dataAdd')) {
|
if (options.log.hasOwnProperty('dataAdd')) {
|
||||||
_.assign(tmp, options.log.dataAdd)
|
_.assign(tmp, options.log.dataAdd)
|
||||||
}
|
}
|
||||||
should(IdValidate.stringify(_.omit(data.data, ignore))).be.eql(_.omit(tmp, ignore));
|
should(IdValidate.stringify(_.omit(data.data, ignore))).be.eql(_.omit(tmp, ignore));
|
||||||
}
|
}
|
||||||
if (data.user_id) {
|
if (data.user_id) {
|
||||||
should(data.user_id.toString()).be.eql(this.auth[options.auth.basic].id);
|
should(data.user_id.toString()).be.eql(this.auth[options.auth.basic].id);
|
||||||
}
|
}
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else { // return object to do .end() manually
|
else { // return object to do .end() manually
|
||||||
return st;
|
return st;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static afterEach (server, done) {
|
static afterEach (server, done) {
|
||||||
server.close(done);
|
server.close(done);
|
||||||
}
|
}
|
||||||
|
|
||||||
static after(done) {
|
static after(done) {
|
||||||
db.disconnect(done);
|
db.disconnect(done);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,12 +3,12 @@ import db from '../db';
|
|||||||
// script to load test db into dev db for a clean start
|
// script to load test db into dev db for a clean start
|
||||||
|
|
||||||
db.connect('dev', () => {
|
db.connect('dev', () => {
|
||||||
console.info('dropping data...');
|
console.info('dropping data...');
|
||||||
db.drop(() => { // reset database
|
db.drop(() => { // reset database
|
||||||
console.info('loading data...');
|
console.info('loading data...');
|
||||||
db.loadJson(require('./db.json'), () => {
|
db.loadJson(require('./db.json'), () => {
|
||||||
console.info('done');
|
console.info('done');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user