diff --git a/package-lock.json b/package-lock.json
index c72166e..2372f4d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
- "name": "dfop-api",
- "version": "1.0.0",
+ "name": "definma-api",
+ "version": "0.9.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/src/customTypes/express.ts b/src/customTypes/express.ts
index ce89d49..1e5a5cc 100644
--- a/src/customTypes/express.ts
+++ b/src/customTypes/express.ts
@@ -9,10 +9,10 @@
/* =================== USAGE ===================
- import * as express from "express";
- let app = express();
+ import * as express from "express";
+ let app = express();
- =============================================== */
+ =============================================== */
///
///
@@ -28,96 +28,96 @@ import * as qs from "qs";
declare function e(): core.Express;
declare namespace e {
- /**
- * This is a built-in middleware function in Express. It parses incoming requests with JSON payloads and is based on
- * body-parser.
- * @since 4.16.0
- */
- let json: typeof bodyParser.json;
+ /**
+ * This is a built-in middleware function in Express. It parses incoming requests with JSON payloads and is based on
+ * body-parser.
+ * @since 4.16.0
+ */
+ let json: typeof bodyParser.json;
- /**
- * This is a built-in middleware function in Express. It parses incoming requests with Buffer payloads and is based
- * on body-parser.
- * @since 4.17.0
- */
- let raw: typeof bodyParser.raw;
+ /**
+ * This is a built-in middleware function in Express. It parses incoming requests with Buffer payloads and is based
+ * on body-parser.
+ * @since 4.17.0
+ */
+ 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
- * body-parser.
- * @since 4.17.0
- */
- let text: typeof bodyParser.text;
+ /**
+ * This is a built-in middleware function in Express. It parses incoming requests with text payloads and is based on
+ * body-parser.
+ * @since 4.17.0
+ */
+ let text: typeof bodyParser.text;
- /**
- * These are the exposed prototypes.
- */
- let application: Application;
- let request: Request;
- let response: Response;
+ /**
+ * These are the exposed prototypes.
+ */
+ let application: Application;
+ let request: Request;
+ let response: Response;
- /**
- * This is a built-in middleware function in Express. It serves static files and is based on serve-static.
- */
- let static: typeof serveStatic;
+ /**
+ * This is a built-in middleware function in Express. It serves static files and is based on serve-static.
+ */
+ let static: typeof serveStatic;
- /**
- * This is a built-in middleware function in Express. It parses incoming requests with urlencoded payloads and is
- * based on body-parser.
- * @since 4.16.0
- */
- let urlencoded: typeof bodyParser.urlencoded;
+ /**
+ * This is a built-in middleware function in Express. It parses incoming requests with urlencoded payloads and is
+ * based on body-parser.
+ * @since 4.16.0
+ */
+ let urlencoded: typeof bodyParser.urlencoded;
- /**
- * 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;
+ /**
+ * 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 Router(options?: RouterOptions): core.Router;
+ export function Router(options?: RouterOptions): core.Router;
- interface RouterOptions {
- /**
- * Enable case sensitivity.
- */
- caseSensitive?: boolean;
+ interface RouterOptions {
+ /**
+ * Enable case sensitivity.
+ */
+ caseSensitive?: boolean;
- /**
- * 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.
- *
- * @default false
- * @since 4.5.0
- */
- mergeParams?: boolean;
+ /**
+ * 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.
+ *
+ * @default false
+ * @since 4.5.0
+ */
+ mergeParams?: boolean;
- /**
- * Enable strict routing.
- */
- strict?: boolean;
- }
+ /**
+ * Enable strict routing.
+ */
+ strict?: boolean;
+ }
- interface Application extends core.Application { }
- interface CookieOptions extends core.CookieOptions { }
- interface Errback extends core.Errback { }
- interface ErrorRequestHandler
- extends core.ErrorRequestHandler
{ }
- interface Express extends core.Express { }
- interface Handler extends core.Handler { }
- interface IRoute extends core.IRoute { }
- interface IRouter extends core.IRouter { }
- interface IRouterHandler extends core.IRouterHandler { }
- interface IRouterMatcher extends core.IRouterMatcher { }
- interface MediaType extends core.MediaType { }
- interface NextFunction extends core.NextFunction { }
- interface Request extends core.Request
{ }
- interface RequestHandler
extends core.RequestHandler
{ }
- interface RequestParamHandler extends core.RequestParamHandler { }
- export interface Response extends core.Response { }
- interface Router extends core.Router { }
- interface Send extends core.Send { }
+ interface Application extends core.Application { }
+ interface CookieOptions extends core.CookieOptions { }
+ interface Errback extends core.Errback { }
+ interface ErrorRequestHandler
+ extends core.ErrorRequestHandler
{ }
+ interface Express extends core.Express { }
+ interface Handler extends core.Handler { }
+ interface IRoute extends core.IRoute { }
+ interface IRouter extends core.IRouter { }
+ interface IRouterHandler extends core.IRouterHandler { }
+ interface IRouterMatcher extends core.IRouterMatcher { }
+ interface MediaType extends core.MediaType { }
+ interface NextFunction extends core.NextFunction { }
+ interface Request extends core.Request
{ }
+ interface RequestHandler
extends core.RequestHandler
{ }
+ interface RequestParamHandler extends core.RequestParamHandler { }
+ export interface Response extends core.Response { }
+ interface Router extends core.Router { }
+ interface Send extends core.Send { }
}
export = e;
diff --git a/src/helpers/authorize.ts b/src/helpers/authorize.ts
index 49b9278..82573c8 100644
--- a/src/helpers/authorize.ts
+++ b/src/helpers/authorize.ts
@@ -9,111 +9,111 @@ import globals from '../globals';
// req.authDetails returns eg. {methods: ['basic'], username: 'johndoe', level: 'write'}
module.exports = async (req, res, next) => {
- let givenMethod = ''; // authorization method given by client, basic taken preferred
- let user = {name: '', level: '', id: '', location: '', models: []}; // user object
+ let givenMethod = ''; // authorization method given by client, basic taken preferred
+ let user = {name: '', level: '', id: '', location: '', models: []}; // user object
- // test authentications
- const userBasic = await basic(req, next);
+ // test authentications
+ const userBasic = await basic(req, next);
- if (userBasic) { // basic available
- givenMethod = 'basic';
- user = userBasic;
- }
- else { // if basic not available, test key
- const userKey = await key(req, next);
- if (userKey) {
- givenMethod = 'key';
- user = userKey;
- }
- }
+ if (userBasic) { // basic available
+ givenMethod = 'basic';
+ user = userBasic;
+ }
+ else { // if basic not available, test key
+ const userKey = await key(req, next);
+ if (userKey) {
+ givenMethod = 'key';
+ user = userKey;
+ }
+ }
- req.auth = (res, levels, method = 'all') => {
- if (givenMethod === method || (method === 'all' && givenMethod !== '')) { // method is available
- if (levels.indexOf(user.level) > -1) { // level is available
- return true;
- }
- else {
- res.status(403).json({status: 'Forbidden'});
- return false;
- }
- }
- else {
- res.status(401).json({status: 'Unauthorized'});
- return false;
- }
- }
+ req.auth = (res, levels, method = 'all') => {
+ if (givenMethod === method || (method === 'all' && givenMethod !== '')) { // method is available
+ if (levels.indexOf(user.level) > -1) { // level is available
+ return true;
+ }
+ else {
+ res.status(403).json({status: 'Forbidden'});
+ return false;
+ }
+ }
+ else {
+ res.status(401).json({status: 'Unauthorized'});
+ return false;
+ }
+ }
- req.authDetails = {
- method: givenMethod,
- username: user.name,
- level: user.level,
- id: user.id,
- location: user.location,
- models: user.models
- };
+ req.authDetails = {
+ method: givenMethod,
+ username: user.name,
+ level: user.level,
+ id: user.id,
+ location: user.location,
+ models: user.models
+ };
- next();
+ next();
}
function basic (req, next): any { // checks basic auth and returns changed user object
- return new Promise(resolve => {
- const auth = basicAuth(req);
- if (auth !== undefined) { // basic auth available
- UserModel.find({name: auth.name, status: globals.status.new}).lean().exec( (err, data: any) => { // find user
- if (err) return next(err);
- if (data.length === 1) { // one user found
- bcrypt.compare(auth.pass, data[0].pass, (err, res) => { // check password
- if (err) return next(err);
- if (res === true) { // password correct
- resolve({
- level: Object.entries(globals.levels).find(e => e[1] === data[0].level)[0],
- name: data[0].name,
- id: data[0]._id.toString(),
- location: data[0].location,
- models: data[0].models
- });
- }
- else {
- resolve(null);
- }
- });
- }
- else {
- resolve(null);
- }
- });
- }
- else {
- resolve(null);
- }
- });
+ return new Promise(resolve => {
+ const auth = basicAuth(req);
+ if (auth !== undefined) { // basic auth available
+ UserModel.find({name: auth.name, status: globals.status.new}).lean().exec( (err, data: any) => { // find user
+ if (err) return next(err);
+ if (data.length === 1) { // one user found
+ bcrypt.compare(auth.pass, data[0].pass, (err, res) => { // check password
+ if (err) return next(err);
+ if (res === true) { // password correct
+ resolve({
+ level: Object.entries(globals.levels).find(e => e[1] === data[0].level)[0],
+ name: data[0].name,
+ id: data[0]._id.toString(),
+ location: data[0].location,
+ models: data[0].models
+ });
+ }
+ else {
+ resolve(null);
+ }
+ });
+ }
+ else {
+ resolve(null);
+ }
+ });
+ }
+ else {
+ resolve(null);
+ }
+ });
}
function key (req, next): any { // checks API key and returns changed user object
- return new Promise(resolve => {
- if (req.query.key !== undefined) { // key available
- UserModel.find({key: req.query.key, status: globals.status.new}).lean().exec( (err, data: any) => { // find user
- if (err) return next(err);
- if (data.length === 1) { // one user found
- resolve({
- level: Object.entries(globals.levels).find(e => e[1] === data[0].level)[0],
- name: data[0].name,
- id: data[0]._id.toString(),
- location: data[0].location,
- models: data[0].models
- });
- if (!/^\/api/m.test(req.url)){
- delete req.query.key; // delete query parameter to avoid interference with later validation
- }
- }
- else {
- resolve(null);
- }
- });
- }
- else {
- resolve(null);
- }
- });
-}
\ No newline at end of file
+ return new Promise(resolve => {
+ if (req.query.key !== undefined) { // key available
+ UserModel.find({key: req.query.key, status: globals.status.new}).lean().exec( (err, data: any) => { // find user
+ if (err) return next(err);
+ if (data.length === 1) { // one user found
+ resolve({
+ level: Object.entries(globals.levels).find(e => e[1] === data[0].level)[0],
+ name: data[0].name,
+ id: data[0]._id.toString(),
+ location: data[0].location,
+ models: data[0].models
+ });
+ if (!/^\/api/m.test(req.url)){
+ delete req.query.key; // delete query parameter to avoid interference with later validation
+ }
+ }
+ else {
+ resolve(null);
+ }
+ });
+ }
+ else {
+ resolve(null);
+ }
+ });
+}
diff --git a/src/helpers/csv.ts b/src/helpers/csv.ts
index 5e8ae03..d5b16ab 100644
--- a/src/helpers/csv.ts
+++ b/src/helpers/csv.ts
@@ -2,8 +2,8 @@ import {parseAsync} from 'json2csv';
import flatten from './flatten';
export default function csv(input: any[], f: (err, data) => void) { // parse JSON to CSV
- parseAsync(input.map(e => flatten(e)), {includeEmptyRows: true})
- .then(csv => f(null, csv))
- .catch(err => f(err, null));
+ parseAsync(input.map(e => flatten(e)), {includeEmptyRows: true})
+ .then(csv => f(null, csv))
+ .catch(err => f(err, null));
}
diff --git a/src/helpers/flatten.ts b/src/helpers/flatten.ts
index d28bd64..54c07c8 100644
--- a/src/helpers/flatten.ts
+++ b/src/helpers/flatten.ts
@@ -1,42 +1,42 @@
import globals from '../globals';
export default function flatten (data, keepArray = false) { // flatten object: {a: {b: true}} -> {a.b: true}
- const result = {};
- function recurse (cur, prop) {
- if (Object(cur) !== cur || Object.keys(cur).length === 0) { // simple value
- result[prop] = cur;
- }
- else if (prop === `${globals.spectrum.spectrum}.${globals.spectrum.dpt}`) { // convert spectrum for ML
- result[prop + '.labels'] = cur.map(e => parseFloat(e[0]));
- result[prop + '.values'] = cur.map(e => parseFloat(e[1]));
- }
- else if (Array.isArray(cur)) {
- if (keepArray) {
- result[prop] = cur;
- }
- else { // array to string
- if (cur.length && (Object(cur[0]) !== cur || Object.keys(cur[0]).length === 0)) { // array of non-objects
- result[prop] = '[' + cur.join(', ') + ']';
- }
- else {
- let l = 0;
- for(let i = 0, l = cur.length; i < l; i++)
- recurse(cur[i], prop + "[" + i + "]");
- if (l == 0)
- result[prop] = [];
- }
- }
- }
- else { // object
- let isEmpty = true;
- for (let p in cur) {
- isEmpty = false;
- recurse(cur[p], prop ? prop+"."+p : p);
- }
- if (isEmpty && prop)
- result[prop] = {};
- }
- }
- recurse(data, '');
- return result;
-}
\ No newline at end of file
+ const result = {};
+ function recurse (cur, prop) {
+ if (Object(cur) !== cur || Object.keys(cur).length === 0) { // simple value
+ result[prop] = cur;
+ }
+ else if (prop === `${globals.spectrum.spectrum}.${globals.spectrum.dpt}`) { // convert spectrum for ML
+ result[prop + '.labels'] = cur.map(e => parseFloat(e[0]));
+ result[prop + '.values'] = cur.map(e => parseFloat(e[1]));
+ }
+ else if (Array.isArray(cur)) {
+ if (keepArray) {
+ result[prop] = cur;
+ }
+ else { // array to string
+ if (cur.length && (Object(cur[0]) !== cur || Object.keys(cur[0]).length === 0)) { // array of non-objects
+ result[prop] = '[' + cur.join(', ') + ']';
+ }
+ else {
+ let l = 0;
+ for(let i = 0, l = cur.length; i < l; i++)
+ recurse(cur[i], prop + "[" + i + "]");
+ if (l == 0)
+ result[prop] = [];
+ }
+ }
+ }
+ else { // object
+ let isEmpty = true;
+ for (let p in cur) {
+ isEmpty = false;
+ recurse(cur[p], prop ? prop+"."+p : p);
+ }
+ if (isEmpty && prop)
+ result[prop] = {};
+ }
+ }
+ recurse(data, '');
+ return result;
+}
diff --git a/src/helpers/mail.ts b/src/helpers/mail.ts
index 40326c3..1fdf01e 100644
--- a/src/helpers/mail.ts
+++ b/src/helpers/mail.ts
@@ -4,86 +4,86 @@ import axios from 'axios';
export default class Mail{
- static readonly address = 'definma@bosch-iot.com'; // email address
- static uri: string; // mail API URI
- static auth = {username: '', password: ''}; // mail API credentials
- static mailPass: string; // mail API generates password
+ static readonly address = 'definma@bosch-iot.com'; // email address
+ static uri: string; // mail API URI
+ static auth = {username: '', password: ''}; // mail API credentials
+ static mailPass: string; // mail API generates password
- static init() {
- 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.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.password = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.password;
- axios({ // get registered mail addresses
- method: 'get',
- url: this.uri + '/management/userDomainMapping',
- auth: this.auth
- }).then(res => {
- return new Promise(async (resolve, reject) => {
- try {
- if (res.data.addresses.indexOf(this.address) < 0) { // mail address not registered
- if (res.data.addresses.length) { // delete wrong registered mail address
- await axios({
- method: 'delete',
- url: this.uri + '/management/mailAddresses/' + res.data.addresses[0],
- auth: this.auth
- });
- }
- await axios({ // register right mail address
- method: 'post',
- url: this.uri + '/management/mailAddresses/' + this.address,
- auth: this.auth
- });
- }
- resolve();
- }
- catch (e) {
- reject(e);
- }
- });
- }).then(() => {
- return axios({ // set new mail password
- method: 'put',
- url: this.uri + '/management/mailAddresses/' + this.address + '/password/' + this.mailPass,
- auth: this.auth
- });
- }).then(() => { // init done successfully
- console.info('Mail service established successfully');
- }).catch(err => { // somewhere an error occurred
- console.error(`Mail init error: ${err.request.method} ${err.request.path}: ${err.response.status}`,
- err.response.data);
- });
- }
- }
+ static init() {
+ 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.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.password = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.password;
+ axios({ // get registered mail addresses
+ method: 'get',
+ url: this.uri + '/management/userDomainMapping',
+ auth: this.auth
+ }).then(res => {
+ return new Promise(async (resolve, reject) => {
+ try {
+ if (res.data.addresses.indexOf(this.address) < 0) { // mail address not registered
+ if (res.data.addresses.length) { // delete wrong registered mail address
+ await axios({
+ method: 'delete',
+ url: this.uri + '/management/mailAddresses/' + res.data.addresses[0],
+ auth: this.auth
+ });
+ }
+ await axios({ // register right mail address
+ method: 'post',
+ url: this.uri + '/management/mailAddresses/' + this.address,
+ auth: this.auth
+ });
+ }
+ resolve();
+ }
+ catch (e) {
+ reject(e);
+ }
+ });
+ }).then(() => {
+ return axios({ // set new mail password
+ method: 'put',
+ url: this.uri + '/management/mailAddresses/' + this.address + '/password/' + this.mailPass,
+ auth: this.auth
+ });
+ }).then(() => { // init done successfully
+ console.info('Mail service established successfully');
+ }).catch(err => { // somewhere an error occurred
+ console.error(`Mail init error: ${err.request.method} ${err.request.path}: ${err.response.status}`,
+ err.response.data);
+ });
+ }
+ }
- 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
- axios({
- method: 'post',
- url: this.uri + '/email',
- auth: this.auth,
- data: {
- recipients: [{to: mailAddress}],
- subject: {content: subject},
- body: {
- content: content,
- contentType: "text/html"
- },
- from: {
- eMail: this.address,
- password: this.mailPass
- }
- }
- }).then(() => {
- f();
- }).catch((err) => {
- f(err);
- });
- }
- else { // dev dummy replacement
- console.info('Sending mail to ' + mailAddress + ': -- ' + subject + ' -- ' + content);
- f();
- }
- }
+ 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
+ axios({
+ method: 'post',
+ url: this.uri + '/email',
+ auth: this.auth,
+ data: {
+ recipients: [{to: mailAddress}],
+ subject: {content: subject},
+ body: {
+ content: content,
+ contentType: "text/html"
+ },
+ from: {
+ eMail: this.address,
+ password: this.mailPass
+ }
+ }
+ }).then(() => {
+ f();
+ }).catch((err) => {
+ f(err);
+ });
+ }
+ else { // dev dummy replacement
+ console.info('Sending mail to ' + mailAddress + ': -- ' + subject + ' -- ' + content);
+ f();
+ }
+ }
}
diff --git a/src/models/changelog.ts b/src/models/changelog.ts
index 57701a9..0a30f32 100644
--- a/src/models/changelog.ts
+++ b/src/models/changelog.ts
@@ -1,11 +1,11 @@
import mongoose from 'mongoose';
const ChangelogSchema = new mongoose.Schema({
- action: String,
- collection_name: String,
- conditions: mongoose.Schema.Types.Mixed,
- data: Object,
- user_id: mongoose.Schema.Types.ObjectId
+ action: String,
+ collection_name: String,
+ conditions: mongoose.Schema.Types.Mixed,
+ data: Object,
+ user_id: mongoose.Schema.Types.ObjectId
}, {minimize: false, strict: false});
-export default mongoose.model>('changelog', ChangelogSchema);
\ No newline at end of file
+export default mongoose.model>('changelog', ChangelogSchema);
diff --git a/src/models/condition_template.ts b/src/models/condition_template.ts
index ca61da2..ab80dfa 100644
--- a/src/models/condition_template.ts
+++ b/src/models/condition_template.ts
@@ -2,19 +2,19 @@ import mongoose from 'mongoose';
import db from '../db';
const ConditionTemplateSchema = new mongoose.Schema({
- first_id: mongoose.Schema.Types.ObjectId,
- name: String,
- version: Number,
- parameters: [new mongoose.Schema({
- name: String,
- range: mongoose.Schema.Types.Mixed
- } ,{ _id : false })]
+ first_id: mongoose.Schema.Types.ObjectId,
+ name: String,
+ version: Number,
+ parameters: [new mongoose.Schema({
+ name: String,
+ range: mongoose.Schema.Types.Mixed
+ } ,{ _id : false })]
}, {minimize: false}); // to allow empty objects
// changelog query helper
ConditionTemplateSchema.query.log = function > (req) {
- db.log(req, this);
- return this;
+ db.log(req, this);
+ return this;
}
-export default mongoose.model>('condition_template', ConditionTemplateSchema);
\ No newline at end of file
+export default mongoose.model>('condition_template', ConditionTemplateSchema);
diff --git a/src/models/help.ts b/src/models/help.ts
index a04ae33..da5ee6d 100644
--- a/src/models/help.ts
+++ b/src/models/help.ts
@@ -2,15 +2,15 @@ import db from '../db';
import mongoose from 'mongoose';
const HelpSchema = new mongoose.Schema({
- key: {type: String, index: {unique: true}},
- level: String,
- text: String
+ key: {type: String, index: {unique: true}},
+ level: String,
+ text: String
}, {minimize: false});
// changelog query helper
HelpSchema.query.log = function > (req) {
- db.log(req, this);
- return this;
+ db.log(req, this);
+ return this;
}
-export default mongoose.model>('help', HelpSchema);
\ No newline at end of file
+export default mongoose.model>('help', HelpSchema);
diff --git a/src/models/material.ts b/src/models/material.ts
index 4790a44..b36b51d 100644
--- a/src/models/material.ts
+++ b/src/models/material.ts
@@ -4,20 +4,20 @@ import MaterialGroupsModel from '../models/material_groups';
import db from '../db';
const MaterialSchema = new mongoose.Schema({
- name: {type: String, index: {unique: true}},
- supplier_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialSupplierModel},
- group_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialGroupsModel},
- properties: mongoose.Schema.Types.Mixed,
- numbers: [String],
- status: String
+ name: {type: String, index: {unique: true}},
+ supplier_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialSupplierModel},
+ group_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialGroupsModel},
+ properties: mongoose.Schema.Types.Mixed,
+ numbers: [String],
+ status: String
}, {minimize: false});
// changelog query helper
MaterialSchema.query.log = function > (req) {
- db.log(req, this);
- return this;
+ db.log(req, this);
+ return this;
}
MaterialSchema.index({supplier_id: 1});
MaterialSchema.index({group_id: 1});
-export default mongoose.model>('material', MaterialSchema);
\ No newline at end of file
+export default mongoose.model>('material', MaterialSchema);
diff --git a/src/models/material_groups.ts b/src/models/material_groups.ts
index 00be706..6aba0e6 100644
--- a/src/models/material_groups.ts
+++ b/src/models/material_groups.ts
@@ -2,13 +2,13 @@ import mongoose from 'mongoose';
import db from '../db';
const MaterialGroupsSchema = new mongoose.Schema({
- name: {type: String, index: {unique: true}}
+ name: {type: String, index: {unique: true}}
});
// changelog query helper
MaterialGroupsSchema.query.log = function > (req) {
- db.log(req, this);
- return this;
+ db.log(req, this);
+ return this;
}
-export default mongoose.model>('material_groups', MaterialGroupsSchema);
\ No newline at end of file
+export default mongoose.model>('material_groups', MaterialGroupsSchema);
diff --git a/src/models/material_suppliers.ts b/src/models/material_suppliers.ts
index 5c47e3b..24cb102 100644
--- a/src/models/material_suppliers.ts
+++ b/src/models/material_suppliers.ts
@@ -2,13 +2,13 @@ import mongoose from 'mongoose';
import db from '../db';
const MaterialSuppliersSchema = new mongoose.Schema({
- name: {type: String, index: {unique: true}}
+ name: {type: String, index: {unique: true}}
});
// changelog query helper
MaterialSuppliersSchema.query.log = function > (req) {
- db.log(req, this);
- return this;
+ db.log(req, this);
+ return this;
}
-export default mongoose.model>('material_suppliers', MaterialSuppliersSchema);
\ No newline at end of file
+export default mongoose.model>('material_suppliers', MaterialSuppliersSchema);
diff --git a/src/models/material_template.ts b/src/models/material_template.ts
index 5e06819..3e75797 100644
--- a/src/models/material_template.ts
+++ b/src/models/material_template.ts
@@ -2,19 +2,19 @@ import mongoose from 'mongoose';
import db from '../db';
const MaterialTemplateSchema = new mongoose.Schema({
- first_id: mongoose.Schema.Types.ObjectId,
- name: String,
- version: Number,
- parameters: [new mongoose.Schema({
- name: String,
- range: mongoose.Schema.Types.Mixed
- } ,{ _id : false })]
+ first_id: mongoose.Schema.Types.ObjectId,
+ name: String,
+ version: Number,
+ parameters: [new mongoose.Schema({
+ name: String,
+ range: mongoose.Schema.Types.Mixed
+ } ,{ _id : false })]
}, {minimize: false}); // to allow empty objects
// changelog query helper
MaterialTemplateSchema.query.log = function > (req) {
- db.log(req, this);
- return this;
+ db.log(req, this);
+ return this;
}
-export default mongoose.model>('material_template', MaterialTemplateSchema);
\ No newline at end of file
+export default mongoose.model>('material_template', MaterialTemplateSchema);
diff --git a/src/models/measurement.ts b/src/models/measurement.ts
index d83c466..55706fe 100644
--- a/src/models/measurement.ts
+++ b/src/models/measurement.ts
@@ -6,18 +6,18 @@ import db from '../db';
const MeasurementSchema = new mongoose.Schema({
- sample_id: {type: mongoose.Schema.Types.ObjectId, ref: SampleModel},
- values: mongoose.Schema.Types.Mixed,
- measurement_template: {type: mongoose.Schema.Types.ObjectId, ref: MeasurementTemplateModel},
- status: String
+ sample_id: {type: mongoose.Schema.Types.ObjectId, ref: SampleModel},
+ values: mongoose.Schema.Types.Mixed,
+ measurement_template: {type: mongoose.Schema.Types.ObjectId, ref: MeasurementTemplateModel},
+ status: String
}, {minimize: false});
// changelog query helper
MeasurementSchema.query.log = function > (req) {
- db.log(req, this);
- return this;
+ db.log(req, this);
+ return this;
}
MeasurementSchema.index({sample_id: 1});
MeasurementSchema.index({measurement_template: 1});
-export default mongoose.model>('measurement', MeasurementSchema);
\ No newline at end of file
+export default mongoose.model>('measurement', MeasurementSchema);
diff --git a/src/models/measurement_template.ts b/src/models/measurement_template.ts
index b34e847..19228c1 100644
--- a/src/models/measurement_template.ts
+++ b/src/models/measurement_template.ts
@@ -2,19 +2,19 @@ import mongoose from 'mongoose';
import db from '../db';
const MeasurementTemplateSchema = new mongoose.Schema({
- first_id: mongoose.Schema.Types.ObjectId,
- name: String,
- version: Number,
- parameters: [new mongoose.Schema({
- name: String,
- range: mongoose.Schema.Types.Mixed
- } ,{ _id : false })]
+ first_id: mongoose.Schema.Types.ObjectId,
+ name: String,
+ version: Number,
+ parameters: [new mongoose.Schema({
+ name: String,
+ range: mongoose.Schema.Types.Mixed
+ } ,{ _id : false })]
}, {minimize: false}); // to allow empty objects
// changelog query helper
MeasurementTemplateSchema.query.log = function > (req) {
- db.log(req, this);
- return this;
+ db.log(req, this);
+ return this;
}
-export default mongoose.model>('measurement_template', MeasurementTemplateSchema);
\ No newline at end of file
+export default mongoose.model>('measurement_template', MeasurementTemplateSchema);
diff --git a/src/models/model.ts b/src/models/model.ts
index 393b955..2059c98 100644
--- a/src/models/model.ts
+++ b/src/models/model.ts
@@ -2,19 +2,19 @@ import mongoose from 'mongoose';
import db from '../db';
const ModelSchema = new mongoose.Schema({
- group: {type: String, index: {unique: true}},
- models: [new mongoose.Schema({
- name: String,
- url: String,
- label: String
- } ,{ _id : true })]
+ group: {type: String, index: {unique: true}},
+ models: [new mongoose.Schema({
+ name: String,
+ url: String,
+ label: String
+ } ,{ _id : true })]
});
// changelog query helper
ModelSchema.query.log = function > (req) {
- db.log(req, this);
- return this;
+ db.log(req, this);
+ return this;
}
ModelSchema.index({group: 1});
-export default mongoose.model>('model', ModelSchema);
\ No newline at end of file
+export default mongoose.model>('model', ModelSchema);
diff --git a/src/models/model_file.ts b/src/models/model_file.ts
index 891353a..9356b39 100644
--- a/src/models/model_file.ts
+++ b/src/models/model_file.ts
@@ -1,8 +1,8 @@
import mongoose from 'mongoose';
const ModelFileSchema = new mongoose.Schema({
- name: {type: String, index: {unique: true}},
- data: Buffer
+ name: {type: String, index: {unique: true}},
+ data: Buffer
});
-export default mongoose.model>('model_file', ModelFileSchema);
\ No newline at end of file
+export default mongoose.model>('model_file', ModelFileSchema);
diff --git a/src/models/note.ts b/src/models/note.ts
index 5d02502..8df74a4 100644
--- a/src/models/note.ts
+++ b/src/models/note.ts
@@ -2,18 +2,18 @@ import mongoose from 'mongoose';
import db from '../db';
const NoteSchema = new mongoose.Schema({
- comment: String,
- sample_references: [{
- sample_id: mongoose.Schema.Types.ObjectId,
- relation: String
- }],
- custom_fields: mongoose.Schema.Types.Mixed
+ comment: String,
+ sample_references: [{
+ sample_id: mongoose.Schema.Types.ObjectId,
+ relation: String
+ }],
+ custom_fields: mongoose.Schema.Types.Mixed
});
// changelog query helper
NoteSchema.query.log = function > (req) {
- db.log(req, this);
- return this;
+ db.log(req, this);
+ return this;
}
-export default mongoose.model>('note', NoteSchema);
\ No newline at end of file
+export default mongoose.model>('note', NoteSchema);
diff --git a/src/models/note_field.ts b/src/models/note_field.ts
index 733ba02..715e924 100644
--- a/src/models/note_field.ts
+++ b/src/models/note_field.ts
@@ -2,14 +2,14 @@ import mongoose from 'mongoose';
import db from '../db';
const NoteFieldSchema = new mongoose.Schema({
- name: {type: String, index: {unique: true}},
- qty: Number
+ name: {type: String, index: {unique: true}},
+ qty: Number
});
// changelog query helper
NoteFieldSchema.query.log = function > (req) {
- db.log(req, this);
- return this;
+ db.log(req, this);
+ return this;
}
-export default mongoose.model>('note_field', NoteFieldSchema);
\ No newline at end of file
+export default mongoose.model>('note_field', NoteFieldSchema);
diff --git a/src/models/sample.ts b/src/models/sample.ts
index fd8f53e..10df124 100644
--- a/src/models/sample.ts
+++ b/src/models/sample.ts
@@ -6,24 +6,24 @@ import UserModel from './user';
import db from '../db';
const SampleSchema = new mongoose.Schema({
- number: {type: String, index: {unique: true}},
- type: String,
- color: String,
- batch: String,
- condition: mongoose.Schema.Types.Mixed,
- material_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialModel},
- note_id: {type: mongoose.Schema.Types.ObjectId, ref: NoteModel},
- user_id: {type: mongoose.Schema.Types.ObjectId, ref: UserModel},
- status: String
+ number: {type: String, index: {unique: true}},
+ type: String,
+ color: String,
+ batch: String,
+ condition: mongoose.Schema.Types.Mixed,
+ material_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialModel},
+ note_id: {type: mongoose.Schema.Types.ObjectId, ref: NoteModel},
+ user_id: {type: mongoose.Schema.Types.ObjectId, ref: UserModel},
+ status: String
}, {minimize: false});
// changelog query helper
SampleSchema.query.log = function > (req) {
- db.log(req, this);
- return this;
+ db.log(req, this);
+ return this;
}
SampleSchema.index({material_id: 1});
SampleSchema.index({note_id: 1});
SampleSchema.index({user_id: 1});
-export default mongoose.model>('sample', SampleSchema);
\ No newline at end of file
+export default mongoose.model>('sample', SampleSchema);
diff --git a/src/models/user.ts b/src/models/user.ts
index b83a1e2..d546bd2 100644
--- a/src/models/user.ts
+++ b/src/models/user.ts
@@ -3,21 +3,21 @@ import db from '../db';
import ModelModel from './model';
const UserSchema = new mongoose.Schema({
- name: {type: String, index: {unique: true}},
- email: String,
- pass: String,
- key: String,
- level: String,
- location: String,
- devices: [String],
- models: [{type: mongoose.Schema.Types.ObjectId, ref: ModelModel}],
- status: String
+ name: {type: String, index: {unique: true}},
+ email: String,
+ pass: String,
+ key: String,
+ level: String,
+ location: String,
+ devices: [String],
+ models: [{type: mongoose.Schema.Types.ObjectId, ref: ModelModel}],
+ status: String
});
// changelog query helper
UserSchema.query.log = function > (req) {
- db.log(req, this);
- return this;
+ db.log(req, this);
+ return this;
}
-export default mongoose.model>('user', UserSchema);
\ No newline at end of file
+export default mongoose.model>('user', UserSchema);
diff --git a/src/routes/help.spec.ts b/src/routes/help.spec.ts
index 590d47b..010dfb6 100644
--- a/src/routes/help.spec.ts
+++ b/src/routes/help.spec.ts
@@ -4,181 +4,181 @@ import HelpModel from '../models/help';
describe('/help', () => {
- let server;
- before(done => TestHelper.before(done));
- beforeEach(done => server = TestHelper.beforeEach(server, done));
- afterEach(done => TestHelper.afterEach(server, done));
- after(done => TestHelper.after(done));
+ let server;
+ before(done => TestHelper.before(done));
+ beforeEach(done => server = TestHelper.beforeEach(server, done));
+ afterEach(done => TestHelper.afterEach(server, done));
+ after(done => TestHelper.after(done));
- describe('GET /help/{key}', () => {
- it('returns the required text', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/help/%2Fsamples',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- res: {text: 'Samples help', level: 'read'}
- });
- });
- it('returns the required text without authorization if allowed', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/help/%2Fdocumentation',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- res: {text: 'Documentation help', level: 'none'}
- });
- });
- it('returns 404 for an invalid key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/help/documentation/database',
- httpStatus: 404
- });
- });
- it('returns 404 for an unknown key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/help/xxx',
- auth: {basic: 'janedoe'},
- httpStatus: 404
- });
- });
- it('returns 403 for a text with a higher level than given', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/help/%2Fmodels',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/help/%2Fsamples',
- auth: {api: 'janedoe'},
- httpStatus: 401,
- });
- });
- it('rejects an unauthorized request if a level is given', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/help/%2Fsamples',
- httpStatus: 401
- });
- });
- });
+ describe('GET /help/{key}', () => {
+ it('returns the required text', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/help/%2Fsamples',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ res: {text: 'Samples help', level: 'read'}
+ });
+ });
+ it('returns the required text without authorization if allowed', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/help/%2Fdocumentation',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ res: {text: 'Documentation help', level: 'none'}
+ });
+ });
+ it('returns 404 for an invalid key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/help/documentation/database',
+ httpStatus: 404
+ });
+ });
+ it('returns 404 for an unknown key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/help/xxx',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404
+ });
+ });
+ it('returns 403 for a text with a higher level than given', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/help/%2Fmodels',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/help/%2Fsamples',
+ auth: {api: 'janedoe'},
+ httpStatus: 401,
+ });
+ });
+ it('rejects an unauthorized request if a level is given', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/help/%2Fsamples',
+ httpStatus: 401
+ });
+ });
+ });
- describe('POST /help/{key}', () => {
- it('changes the required text', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/help/%2Fsamples',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {text: 'New samples help', level: 'write'}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- HelpModel.find({key: '/samples'}).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]).have.only.keys('_id', 'key', 'text', 'level');
- should(data[0]).property('key', '/samples');
- should(data[0]).property('text', 'New samples help');
- should(data[0]).property('level', 'write');
- done();
- });
- });
- });
- it('saves a new text', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/help/%2Fmaterials',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {text: 'Materials help', level: 'dev'}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- HelpModel.find({key: '/materials'}).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]).have.only.keys('_id', 'key', 'text', 'level', '__v');
- should(data[0]).property('key', '/materials');
- should(data[0]).property('text', 'Materials help');
- should(data[0]).property('level', 'dev');
- done();
- });
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/help/%2Fsamples',
- auth: {key: 'admin'},
- httpStatus: 401,
- req: {text: 'New samples help', level: 'write'}
- });
- });
- it('rejects a write user', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/help/%2Fsamples',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {text: 'New samples help', level: 'write'}
- });
- });
- it('rejects an unauthorized request', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/help/%2Fsamples',
- httpStatus: 401,
- req: {text: 'New samples help', level: 'write'}
- });
- });
- });
+ describe('POST /help/{key}', () => {
+ it('changes the required text', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/help/%2Fsamples',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {text: 'New samples help', level: 'write'}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ HelpModel.find({key: '/samples'}).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.only.keys('_id', 'key', 'text', 'level');
+ should(data[0]).property('key', '/samples');
+ should(data[0]).property('text', 'New samples help');
+ should(data[0]).property('level', 'write');
+ done();
+ });
+ });
+ });
+ it('saves a new text', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/help/%2Fmaterials',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {text: 'Materials help', level: 'dev'}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ HelpModel.find({key: '/materials'}).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.only.keys('_id', 'key', 'text', 'level', '__v');
+ should(data[0]).property('key', '/materials');
+ should(data[0]).property('text', 'Materials help');
+ should(data[0]).property('level', 'dev');
+ done();
+ });
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/help/%2Fsamples',
+ auth: {key: 'admin'},
+ httpStatus: 401,
+ req: {text: 'New samples help', level: 'write'}
+ });
+ });
+ it('rejects a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/help/%2Fsamples',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {text: 'New samples help', level: 'write'}
+ });
+ });
+ it('rejects an unauthorized request', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/help/%2Fsamples',
+ httpStatus: 401,
+ req: {text: 'New samples help', level: 'write'}
+ });
+ });
+ });
- describe('DELETE /help/{key}', () => {
- it('deletes the required entry', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/help/%2Fsamples',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- HelpModel.find({key: '/materials'}).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(0);
- done();
- });
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/help/%2Fsamples',
- auth: {key: 'admin'},
- httpStatus: 401
- });
- });
- it('rejects a write user', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/help/%2Fsamples',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('rejects an unauthorized request', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/help/%2Fsamples',
- httpStatus: 401
- });
- });
- });
-});
\ No newline at end of file
+ describe('DELETE /help/{key}', () => {
+ it('deletes the required entry', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/help/%2Fsamples',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ HelpModel.find({key: '/materials'}).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(0);
+ done();
+ });
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/help/%2Fsamples',
+ auth: {key: 'admin'},
+ httpStatus: 401
+ });
+ });
+ it('rejects a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/help/%2Fsamples',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('rejects an unauthorized request', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/help/%2Fsamples',
+ httpStatus: 401
+ });
+ });
+ });
+});
diff --git a/src/routes/help.ts b/src/routes/help.ts
index 3a2cb51..9c56d81 100644
--- a/src/routes/help.ts
+++ b/src/routes/help.ts
@@ -7,49 +7,49 @@ import globals from '../globals';
const router = express.Router();
router.get('/help/:key', (req, res, next) => {
- const {error: paramError, value: key} = HelpValidate.params(req.params);
- if (paramError) return res400(paramError, res);
+ const {error: paramError, value: key} = HelpValidate.params(req.params);
+ if (paramError) return res400(paramError, res);
- HelpModel.findOne(key).lean().exec((err, data) => {
- if (err) return next(err);
+ HelpModel.findOne(key).lean().exec((err, data) => {
+ if (err) return next(err);
- if (!data) {
- return res.status(404).json({status: 'Not found'});
- }
- if (data.level !== 'none') { // check level
- if (!req.auth(res,
- Object.values(globals.levels).slice(Object.values(globals.levels).findIndex(e => e === data.level))
- , 'basic')) return;
- }
- res.json(HelpValidate.output(data));
- })
+ if (!data) {
+ return res.status(404).json({status: 'Not found'});
+ }
+ if (data.level !== 'none') { // check level
+ if (!req.auth(res,
+ Object.values(globals.levels).slice(Object.values(globals.levels).findIndex(e => e === data.level))
+ , 'basic')) return;
+ }
+ res.json(HelpValidate.output(data));
+ })
});
router.post('/help/:key', (req, res, next) => {
- if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
- const {error: paramError, value: key} = HelpValidate.params(req.params);
- if (paramError) return res400(paramError, res);
- const {error, value: help} = HelpValidate.input(req.body);
- if (error) return res400(error, res);
+ if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
+ const {error: paramError, value: key} = HelpValidate.params(req.params);
+ if (paramError) return res400(paramError, res);
+ const {error, value: help} = HelpValidate.input(req.body);
+ if (error) return res400(error, res);
- HelpModel.findOneAndUpdate(key, help, {upsert: true}).log(req).lean().exec(err => {
- if (err) return next(err);
- res.json({status: 'OK'});
- });
+ HelpModel.findOneAndUpdate(key, help, {upsert: true}).log(req).lean().exec(err => {
+ if (err) return next(err);
+ res.json({status: 'OK'});
+ });
});
router.delete('/help/:key', (req, res, next) => {
- if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
- const {error: paramError, value: key} = HelpValidate.params(req.params);
- if (paramError) return res400(paramError, res);
+ if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
+ const {error: paramError, value: key} = HelpValidate.params(req.params);
+ if (paramError) return res400(paramError, res);
- HelpModel.findOneAndDelete(key).log(req).lean().exec((err, data) => {
- if (err) return next(err);
- if (!data) {
- return res.status(404).json({status: 'Not found'});
- }
- res.json({status: 'OK'});
- });
+ HelpModel.findOneAndDelete(key).log(req).lean().exec((err, data) => {
+ if (err) return next(err);
+ if (!data) {
+ return res.status(404).json({status: 'Not found'});
+ }
+ res.json({status: 'OK'});
+ });
});
-module.exports = router;
\ No newline at end of file
+module.exports = router;
diff --git a/src/routes/material.spec.ts b/src/routes/material.spec.ts
index 657cb7c..7aca4a3 100644
--- a/src/routes/material.spec.ts
+++ b/src/routes/material.spec.ts
@@ -6,1115 +6,1115 @@ import TestHelper from "../test/helper";
describe('/material', () => {
- let server;
- before(done => TestHelper.before(done));
- beforeEach(done => server = TestHelper.beforeEach(server, done));
- afterEach(done => TestHelper.afterEach(server, done));
- after(done => TestHelper.after(done));
+ let server;
+ before(done => TestHelper.before(done));
+ beforeEach(done => server = TestHelper.beforeEach(server, done));
+ afterEach(done => TestHelper.afterEach(server, done));
+ after(done => TestHelper.after(done));
- describe('GET /materials', () => {
- it('returns all materials', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/materials',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'validated').length);
- should(res.body).matchEach(material => {
- should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers', 'status');
- should(material).have.property('_id').be.type('string');
- should(material).have.property('name').be.type('string');
- should(material).have.property('supplier').be.type('string');
- should(material).have.property('group').be.type('string');
- should(material).have.property('status', 'validated');
- should(material.properties).have.property('material_template').be.type('string');
- should(material.numbers).be.instanceof(Array);
- });
- done();
- });
- });
- it('works with an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/materials',
- auth: {key: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'validated').length);
- should(res.body).matchEach(material => {
- should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers', 'status');
- should(material).have.property('_id').be.type('string');
- should(material).have.property('name').be.type('string');
- should(material).have.property('supplier').be.type('string');
- should(material).have.property('group').be.type('string');
- should(material).have.property('status', 'validated');
- should(material.properties).have.property('material_template').be.type('string');
- should(material.numbers).be.instanceof(Array);
- });
- done();
- });
- });
- it('allows filtering by state', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/materials?status[]=new',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'new').length);
- should(res.body).matchEach(material => {
- should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers', 'status');
- should(material).have.property('_id').be.type('string');
- should(material).have.property('name').be.type('string');
- should(material).have.property('supplier').be.type('string');
- should(material).have.property('group').be.type('string');
- should(material).have.property('status', 'new');
- should(material.properties).have.property('material_template').be.type('string');
- should(material.numbers).be.instanceof(Array);
- });
- done();
- });
- });
- it('allows filtering by deleted state for admins', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/materials?status[]=deleted',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'deleted').length);
- should(res.body).matchEach(material => {
- should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers', 'status');
- should(material).have.property('_id').be.type('string');
- should(material).have.property('name').be.type('string');
- should(material).have.property('supplier').be.type('string');
- should(material).have.property('group').be.type('string');
- should(material).have.property('status', 'deleted');
- should(material.properties).have.property('material_template').be.type('string');
- should(material.numbers).be.instanceof(Array);
- });
- done();
- });
- });
- it('rejects filtering by deleted state for a write user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/materials?status[]=deleted',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: '"status[0]" must be one of [validated, new]'}
- });
- });
- it('rejects an invalid state name', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/materials?status[]=xxx',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: '"status[0]" must be one of [validated, new]'}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/materials',
- httpStatus: 401
- });
- });
- });
+ describe('GET /materials', () => {
+ it('returns all materials', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/materials',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'validated').length);
+ should(res.body).matchEach(material => {
+ should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers', 'status');
+ should(material).have.property('_id').be.type('string');
+ should(material).have.property('name').be.type('string');
+ should(material).have.property('supplier').be.type('string');
+ should(material).have.property('group').be.type('string');
+ should(material).have.property('status', 'validated');
+ should(material.properties).have.property('material_template').be.type('string');
+ should(material.numbers).be.instanceof(Array);
+ });
+ done();
+ });
+ });
+ it('works with an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/materials',
+ auth: {key: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'validated').length);
+ should(res.body).matchEach(material => {
+ should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers', 'status');
+ should(material).have.property('_id').be.type('string');
+ should(material).have.property('name').be.type('string');
+ should(material).have.property('supplier').be.type('string');
+ should(material).have.property('group').be.type('string');
+ should(material).have.property('status', 'validated');
+ should(material.properties).have.property('material_template').be.type('string');
+ should(material.numbers).be.instanceof(Array);
+ });
+ done();
+ });
+ });
+ it('allows filtering by state', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/materials?status[]=new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'new').length);
+ should(res.body).matchEach(material => {
+ should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers', 'status');
+ should(material).have.property('_id').be.type('string');
+ should(material).have.property('name').be.type('string');
+ should(material).have.property('supplier').be.type('string');
+ should(material).have.property('group').be.type('string');
+ should(material).have.property('status', 'new');
+ should(material.properties).have.property('material_template').be.type('string');
+ should(material.numbers).be.instanceof(Array);
+ });
+ done();
+ });
+ });
+ it('allows filtering by deleted state for admins', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/materials?status[]=deleted',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'deleted').length);
+ should(res.body).matchEach(material => {
+ should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers', 'status');
+ should(material).have.property('_id').be.type('string');
+ should(material).have.property('name').be.type('string');
+ should(material).have.property('supplier').be.type('string');
+ should(material).have.property('group').be.type('string');
+ should(material).have.property('status', 'deleted');
+ should(material.properties).have.property('material_template').be.type('string');
+ should(material.numbers).be.instanceof(Array);
+ });
+ done();
+ });
+ });
+ it('rejects filtering by deleted state for a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/materials?status[]=deleted',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: '"status[0]" must be one of [validated, new]'}
+ });
+ });
+ it('rejects an invalid state name', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/materials?status[]=xxx',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: '"status[0]" must be one of [validated, new]'}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/materials',
+ httpStatus: 401
+ });
+ });
+ });
- describe('GET /materials/{state}', () => {
- it('returns all new materials', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/materials/new',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- let asyncCounter = res.body.length;
- should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status ==='new').length);
- should(res.body).matchEach(material => {
- should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers');
- should(material).have.property('_id').be.type('string');
- should(material).have.property('name').be.type('string');
- should(material).have.property('supplier').be.type('string');
- should(material).have.property('group').be.type('string');
- should(material.properties).have.property('material_template').be.type('string');
- should(material.numbers).be.instanceof(Array);
- MaterialModel.findById(material._id).lean().exec((err, data) => {
- should(data).have.property('status','new');
- if (--asyncCounter === 0) {
- done();
- }
- });
- });
- });
- });
- it('returns all deleted materials', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/materials/deleted',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- let asyncCounter = res.body.length;
- should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status ==='deleted').length);
- should(res.body).matchEach(material => {
- should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers');
- should(material).have.property('_id').be.type('string');
- should(material).have.property('name').be.type('string');
- should(material).have.property('supplier').be.type('string');
- should(material).have.property('group').be.type('string');
- should(material.properties).have.property('material_template').be.type('string');
- should(material.numbers).be.instanceof(Array);
- MaterialModel.findById(material._id).lean().exec((err, data) => {
- should(data).have.property('status','deleted');
- if (--asyncCounter === 0) {
- done();
- }
- });
- });
- });
- });
- it('rejects requests from a write user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/materials/new',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/materials/deleted',
- auth: {key: 'admin'},
- httpStatus: 401
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/materials/new',
- httpStatus: 401
- });
- });
- });
+ describe('GET /materials/{state}', () => {
+ it('returns all new materials', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/materials/new',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ let asyncCounter = res.body.length;
+ should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status ==='new').length);
+ should(res.body).matchEach(material => {
+ should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers');
+ should(material).have.property('_id').be.type('string');
+ should(material).have.property('name').be.type('string');
+ should(material).have.property('supplier').be.type('string');
+ should(material).have.property('group').be.type('string');
+ should(material.properties).have.property('material_template').be.type('string');
+ should(material.numbers).be.instanceof(Array);
+ MaterialModel.findById(material._id).lean().exec((err, data) => {
+ should(data).have.property('status','new');
+ if (--asyncCounter === 0) {
+ done();
+ }
+ });
+ });
+ });
+ });
+ it('returns all deleted materials', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/materials/deleted',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ let asyncCounter = res.body.length;
+ should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status ==='deleted').length);
+ should(res.body).matchEach(material => {
+ should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers');
+ should(material).have.property('_id').be.type('string');
+ should(material).have.property('name').be.type('string');
+ should(material).have.property('supplier').be.type('string');
+ should(material).have.property('group').be.type('string');
+ should(material.properties).have.property('material_template').be.type('string');
+ should(material.numbers).be.instanceof(Array);
+ MaterialModel.findById(material._id).lean().exec((err, data) => {
+ should(data).have.property('status','deleted');
+ if (--asyncCounter === 0) {
+ done();
+ }
+ });
+ });
+ });
+ });
+ it('rejects requests from a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/materials/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/materials/deleted',
+ auth: {key: 'admin'},
+ httpStatus: 401
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/materials/new',
+ httpStatus: 401
+ });
+ });
+ });
- describe('GET /material/{id}', () => {
- it('returns the right material', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/material/100000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- res: {_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423', '5514263422']}
- });
- });
- it('returns the right material for an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/material/100000000000000000000003',
- auth: {key: 'admin'},
- httpStatus: 200,
- res: {_id: '100000000000000000000003', name: 'PA GF 50 black (2706)', supplier: 'Akro-Plastic', group: 'PA66+PA6I/6T', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 0, carbon_fiber: 0}, numbers: []}
- });
- });
- it('returns a material with a color without number', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/material/100000000000000000000007',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- res: {_id: '100000000000000000000007', name: 'Ultramid A4H', supplier: 'BASF', group: 'PA66', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 0, carbon_fiber: 0}, numbers: []}
- });
- });
- it('returns a deleted material for a dev/admin user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/material/100000000000000000000008',
- auth: {basic: 'admin'},
- httpStatus: 200,
- res: {_id: '100000000000000000000008', name: 'Latamid 66 H 2 G 30', supplier: 'LATI', group: 'PA66', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: ['5513943509']}
- });
- });
- it('returns 403 for a write user when requesting a deleted material', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/material/100000000000000000000008',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('rejects an invalid id', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/material/10000000000000000000000x',
- auth: {key: 'admin'},
- httpStatus: 404
- });
- });
- it('rejects an unknown id', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/material/100000000000000000000111',
- auth: {key: 'janedoe'},
- httpStatus: 404
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/material/100000000000000000000001',
- httpStatus: 401
- });
- });
- });
+ describe('GET /material/{id}', () => {
+ it('returns the right material', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/material/100000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ res: {_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423', '5514263422']}
+ });
+ });
+ it('returns the right material for an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/material/100000000000000000000003',
+ auth: {key: 'admin'},
+ httpStatus: 200,
+ res: {_id: '100000000000000000000003', name: 'PA GF 50 black (2706)', supplier: 'Akro-Plastic', group: 'PA66+PA6I/6T', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 0, carbon_fiber: 0}, numbers: []}
+ });
+ });
+ it('returns a material with a color without number', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/material/100000000000000000000007',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ res: {_id: '100000000000000000000007', name: 'Ultramid A4H', supplier: 'BASF', group: 'PA66', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 0, carbon_fiber: 0}, numbers: []}
+ });
+ });
+ it('returns a deleted material for a dev/admin user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/material/100000000000000000000008',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ res: {_id: '100000000000000000000008', name: 'Latamid 66 H 2 G 30', supplier: 'LATI', group: 'PA66', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: ['5513943509']}
+ });
+ });
+ it('returns 403 for a write user when requesting a deleted material', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/material/100000000000000000000008',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('rejects an invalid id', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/material/10000000000000000000000x',
+ auth: {key: 'admin'},
+ httpStatus: 404
+ });
+ });
+ it('rejects an unknown id', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/material/100000000000000000000111',
+ auth: {key: 'janedoe'},
+ httpStatus: 404
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/material/100000000000000000000001',
+ httpStatus: 401
+ });
+ });
+ });
- describe('PUT /material/{id}', () => {
- it('returns the right material', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {},
- res: {_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423', '5514263422']}
- });
- });
- it('keeps unchanged properties', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423', '5514263422']}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423', '5514263422']});
- MaterialModel.findById('100000000000000000000001').lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.property('status','validated');
- MaterialGroupModel.find({name: 'PA46'}).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]._id.toString()).be.eql('900000000000000000000001');
- MaterialSupplierModel.find({name: 'DSM'}).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]._id.toString()).be.eql('110000000000000000000001');
- done();
- });
- });
- });
- });
- });
- it('keeps only one unchanged property', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {name: 'Stanyl TW 200 F8'}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423', '5514263422']});
- MaterialModel.findById('100000000000000000000001').lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.property('status','validated');
- done();
- });
- });
- });
- it('keeps unchanged properties', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423', '5514263422']});
- MaterialModel.findById('100000000000000000000001').lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.property('status','validated');
- done();
- });
- });
- });
- it('changes the given properties', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {name: 'UltramidTKR4355G7_2', supplier: 'BASF', group: 'PA6/6T', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 35, carbon_fiber: 0}, numbers: ['5514212901', '5514612901']}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({_id: '100000000000000000000001', name: 'UltramidTKR4355G7_2', supplier: 'BASF', group: 'PA6/6T', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 35, carbon_fiber: 0}, numbers: ['5514212901', '5514612901']});
- MaterialModel.findById('100000000000000000000001').lean().exec((err, data:any) => {
- if (err) return done(err);
- data._id = data._id.toString();
- data.group_id = data.group_id.toString();
- data.supplier_id = data.supplier_id.toString();
- should(data).be.eql({_id: '100000000000000000000001', name: 'UltramidTKR4355G7_2', supplier_id: '110000000000000000000002', group_id: '900000000000000000000002', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 35, carbon_fiber: 0}, numbers: ['5514212901', '5514612901'], status: 'new', __v: 0});
- MaterialGroupModel.find({name: 'PA6/6T'}).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]._id.toString()).be.eql('900000000000000000000002');
- MaterialSupplierModel.find({name: 'BASF'}).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]._id.toString()).be.eql('110000000000000000000002');
- done();
- });
- });
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {name: 'UltramidTKR4355G7_2', supplier: 'BASF', group: 'PA6/6T', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 35, carbon_fiber: 0}, numbers: ['5514212901', '5514612901']},
- log: {
- collection: 'materials',
- dataAdd: {
- group_id: '900000000000000000000002',
- supplier_id: '110000000000000000000002',
- status: 'new'
- },
- dataIgn: ['supplier', 'group']
- }
- });
- });
- it('rejects already existing material names', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {name: 'Ultramid T KR 4355 G7'},
- res: {status: 'Material name already taken'}
- });
- });
- it('rejects wrong material properties', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {properties: {material_template: '130000000000000000000003', mineral: 'x', glass_fiber: 0, carbon_fiber: 0}},
- res: {status: 'Invalid body format', details: '"mineral" must be a number'}
- });
- });
- it('rejects an invalid id', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/10000000000000000000000x',
- auth: {basic: 'admin'},
- httpStatus: 404,
- req: {},
- });
- });
- it('rejects not specified properties parameters', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 0, carbon_fiber: 0, x: 55}},
- res: {status: 'Invalid body format', details: '"x" is not allowed'}
- });
- });
- it('rejects a properties parameter not in the value range', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000009',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {properties: {material_template: '130000000000000000000002', stickiness: 'xx'}},
- res: {status: 'Invalid body format', details: '"stickiness" must be one of [not so sticky, medium, very sticky]'}
- });
- });
- it('rejects a properties parameter below minimum range', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: -5, carbon_fiber: 0}},
- res: {status: 'Invalid body format', details: '"glass_fiber" must be greater than or equal to 0'}
- });
- });
- it('rejects a properties parameter above maximum range', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 0, carbon_fiber: 105}},
- res: {status: 'Invalid body format', details: '"carbon_fiber" must be less than or equal to 100'}
- });
- });
- it('rejects an invalid material template', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {properties: {material_template: '1300000000000h0000000001', mineral: 0, glass_fiber: 0, carbon_fiber: 0}},
- res: {status: 'Material template not available'}
- });
- });
- it('rejects an unknown material template', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {properties: {material_template: '100000000000000000000001', mineral: 0, glass_fiber: 0, carbon_fiber: 0}},
- res: {status: 'Material template not available'}
- });
- });
- it('rejects an old version of a material template', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {properties: {material_template: '130000000000000000000001', glass_fiber: 0}},
- res: {status: 'Old template version not allowed'}
- });
- });
- it('allows keeping an old version of a material template', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000010',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {properties: {material_template: '130000000000000000000001', glass_fiber: 5}},
- res: {_id: '100000000000000000000010', name: 'Latamid 66 G 40', numbers: ['5513943509'], supplier: 'LATI', group: 'PA66', properties: {material_template: '130000000000000000000001', glass_fiber: 5}}
- });
- });
- it('rejects editing a deleted material', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000008',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {}
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000002',
- auth: {key: 'admin'},
- httpStatus: 401,
- req: {}
- });
- });
- it('rejects requests from a read user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000002',
- auth: {basic: 'user'},
- httpStatus: 403,
- req: {}
- });
- });
- it('returns 404 for an unknown material', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000111',
- auth: {basic: 'janedoe'},
- httpStatus: 404,
- req: {}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/100000000000000000000001',
- httpStatus: 401,
- req: {}
- });
- });
- });
+ describe('PUT /material/{id}', () => {
+ it('returns the right material', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {},
+ res: {_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423', '5514263422']}
+ });
+ });
+ it('keeps unchanged properties', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423', '5514263422']}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423', '5514263422']});
+ MaterialModel.findById('100000000000000000000001').lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.property('status','validated');
+ MaterialGroupModel.find({name: 'PA46'}).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]._id.toString()).be.eql('900000000000000000000001');
+ MaterialSupplierModel.find({name: 'DSM'}).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]._id.toString()).be.eql('110000000000000000000001');
+ done();
+ });
+ });
+ });
+ });
+ });
+ it('keeps only one unchanged property', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {name: 'Stanyl TW 200 F8'}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423', '5514263422']});
+ MaterialModel.findById('100000000000000000000001').lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.property('status','validated');
+ done();
+ });
+ });
+ });
+ it('keeps unchanged properties', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423', '5514263422']});
+ MaterialModel.findById('100000000000000000000001').lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.property('status','validated');
+ done();
+ });
+ });
+ });
+ it('changes the given properties', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {name: 'UltramidTKR4355G7_2', supplier: 'BASF', group: 'PA6/6T', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 35, carbon_fiber: 0}, numbers: ['5514212901', '5514612901']}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({_id: '100000000000000000000001', name: 'UltramidTKR4355G7_2', supplier: 'BASF', group: 'PA6/6T', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 35, carbon_fiber: 0}, numbers: ['5514212901', '5514612901']});
+ MaterialModel.findById('100000000000000000000001').lean().exec((err, data:any) => {
+ if (err) return done(err);
+ data._id = data._id.toString();
+ data.group_id = data.group_id.toString();
+ data.supplier_id = data.supplier_id.toString();
+ should(data).be.eql({_id: '100000000000000000000001', name: 'UltramidTKR4355G7_2', supplier_id: '110000000000000000000002', group_id: '900000000000000000000002', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 35, carbon_fiber: 0}, numbers: ['5514212901', '5514612901'], status: 'new', __v: 0});
+ MaterialGroupModel.find({name: 'PA6/6T'}).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]._id.toString()).be.eql('900000000000000000000002');
+ MaterialSupplierModel.find({name: 'BASF'}).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]._id.toString()).be.eql('110000000000000000000002');
+ done();
+ });
+ });
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {name: 'UltramidTKR4355G7_2', supplier: 'BASF', group: 'PA6/6T', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 35, carbon_fiber: 0}, numbers: ['5514212901', '5514612901']},
+ log: {
+ collection: 'materials',
+ dataAdd: {
+ group_id: '900000000000000000000002',
+ supplier_id: '110000000000000000000002',
+ status: 'new'
+ },
+ dataIgn: ['supplier', 'group']
+ }
+ });
+ });
+ it('rejects already existing material names', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {name: 'Ultramid T KR 4355 G7'},
+ res: {status: 'Material name already taken'}
+ });
+ });
+ it('rejects wrong material properties', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {properties: {material_template: '130000000000000000000003', mineral: 'x', glass_fiber: 0, carbon_fiber: 0}},
+ res: {status: 'Invalid body format', details: '"mineral" must be a number'}
+ });
+ });
+ it('rejects an invalid id', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/10000000000000000000000x',
+ auth: {basic: 'admin'},
+ httpStatus: 404,
+ req: {},
+ });
+ });
+ it('rejects not specified properties parameters', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 0, carbon_fiber: 0, x: 55}},
+ res: {status: 'Invalid body format', details: '"x" is not allowed'}
+ });
+ });
+ it('rejects a properties parameter not in the value range', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000009',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {properties: {material_template: '130000000000000000000002', stickiness: 'xx'}},
+ res: {status: 'Invalid body format', details: '"stickiness" must be one of [not so sticky, medium, very sticky]'}
+ });
+ });
+ it('rejects a properties parameter below minimum range', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: -5, carbon_fiber: 0}},
+ res: {status: 'Invalid body format', details: '"glass_fiber" must be greater than or equal to 0'}
+ });
+ });
+ it('rejects a properties parameter above maximum range', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 0, carbon_fiber: 105}},
+ res: {status: 'Invalid body format', details: '"carbon_fiber" must be less than or equal to 100'}
+ });
+ });
+ it('rejects an invalid material template', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {properties: {material_template: '1300000000000h0000000001', mineral: 0, glass_fiber: 0, carbon_fiber: 0}},
+ res: {status: 'Material template not available'}
+ });
+ });
+ it('rejects an unknown material template', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {properties: {material_template: '100000000000000000000001', mineral: 0, glass_fiber: 0, carbon_fiber: 0}},
+ res: {status: 'Material template not available'}
+ });
+ });
+ it('rejects an old version of a material template', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {properties: {material_template: '130000000000000000000001', glass_fiber: 0}},
+ res: {status: 'Old template version not allowed'}
+ });
+ });
+ it('allows keeping an old version of a material template', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000010',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {properties: {material_template: '130000000000000000000001', glass_fiber: 5}},
+ res: {_id: '100000000000000000000010', name: 'Latamid 66 G 40', numbers: ['5513943509'], supplier: 'LATI', group: 'PA66', properties: {material_template: '130000000000000000000001', glass_fiber: 5}}
+ });
+ });
+ it('rejects editing a deleted material', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000008',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {}
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000002',
+ auth: {key: 'admin'},
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ it('rejects requests from a read user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000002',
+ auth: {basic: 'user'},
+ httpStatus: 403,
+ req: {}
+ });
+ });
+ it('returns 404 for an unknown material', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000111',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404,
+ req: {}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/100000000000000000000001',
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ });
- describe('DELETE /material/{id}', () => {
- it('sets the status to deleted', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/material/100000000000000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- MaterialModel.findById('100000000000000000000002').lean().exec((err, data: any) => {
- if (err) return done(err);
- data._id = data._id.toString();
- data.group_id = data.group_id.toString();
- data.supplier_id = data.supplier_id.toString();
- data.properties.material_template = data.properties.material_template.toString();
- should(data).be.eql({_id: '100000000000000000000002', name: 'Ultramid T KR 4355 G7', supplier_id: '110000000000000000000002', group_id: '900000000000000000000002', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 35, carbon_fiber: 0}, numbers: ['5514212901', '5514612901'], status: 'deleted', __v: 0}
- );
- done();
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/material/100000000000000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- log: {
- collection: 'materials',
- dataAdd: { status: 'deleted'}
- }
- });
- });
- it('rejects deleting a material referenced by samples', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/material/100000000000000000000004',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- res: {status: 'Material still in use'}
- })
- });
- it('rejects an invalid id', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/material/10000000000000000000000x',
- auth: {basic: 'admin'},
- httpStatus: 404
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/material/100000000000000000000002',
- auth: {key: 'admin'},
- httpStatus: 401
- });
- });
- it('rejects requests from a read user', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/material/100000000000000000000002',
- auth: {basic: 'user'},
- httpStatus: 403
- });
- });
- it('returns 404 for an unknown id', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/material/100000000000000000000111',
- auth: {basic: 'janedoe'},
- httpStatus: 404
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/material/100000000000000000000001',
- httpStatus: 401
- });
- });
- });
+ describe('DELETE /material/{id}', () => {
+ it('sets the status to deleted', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/material/100000000000000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ MaterialModel.findById('100000000000000000000002').lean().exec((err, data: any) => {
+ if (err) return done(err);
+ data._id = data._id.toString();
+ data.group_id = data.group_id.toString();
+ data.supplier_id = data.supplier_id.toString();
+ data.properties.material_template = data.properties.material_template.toString();
+ should(data).be.eql({_id: '100000000000000000000002', name: 'Ultramid T KR 4355 G7', supplier_id: '110000000000000000000002', group_id: '900000000000000000000002', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 35, carbon_fiber: 0}, numbers: ['5514212901', '5514612901'], status: 'deleted', __v: 0}
+ );
+ done();
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/material/100000000000000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ log: {
+ collection: 'materials',
+ dataAdd: { status: 'deleted'}
+ }
+ });
+ });
+ it('rejects deleting a material referenced by samples', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/material/100000000000000000000004',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ res: {status: 'Material still in use'}
+ })
+ });
+ it('rejects an invalid id', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/material/10000000000000000000000x',
+ auth: {basic: 'admin'},
+ httpStatus: 404
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/material/100000000000000000000002',
+ auth: {key: 'admin'},
+ httpStatus: 401
+ });
+ });
+ it('rejects requests from a read user', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/material/100000000000000000000002',
+ auth: {basic: 'user'},
+ httpStatus: 403
+ });
+ });
+ it('returns 404 for an unknown id', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/material/100000000000000000000111',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/material/100000000000000000000001',
+ httpStatus: 401
+ });
+ });
+ });
- describe('PUT /material/restore/{id}', () => {
- it('sets the status', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/restore/100000000000000000000008',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- MaterialModel.findById('100000000000000000000008').lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).have.property('status','new');
- done();
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/restore/100000000000000000000008',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {},
- log: {
- collection: 'materials',
- dataAdd: {
- status: 'new'
- }
- }
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/restore/100000000000000000000008',
- auth: {key: 'admin'},
- httpStatus: 401,
- req: {}
- });
- });
- it('rejects a write user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/restore/100000000000000000000008',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {}
- });
- });
- it('returns 404 for an unknown sample', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/restore/000000000000000000000008',
- auth: {basic: 'admin'},
- httpStatus: 404,
- req: {}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/restore/100000000000000000000008',
- httpStatus: 401,
- req: {}
- });
- });
- });
+ describe('PUT /material/restore/{id}', () => {
+ it('sets the status', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/restore/100000000000000000000008',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ MaterialModel.findById('100000000000000000000008').lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).have.property('status','new');
+ done();
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/restore/100000000000000000000008',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {},
+ log: {
+ collection: 'materials',
+ dataAdd: {
+ status: 'new'
+ }
+ }
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/restore/100000000000000000000008',
+ auth: {key: 'admin'},
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ it('rejects a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/restore/100000000000000000000008',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {}
+ });
+ });
+ it('returns 404 for an unknown sample', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/restore/000000000000000000000008',
+ auth: {basic: 'admin'},
+ httpStatus: 404,
+ req: {}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/restore/100000000000000000000008',
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ });
- describe('PUT /material/validate/{id}', () => {
- it('sets the status', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/validate/100000000000000000000007',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- MaterialModel.findById('100000000000000000000007').lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).have.property('status','validated');
- done();
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/validate/100000000000000000000007',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {},
- log: {
- collection: 'materials',
- dataAdd: {
- status: 'validated'
- }
- }
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/validate/100000000000000000000007',
- auth: {key: 'admin'},
- httpStatus: 401,
- req: {}
- });
- });
- it('rejects a write user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/validate/100000000000000000000007',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {}
- });
- });
- it('returns 404 for an unknown sample', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/validate/000000000000000000000007',
- auth: {basic: 'admin'},
- httpStatus: 404,
- req: {}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/material/validate/100000000000000000000007',
- httpStatus: 401,
- req: {}
- });
- });
- });
+ describe('PUT /material/validate/{id}', () => {
+ it('sets the status', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/validate/100000000000000000000007',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ MaterialModel.findById('100000000000000000000007').lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).have.property('status','validated');
+ done();
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/validate/100000000000000000000007',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {},
+ log: {
+ collection: 'materials',
+ dataAdd: {
+ status: 'validated'
+ }
+ }
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/validate/100000000000000000000007',
+ auth: {key: 'admin'},
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ it('rejects a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/validate/100000000000000000000007',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {}
+ });
+ });
+ it('returns 404 for an unknown sample', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/validate/000000000000000000000007',
+ auth: {basic: 'admin'},
+ httpStatus: 404,
+ req: {}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/material/validate/100000000000000000000007',
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ });
- describe('POST /material/new', () => {
- it('returns the right material', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: ['5515798402']}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers');
- should(res.body).have.property('_id').be.type('string');
- should(res.body).have.property('name', 'Crastin CE 2510');
- should(res.body).have.property('supplier', 'Du Pont');
- should(res.body).have.property('group', 'PBT');
- should(res.body.properties).have.property('material_template', '130000000000000000000003');
- should(res.body.properties).have.property('mineral', 0);
- should(res.body.properties).have.property('glass_fiber', 30);
- should(res.body.properties).have.property('carbon_fiber', 0);
- should(res.body).have.property('numbers', ['5515798402']);
- done();
- });
- });
- it('stores the material', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: []}
- }).end(err => {
- if (err) return done (err);
- MaterialModel.find({name: 'Crastin CE 2510'}).lean().exec((err, materialData: any) => {
- if (err) return done (err);
- should(materialData).have.lengthOf(1);
- should(materialData[0]).have.only.keys('_id', 'name', 'supplier_id', 'group_id', 'properties', 'numbers', 'status', '__v');
- should(materialData[0]).have.property('name', 'Crastin CE 2510');
- should(materialData[0].properties).have.property('material_template', '130000000000000000000003');
- should(materialData[0].properties).have.property('mineral', 0);
- should(materialData[0].properties).have.property('glass_fiber', 30);
- should(materialData[0].properties).have.property('carbon_fiber', 0);
- should(materialData[0]).have.property('status','new');
- should(materialData[0].numbers).have.lengthOf(0);
- MaterialGroupModel.findById(materialData[0].group_id).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.property('name', 'PBT')
- MaterialSupplierModel.findById(materialData[0].supplier_id).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.property('name', 'Du Pont');
- done();
- });
- });
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: []},
- log: {
- collection: 'materials',
- dataAdd: {status: 'new'},
- dataIgn: ['group_id', 'supplier_id', 'group', 'supplier']
- }
- });
- });
- it('rejects already existing material names', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423']},
- res: {status: 'Material name already taken'}
- });
- });
- it('rejects a missing name', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: ['5515798402']},
- res: {status: 'Invalid body format', details: '"name" is required'}
- });
- });
- it('rejects a missing supplier', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {name: 'Crastin CE 2510', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: ['5515798402']},
- res: {status: 'Invalid body format', details: '"supplier" is required'}
- });
- });
- it('rejects a missing group', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: ['5515798402']},
- res: {status: 'Invalid body format', details: '"group" is required'}
- });
- });
- it('rejects a missing mineral property', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', glass_fiber: 30, carbon_fiber: 0}, numbers: ['5515798402']},
- res: {status: 'Invalid body format', details: '"mineral" is required'}
- });
- });
- it('rejects a missing glass_fiber property', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, carbon_fiber: 0}, numbers: ['5515798402']},
- res: {status: 'Invalid body format', details: '"glass_fiber" is required'}
- });
- });
- it('rejects a missing carbon_fiber property', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30}, numbers: ['5515798402']},
- res: {status: 'Invalid body format', details: '"carbon_fiber" is required'}
- });
- });
- it('rejects a missing numbers array', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}},
- res: {status: 'Invalid body format', details: '"numbers" is required'}
- });
- });
- it('rejects not specified properties parameters', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, carbon_fiber: 0, glass_fiber: 30, x: 47}, numbers: ['5515798402']},
- res: {status: 'Invalid body format', details: '"x" is not allowed'}
- });
- });
- it('rejects a properties parameter not in the value range', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {name: 'Glue2', supplier: 'BASF', group: 'Glue', properties: {material_template: '130000000000000000000002', stickiness: 'not so much'}, numbers: []},
- res: {status: 'Invalid body format', details: '"stickiness" must be one of [not so sticky, medium, very sticky]'}
- });
- });
- it('rejects a properties parameter below minimum range', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', glass_fiber: -0.3}, numbers: ['5515798402']},
- res: {status: 'Invalid body format', details: '"glass_fiber" must be greater than or equal to 0'}
- });
- });
- it('rejects a properties parameter above maximum range', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', glass_fiber: 100.001}, numbers: ['5515798402']},
- res: {status: 'Invalid body format', details: '"glass_fiber" must be less than or equal to 100'}
- });
- });
- it('rejects an invalid material template', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000h00000000000003', glass_fiber: 30}, numbers: ['5515798402']},
- res: {status: 'Material template not available'}
- });
- });
- it('rejects an unknown material template', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '100000000000000000000003', glass_fiber: 30}, numbers: ['5515798402']},
- res: {status: 'Material template not available'}
- });
- });
- it('rejects an old version of a material template', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000001', glass_fiber: 30}, numbers: ['5515798402']},
- res: {status: 'Old template version not allowed'}
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {key: 'janedoe'},
- httpStatus: 401,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: []}
- });
- });
- it('rejects requests from a read user', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- auth: {basic: 'user'},
- httpStatus: 403,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: []}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/material/new',
- httpStatus: 401,
- req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: []}
- });
- });
- });
+ describe('POST /material/new', () => {
+ it('returns the right material', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: ['5515798402']}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers');
+ should(res.body).have.property('_id').be.type('string');
+ should(res.body).have.property('name', 'Crastin CE 2510');
+ should(res.body).have.property('supplier', 'Du Pont');
+ should(res.body).have.property('group', 'PBT');
+ should(res.body.properties).have.property('material_template', '130000000000000000000003');
+ should(res.body.properties).have.property('mineral', 0);
+ should(res.body.properties).have.property('glass_fiber', 30);
+ should(res.body.properties).have.property('carbon_fiber', 0);
+ should(res.body).have.property('numbers', ['5515798402']);
+ done();
+ });
+ });
+ it('stores the material', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: []}
+ }).end(err => {
+ if (err) return done (err);
+ MaterialModel.find({name: 'Crastin CE 2510'}).lean().exec((err, materialData: any) => {
+ if (err) return done (err);
+ should(materialData).have.lengthOf(1);
+ should(materialData[0]).have.only.keys('_id', 'name', 'supplier_id', 'group_id', 'properties', 'numbers', 'status', '__v');
+ should(materialData[0]).have.property('name', 'Crastin CE 2510');
+ should(materialData[0].properties).have.property('material_template', '130000000000000000000003');
+ should(materialData[0].properties).have.property('mineral', 0);
+ should(materialData[0].properties).have.property('glass_fiber', 30);
+ should(materialData[0].properties).have.property('carbon_fiber', 0);
+ should(materialData[0]).have.property('status','new');
+ should(materialData[0].numbers).have.lengthOf(0);
+ MaterialGroupModel.findById(materialData[0].group_id).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.property('name', 'PBT')
+ MaterialSupplierModel.findById(materialData[0].supplier_id).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.property('name', 'Du Pont');
+ done();
+ });
+ });
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: []},
+ log: {
+ collection: 'materials',
+ dataAdd: {status: 'new'},
+ dataIgn: ['group_id', 'supplier_id', 'group', 'supplier']
+ }
+ });
+ });
+ it('rejects already existing material names', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423']},
+ res: {status: 'Material name already taken'}
+ });
+ });
+ it('rejects a missing name', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: ['5515798402']},
+ res: {status: 'Invalid body format', details: '"name" is required'}
+ });
+ });
+ it('rejects a missing supplier', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {name: 'Crastin CE 2510', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: ['5515798402']},
+ res: {status: 'Invalid body format', details: '"supplier" is required'}
+ });
+ });
+ it('rejects a missing group', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: ['5515798402']},
+ res: {status: 'Invalid body format', details: '"group" is required'}
+ });
+ });
+ it('rejects a missing mineral property', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', glass_fiber: 30, carbon_fiber: 0}, numbers: ['5515798402']},
+ res: {status: 'Invalid body format', details: '"mineral" is required'}
+ });
+ });
+ it('rejects a missing glass_fiber property', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, carbon_fiber: 0}, numbers: ['5515798402']},
+ res: {status: 'Invalid body format', details: '"glass_fiber" is required'}
+ });
+ });
+ it('rejects a missing carbon_fiber property', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30}, numbers: ['5515798402']},
+ res: {status: 'Invalid body format', details: '"carbon_fiber" is required'}
+ });
+ });
+ it('rejects a missing numbers array', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}},
+ res: {status: 'Invalid body format', details: '"numbers" is required'}
+ });
+ });
+ it('rejects not specified properties parameters', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, carbon_fiber: 0, glass_fiber: 30, x: 47}, numbers: ['5515798402']},
+ res: {status: 'Invalid body format', details: '"x" is not allowed'}
+ });
+ });
+ it('rejects a properties parameter not in the value range', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {name: 'Glue2', supplier: 'BASF', group: 'Glue', properties: {material_template: '130000000000000000000002', stickiness: 'not so much'}, numbers: []},
+ res: {status: 'Invalid body format', details: '"stickiness" must be one of [not so sticky, medium, very sticky]'}
+ });
+ });
+ it('rejects a properties parameter below minimum range', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', glass_fiber: -0.3}, numbers: ['5515798402']},
+ res: {status: 'Invalid body format', details: '"glass_fiber" must be greater than or equal to 0'}
+ });
+ });
+ it('rejects a properties parameter above maximum range', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', glass_fiber: 100.001}, numbers: ['5515798402']},
+ res: {status: 'Invalid body format', details: '"glass_fiber" must be less than or equal to 100'}
+ });
+ });
+ it('rejects an invalid material template', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000h00000000000003', glass_fiber: 30}, numbers: ['5515798402']},
+ res: {status: 'Material template not available'}
+ });
+ });
+ it('rejects an unknown material template', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '100000000000000000000003', glass_fiber: 30}, numbers: ['5515798402']},
+ res: {status: 'Material template not available'}
+ });
+ });
+ it('rejects an old version of a material template', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000001', glass_fiber: 30}, numbers: ['5515798402']},
+ res: {status: 'Old template version not allowed'}
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {key: 'janedoe'},
+ httpStatus: 401,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: []}
+ });
+ });
+ it('rejects requests from a read user', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ auth: {basic: 'user'},
+ httpStatus: 403,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: []}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/material/new',
+ httpStatus: 401,
+ req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: []}
+ });
+ });
+ });
- describe('GET /material/groups', () => {
- it('returns all groups', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/material/groups',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.material_groups.length);
- should(res.body[0]).be.eql(json.collections.material_groups[0].name);
- should(res.body).matchEach(group => {
- should(group).be.type('string');
- });
- done();
- });
- });
- it('works with an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/material/groups',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.material_groups.length);
- should(res.body[0]).be.eql(json.collections.material_groups[0].name);
- should(res.body).matchEach(group => {
- should(group).be.type('string');
- });
- done();
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/material/groups',
- httpStatus: 401
- });
- });
- });
+ describe('GET /material/groups', () => {
+ it('returns all groups', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/material/groups',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.material_groups.length);
+ should(res.body[0]).be.eql(json.collections.material_groups[0].name);
+ should(res.body).matchEach(group => {
+ should(group).be.type('string');
+ });
+ done();
+ });
+ });
+ it('works with an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/material/groups',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.material_groups.length);
+ should(res.body[0]).be.eql(json.collections.material_groups[0].name);
+ should(res.body).matchEach(group => {
+ should(group).be.type('string');
+ });
+ done();
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/material/groups',
+ httpStatus: 401
+ });
+ });
+ });
- describe('GET /material/suppliers', () => {
- it('returns all suppliers', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/material/suppliers',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.material_suppliers.length);
- should(res.body[0]).be.eql(json.collections.material_suppliers[0].name);
- should(res.body).matchEach(supplier => {
- should(supplier).be.type('string');
- });
- done();
- });
- });
- it('works with an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/material/suppliers',
- auth: {key: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.material_suppliers.length);
- should(res.body[0]).be.eql(json.collections.material_suppliers[0].name);
- should(res.body).matchEach(supplier => {
- should(supplier).be.type('string');
- });
- done();
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/material/suppliers',
- httpStatus: 401
- });
- });
- });
-});
\ No newline at end of file
+ describe('GET /material/suppliers', () => {
+ it('returns all suppliers', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/material/suppliers',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.material_suppliers.length);
+ should(res.body[0]).be.eql(json.collections.material_suppliers[0].name);
+ should(res.body).matchEach(supplier => {
+ should(supplier).be.type('string');
+ });
+ done();
+ });
+ });
+ it('works with an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/material/suppliers',
+ auth: {key: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.material_suppliers.length);
+ should(res.body[0]).be.eql(json.collections.material_suppliers[0].name);
+ should(res.body).matchEach(supplier => {
+ should(supplier).be.type('string');
+ });
+ done();
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/material/suppliers',
+ httpStatus: 401
+ });
+ });
+ });
+});
diff --git a/src/routes/material.ts b/src/routes/material.ts
index 3996e30..de97245 100644
--- a/src/routes/material.ts
+++ b/src/routes/material.ts
@@ -19,169 +19,169 @@ import globals from '../globals';
const router = express.Router();
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} =
- MaterialValidate.query(req.query, ['dev', 'admin'].indexOf(req.authDetails.level) >= 0);
- if (error) return res400(error, res);
+ const {error, value: filters} =
+ MaterialValidate.query(req.query, ['dev', 'admin'].indexOf(req.authDetails.level) >= 0);
+ if (error) return res400(error, res);
- MaterialModel.find(filters).sort({name: 1}).populate('group_id').populate('supplier_id')
- .lean().exec((err, data) => {
- if (err) return next(err);
+ MaterialModel.find(filters).sort({name: 1}).populate('group_id').populate('supplier_id')
+ .lean().exec((err, data) => {
+ if (err) return next(err);
- // validate all and filter null values from validation errors
- res.json(_.compact(data.map(e => MaterialValidate.output(e, true))));
- });
+ // validate all and filter null values from validation errors
+ res.json(_.compact(data.map(e => MaterialValidate.output(e, true))));
+ });
});
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')
- .lean().exec((err, data) => {
- if (err) return next(err);
+ MaterialModel.find({status: req.params.state}).populate('group_id').populate('supplier_id')
+ .lean().exec((err, data) => {
+ if (err) return next(err);
- // validate all and filter null values from validation errors
- res.json(_.compact(data.map(e => MaterialValidate.output(e))));
- });
+ // validate all and filter null values from validation errors
+ res.json(_.compact(data.map(e => MaterialValidate.output(e))));
+ });
});
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) => {
- if (err) return next(err);
+ MaterialModel.findById(req.params.id).populate('group_id').populate('supplier_id').lean().exec((err, data: any) => {
+ if (err) return next(err);
- if (!data) {
- return res.status(404).json({status: 'Not found'});
- }
+ if (!data) {
+ return res.status(404).json({status: 'Not found'});
+ }
- // deleted materials only available for dev/admin
- if (data.status === globals.status.del && !req.auth(res, ['dev', 'admin'], 'all')) return;
- res.json(MaterialValidate.output(data));
- });
+ // deleted materials only available for dev/admin
+ if (data.status === globals.status.del && !req.auth(res, ['dev', 'admin'], 'all')) return;
+ res.json(MaterialValidate.output(data));
+ });
});
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');
- if (error) return res400(error, res);
+ let {error, value: material} = MaterialValidate.input(req.body, 'change');
+ if (error) return res400(error, res);
- MaterialModel.findById(req.params.id).lean().exec(async (err, materialData: any) => {
- if (!materialData) {
- return res.status(404).json({status: 'Not found'});
- }
- if (materialData.status === 'deleted') {
- return res.status(403).json({status: 'Forbidden'});
- }
- if (material.hasOwnProperty('name') && material.name !== materialData.name) {
- if (!await nameCheck(material, res, next)) return;
- }
- if (material.hasOwnProperty('group')) {
- material = await groupResolve(material, req, next);
- if (!material) return;
- }
- if (material.hasOwnProperty('supplier')) {
- material = await supplierResolve(material, req, next);
- if (!material) return;
- }
- if (material.hasOwnProperty('properties')) {
- if (!await propertiesCheck(material.properties, 'change', res, next,
- materialData.properties.material_template.toString() !== material.properties.material_template)) return;
- }
+ MaterialModel.findById(req.params.id).lean().exec(async (err, materialData: any) => {
+ if (!materialData) {
+ return res.status(404).json({status: 'Not found'});
+ }
+ if (materialData.status === 'deleted') {
+ return res.status(403).json({status: 'Forbidden'});
+ }
+ if (material.hasOwnProperty('name') && material.name !== materialData.name) {
+ if (!await nameCheck(material, res, next)) return;
+ }
+ if (material.hasOwnProperty('group')) {
+ material = await groupResolve(material, req, next);
+ if (!material) return;
+ }
+ if (material.hasOwnProperty('supplier')) {
+ material = await supplierResolve(material, req, next);
+ if (!material) return;
+ }
+ if (material.hasOwnProperty('properties')) {
+ if (!await propertiesCheck(material.properties, 'change', res, next,
+ materialData.properties.material_template.toString() !== material.properties.material_template)) return;
+ }
- // check for changes
- if (!_.isEqual(_.pick(IdValidate.stringify(materialData), _.keys(material)), IdValidate.stringify(material))) {
- material.status = globals.status.new; // set status to new
- }
+ // check for changes
+ if (!_.isEqual(_.pick(IdValidate.stringify(materialData), _.keys(material)), IdValidate.stringify(material))) {
+ material.status = globals.status.new; // set status to new
+ }
- await MaterialModel.findByIdAndUpdate(req.params.id, material, {new: true})
- .log(req).populate('group_id').populate('supplier_id').lean().exec((err, data) => {
- if (err) return next(err);
- res.json(MaterialValidate.output(data));
- });
- });
+ await MaterialModel.findByIdAndUpdate(req.params.id, material, {new: true})
+ .log(req).populate('group_id').populate('supplier_id').lean().exec((err, data) => {
+ if (err) return next(err);
+ res.json(MaterialValidate.output(data));
+ });
+ });
});
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
- SampleModel.find({'material_id': new mongoose.Types.ObjectId(req.params.id), status: {$ne: globals.status.del}})
- .lean().exec((err, data) => {
- if (err) return next(err);
- if (data.length) {
- return res.status(400).json({status: 'Material still in use'});
- }
- MaterialModel.findByIdAndUpdate(req.params.id, {status: globals.status.del})
- .log(req).populate('group_id').populate('supplier_id').lean().exec((err, data) => {
- if (err) return next(err);
- if (data) {
- res.json({status: 'OK'});
- }
- else {
- res.status(404).json({status: 'Not found'});
- }
- });
- });
+ // 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}})
+.lean().exec((err, data) => {
+ if (err) return next(err);
+ if (data.length) {
+ return res.status(400).json({status: 'Material still in use'});
+ }
+ MaterialModel.findByIdAndUpdate(req.params.id, {status: globals.status.del})
+ .log(req).populate('group_id').populate('supplier_id').lean().exec((err, data) => {
+ if (err) return next(err);
+ if (data) {
+ res.json({status: 'OK'});
+ }
+ else {
+ res.status(404).json({status: 'Not found'});
+ }
+ });
+});
});
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) => {
- 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) => {
- 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');
- if (error) return res400(error, res);
+ let {error, value: material} = MaterialValidate.input(req.body, 'new');
+ if (error) return res400(error, res);
- if (!await nameCheck(material, res, next)) return;
- material = await groupResolve(material, req, next);
- if (!material) return;
- material = await supplierResolve(material, req, next);
- if (!material) return;
- if (!await propertiesCheck(material.properties, 'new', res, next)) return;
+ if (!await nameCheck(material, res, next)) return;
+ material = await groupResolve(material, req, next);
+ if (!material) return;
+ material = await supplierResolve(material, req, next);
+ if (!material) return;
+ if (!await propertiesCheck(material.properties, 'new', res, next)) return;
- material.status = globals.status.new; // set status to new
- await new MaterialModel(material).save(async (err, data) => {
- if (err) return next(err);
- db.log(req, 'materials', {_id: data._id}, data.toObject());
- await data.populate('group_id').populate('supplier_id').execPopulate().catch(err => next(err));
- if (data instanceof Error) return;
- res.json(MaterialValidate.output(data.toObject()));
- });
+ material.status = globals.status.new; // set status to new
+ await new MaterialModel(material).save(async (err, data) => {
+ if (err) return next(err);
+ db.log(req, 'materials', {_id: data._id}, data.toObject());
+ await data.populate('group_id').populate('supplier_id').execPopulate().catch(err => next(err));
+ if (data instanceof Error) return;
+ res.json(MaterialValidate.output(data.toObject()));
+ });
});
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) => {
- if (err) return next(err);
+ MaterialGroupModel.find().lean().exec((err, data: any) => {
+ if (err) return next(err);
- // validate all and filter null values from validation errors
- res.json(_.compact(data.map(e => MaterialValidate.outputGroups(e.name))));
- });
+ // validate all and filter null values from validation errors
+ res.json(_.compact(data.map(e => MaterialValidate.outputGroups(e.name))));
+ });
});
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) => {
- if (err) return next(err);
+ MaterialSupplierModel.find().lean().exec((err, data: any) => {
+ if (err) return next(err);
- // validate all and filter null values from validation errors
- res.json(_.compact(data.map(e => MaterialValidate.outputSuppliers(e.name))));
- });
+ // validate all and filter null values from validation errors
+ 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
- const materialData = await MaterialModel.findOne({name: material.name}).lean().exec().catch(err => next(err)) as any;
- if (materialData instanceof Error) return false;
- if (materialData) { // could not find material_id
- res.status(400).json({status: 'Material name already taken'});
- return false;
- }
- return true;
+ const materialData = await MaterialModel.findOne({name: material.name}).lean().exec().catch(err => next(err)) as any;
+if (materialData instanceof Error) return false;
+if (materialData) { // could not find material_id
+ res.status(400).json({status: 'Material name already taken'});
+ return false;
+}
+return true;
}
async function groupResolve (material, req, next) {
- const groupData = await MaterialGroupModel.findOneAndUpdate(
- {name: material.group},
- {name: material.group},
- {upsert: true, new: true}
- ).log(req).lean().exec().catch(err => next(err)) as any;
- if (groupData instanceof Error) return false;
- material.group_id = groupData._id;
- delete material.group;
- return material;
+ const groupData = await MaterialGroupModel.findOneAndUpdate(
+ {name: material.group},
+ {name: material.group},
+ {upsert: true, new: true}
+ ).log(req).lean().exec().catch(err => next(err)) as any;
+ if (groupData instanceof Error) return false;
+ material.group_id = groupData._id;
+ delete material.group;
+ return material;
}
async function supplierResolve (material, req, next) {
- const supplierData = await MaterialSupplierModel.findOneAndUpdate(
- {name: material.supplier},
- {name: material.supplier},
- {upsert: true, new: true}
- ).log(req).lean().exec().catch(err => next(err)) as any;
- if (supplierData instanceof Error) return false;
- material.supplier_id = supplierData._id;
- delete material.supplier;
- return material;
+ const supplierData = await MaterialSupplierModel.findOneAndUpdate(
+ {name: material.supplier},
+ {name: material.supplier},
+ {upsert: true, new: true}
+ ).log(req).lean().exec().catch(err => next(err)) as any;
+ if (supplierData instanceof Error) return false;
+ material.supplier_id = supplierData._id;
+ delete material.supplier;
+ return material;
}
// validate material properties, returns false if invalid, otherwise template data
async function propertiesCheck (properties, param, res, next, checkVersion = true) {
- if (!properties.material_template || !IdValidate.valid(properties.material_template)) { // template id not found
- res.status(400).json({status: 'Material template not available'});
- return false;
- }
- const materialData = await MaterialTemplateModel.findById(properties.material_template)
- .lean().exec().catch(err => next(err)) as any;
- if (materialData instanceof Error) return false;
- if (!materialData) { // template not found
- res.status(400).json({status: 'Material template not available'});
- return false;
- }
+ if (!properties.material_template || !IdValidate.valid(properties.material_template)) { // template id not found
+ res.status(400).json({status: 'Material template not available'});
+ return false;
+ }
+ const materialData = await MaterialTemplateModel.findById(properties.material_template)
+ .lean().exec().catch(err => next(err)) as any;
+ if (materialData instanceof Error) return false;
+ if (!materialData) { // template not found
+ res.status(400).json({status: 'Material template not available'});
+ return false;
+ }
- if (checkVersion) {
- // get all template versions and check if given is latest
- const materialVersions = await MaterialTemplateModel.find({first_id: materialData.first_id}).sort({version: -1})
- .lean().exec().catch(err => next(err)) as any;
- if (materialVersions instanceof Error) return false;
- if (properties.material_template !== materialVersions[0]._id.toString()) { // template not latest
- res.status(400).json({status: 'Old template version not allowed'});
- return false;
- }
- }
+ if (checkVersion) {
+ // get all template versions and check if given is latest
+ const materialVersions = await MaterialTemplateModel.find({first_id: materialData.first_id}).sort({version: -1})
+ .lean().exec().catch(err => next(err)) as any;
+ if (materialVersions instanceof Error) return false;
+ if (properties.material_template !== materialVersions[0]._id.toString()) { // template not latest
+ res.status(400).json({status: 'Old template version not allowed'});
+ return false;
+ }
+ }
- // validate parameters
- const {error, value} = ParametersValidate
- .input(_.omit(properties, 'material_template'), materialData.parameters, param);
- if (error) {res400(error, res); return false;}
- Object.keys(value).forEach(key => {
- properties[key] = value[key];
- });
- return materialData;
+ // validate parameters
+ const {error, value} = ParametersValidate
+ .input(_.omit(properties, 'material_template'), materialData.parameters, param);
+ if (error) {res400(error, res); return false;}
+ Object.keys(value).forEach(key => {
+ properties[key] = value[key];
+ });
+ return materialData;
}
function setStatus (status, req, res, next) { // set measurement status
- MaterialModel.findByIdAndUpdate(req.params.id, {status: status}).log(req).lean().exec((err, data) => {
- if (err) return next(err);
+ MaterialModel.findByIdAndUpdate(req.params.id, {status: status}).log(req).lean().exec((err, data) => {
+ if (err) return next(err);
- if (!data) {
- return res.status(404).json({status: 'Not found'});
- }
- res.json({status: 'OK'});
- });
-}
\ No newline at end of file
+ if (!data) {
+ return res.status(404).json({status: 'Not found'});
+ }
+ res.json({status: 'OK'});
+ });
+}
diff --git a/src/routes/measurement.spec.ts b/src/routes/measurement.spec.ts
index ec8885f..eb89c19 100644
--- a/src/routes/measurement.spec.ts
+++ b/src/routes/measurement.spec.ts
@@ -4,850 +4,850 @@ import TestHelper from "../test/helper";
describe('/measurement', () => {
- let server;
- before(done => TestHelper.before(done));
- beforeEach(done => server = TestHelper.beforeEach(server, done));
- afterEach(done => TestHelper.afterEach(server, done));
- after(done => TestHelper.after(done));
+ let server;
+ before(done => TestHelper.before(done));
+ beforeEach(done => server = TestHelper.beforeEach(server, done));
+ afterEach(done => TestHelper.afterEach(server, done));
+ after(done => TestHelper.after(done));
- describe('GET /measurement/{id}', () => {
- it('returns the right measurement', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/measurement/800000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- res: {_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}
- });
- });
- it('returns the measurement for an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/measurement/800000000000000000000001',
- auth: {key: 'admin'},
- httpStatus: 200,
- res: {_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}
- });
- });
- it('filters out spectral data for a write user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/measurement/800000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- res: {_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}
- });
- });
- it('returns deleted measurements for a dev/admin user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/measurement/800000000000000000000004',
- auth: {basic: 'admin'},
- httpStatus: 200,
- res: {_id: '800000000000000000000004', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}
- });
- });
- it('rejects requests for deleted measurements from a write user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/measurement/800000000000000000000004',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('rejects an invalid id', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/measurement/8000000000h0000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 404
- });
- });
- it('rejects an unknown id', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/measurement/000000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 404
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/measurement/800000000000000000000001',
- httpStatus: 401
- });
- });
- });
+ describe('GET /measurement/{id}', () => {
+ it('returns the right measurement', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/measurement/800000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ res: {_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}
+ });
+ });
+ it('returns the measurement for an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/measurement/800000000000000000000001',
+ auth: {key: 'admin'},
+ httpStatus: 200,
+ res: {_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}
+ });
+ });
+ it('filters out spectral data for a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/measurement/800000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ res: {_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}
+ });
+ });
+ it('returns deleted measurements for a dev/admin user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/measurement/800000000000000000000004',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ res: {_id: '800000000000000000000004', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}
+ });
+ });
+ it('rejects requests for deleted measurements from a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/measurement/800000000000000000000004',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('rejects an invalid id', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/measurement/8000000000h0000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404
+ });
+ });
+ it('rejects an unknown id', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/measurement/000000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/measurement/800000000000000000000001',
+ httpStatus: 401
+ });
+ });
+ });
- describe('PUT /measurement/{id}', () => {
- it('returns the right measurement', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {},
- res: {_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}
- });
- });
- it('keeps unchanged values', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I', filename: '1_0.DPT'}}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'});
- MeasurementModel.findById('800000000000000000000001').lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).have.property('status','validated');
- done();
- });
- });
- });
- it('keeps only one unchanged value', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {values: {'weight %': 0.5}}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({_id: '800000000000000000000002', sample_id: '400000000000000000000002', values: {'weight %': 0.5, 'standard deviation': 0.2}, measurement_template: '300000000000000000000002'});
- MeasurementModel.findById('800000000000000000000002').lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).have.property('status','validated');
- done();
- });
- });
- });
- it('changes the given values', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {values: {dpt: [[1,2],[3,4],[5,6]]}}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[1,2],[3,4],[5,6]], device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'});
- MeasurementModel.findById('800000000000000000000001').lean().exec((err, data: any) => {
- should(data).have.only.keys('_id', 'sample_id', 'values', 'measurement_template', 'status', '__v');
- should(data.sample_id.toString()).be.eql('400000000000000000000001');
- should(data.measurement_template.toString()).be.eql('300000000000000000000001');
- should(data).have.property('status','new');
- should(data).have.property('values');
- should(data.values).have.property('dpt', [[1,2],[3,4],[5,6]]);
- done();
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {values: {dpt: [[1,2],[3,4],[5,6]], device: 'Alpha I', filename: '1_0.DPT'}},
- log: {
- collection: 'measurements',
- dataAdd: {
- measurement_template: '300000000000000000000001',
- sample_id: '400000000000000000000001',
- status: 'new'
- },
- dataIgn: ['values']
- }
- });
- });
- it('allows changing only one value', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {values: {'weight %': 0.9}},
- res: {_id: '800000000000000000000002', sample_id: '400000000000000000000002', values: {'weight %': 0.9, 'standard deviation': 0.2}, measurement_template: '300000000000000000000002'}
- });
- });
- it('allows keeping empty values empty', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000005',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {values: {'weight %': 0.9}},
- res: {_id: '800000000000000000000005', sample_id: '400000000000000000000002', values: {'weight %': 0.9, 'standard deviation': null}, measurement_template: '300000000000000000000002'}
- });
- });
- it('rejects not specified values', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {values: {'weight %': 0.9, 'standard deviation': 0.3, xx: 44}},
- res: {status: 'Invalid body format', details: '"xx" is not allowed'}
- });
- });
- it('rejects a value not in the value range', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000003',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {values: {val1: 4}},
- res: {status: 'Invalid body format', details: '"val1" must be one of [1, 2, 3, null]'}
- });
- });
- it('rejects a value below minimum range', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {values: {'weight %': -1, 'standard deviation': 0.3}},
- res: {status: 'Invalid body format', details: '"weight %" must be greater than or equal to 0'}
- });
- });
- it('rejects a value above maximum range', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {values: {'weight %': 0.9, 'standard deviation': 3}},
- res: {status: 'Invalid body format', details: '"standard deviation" must be less than or equal to 0.5'}
- });
- });
- it('rejects a new measurement template', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {values: {'weight %': 0.9, 'standard deviation': 0.3}, measurement_template: '300000000000000000000001'},
- res: {status: 'Invalid body format', details: '"measurement_template" is not allowed'}
- });
- });
- it('rejects a new sample id', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {values: {'weight %': 0.9, 'standard deviation': 0.3}, sample_id: '400000000000000000000002'},
- res: {status: 'Invalid body format', details: '"sample_id" is not allowed'}
- });
- });
- it('rejects editing a measurement for a write user who did not create this measurement', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000003',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {values: {val1: 2}}
- });
- });
- it('accepts editing a measurement of another user for a dev/admin user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000002',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {values: {'weight %': 0.9, 'standard deviation': 0.3}},
- res: {_id: '800000000000000000000002', sample_id: '400000000000000000000002', values: {'weight %': 0.9, 'standard deviation': 0.3}, measurement_template: '300000000000000000000002'}
- });
- });
- it('rejects an invalid id', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000h00000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 404
- });
- });
- it('rejects an unknown id', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/000000000000000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 404
- });
- });
- it('rejects editing a deleted measurement', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000004',
- auth: {basic: 'admin'},
- httpStatus: 403,
- req: {}
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000002',
- auth: {key: 'janedoe'},
- httpStatus: 401,
- req: {values: {'weight %': 0.9, 'standard deviation': 0.3}},
- });
- });
- it('rejects requests from a read user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000002',
- auth: {basic: 'user'},
- httpStatus: 403,
- req: {values: {'weight %': 0.9, 'standard deviation': 0.3}},
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/800000000000000000000002',
- httpStatus: 401,
- req: {values: {'weight %': 0.9, 'standard deviation': 0.3}},
- });
- });
- });
+ describe('PUT /measurement/{id}', () => {
+ it('returns the right measurement', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {},
+ res: {_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}
+ });
+ });
+ it('keeps unchanged values', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I', filename: '1_0.DPT'}}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'});
+ MeasurementModel.findById('800000000000000000000001').lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).have.property('status','validated');
+ done();
+ });
+ });
+ });
+ it('keeps only one unchanged value', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {values: {'weight %': 0.5}}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({_id: '800000000000000000000002', sample_id: '400000000000000000000002', values: {'weight %': 0.5, 'standard deviation': 0.2}, measurement_template: '300000000000000000000002'});
+ MeasurementModel.findById('800000000000000000000002').lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).have.property('status','validated');
+ done();
+ });
+ });
+ });
+ it('changes the given values', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {values: {dpt: [[1,2],[3,4],[5,6]]}}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[1,2],[3,4],[5,6]], device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'});
+ MeasurementModel.findById('800000000000000000000001').lean().exec((err, data: any) => {
+ should(data).have.only.keys('_id', 'sample_id', 'values', 'measurement_template', 'status', '__v');
+ should(data.sample_id.toString()).be.eql('400000000000000000000001');
+ should(data.measurement_template.toString()).be.eql('300000000000000000000001');
+ should(data).have.property('status','new');
+ should(data).have.property('values');
+ should(data.values).have.property('dpt', [[1,2],[3,4],[5,6]]);
+ done();
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {values: {dpt: [[1,2],[3,4],[5,6]], device: 'Alpha I', filename: '1_0.DPT'}},
+ log: {
+ collection: 'measurements',
+ dataAdd: {
+ measurement_template: '300000000000000000000001',
+ sample_id: '400000000000000000000001',
+ status: 'new'
+ },
+ dataIgn: ['values']
+ }
+ });
+ });
+ it('allows changing only one value', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {values: {'weight %': 0.9}},
+ res: {_id: '800000000000000000000002', sample_id: '400000000000000000000002', values: {'weight %': 0.9, 'standard deviation': 0.2}, measurement_template: '300000000000000000000002'}
+ });
+ });
+ it('allows keeping empty values empty', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000005',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {values: {'weight %': 0.9}},
+ res: {_id: '800000000000000000000005', sample_id: '400000000000000000000002', values: {'weight %': 0.9, 'standard deviation': null}, measurement_template: '300000000000000000000002'}
+ });
+ });
+ it('rejects not specified values', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {values: {'weight %': 0.9, 'standard deviation': 0.3, xx: 44}},
+ res: {status: 'Invalid body format', details: '"xx" is not allowed'}
+ });
+ });
+ it('rejects a value not in the value range', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000003',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {values: {val1: 4}},
+ res: {status: 'Invalid body format', details: '"val1" must be one of [1, 2, 3, null]'}
+ });
+ });
+ it('rejects a value below minimum range', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {values: {'weight %': -1, 'standard deviation': 0.3}},
+ res: {status: 'Invalid body format', details: '"weight %" must be greater than or equal to 0'}
+ });
+ });
+ it('rejects a value above maximum range', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {values: {'weight %': 0.9, 'standard deviation': 3}},
+ res: {status: 'Invalid body format', details: '"standard deviation" must be less than or equal to 0.5'}
+ });
+ });
+ it('rejects a new measurement template', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {values: {'weight %': 0.9, 'standard deviation': 0.3}, measurement_template: '300000000000000000000001'},
+ res: {status: 'Invalid body format', details: '"measurement_template" is not allowed'}
+ });
+ });
+ it('rejects a new sample id', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {values: {'weight %': 0.9, 'standard deviation': 0.3}, sample_id: '400000000000000000000002'},
+ res: {status: 'Invalid body format', details: '"sample_id" is not allowed'}
+ });
+ });
+ it('rejects editing a measurement for a write user who did not create this measurement', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000003',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {values: {val1: 2}}
+ });
+ });
+ it('accepts editing a measurement of another user for a dev/admin user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000002',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {values: {'weight %': 0.9, 'standard deviation': 0.3}},
+ res: {_id: '800000000000000000000002', sample_id: '400000000000000000000002', values: {'weight %': 0.9, 'standard deviation': 0.3}, measurement_template: '300000000000000000000002'}
+ });
+ });
+ it('rejects an invalid id', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000h00000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404
+ });
+ });
+ it('rejects an unknown id', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/000000000000000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404
+ });
+ });
+ it('rejects editing a deleted measurement', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000004',
+ auth: {basic: 'admin'},
+ httpStatus: 403,
+ req: {}
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000002',
+ auth: {key: 'janedoe'},
+ httpStatus: 401,
+ req: {values: {'weight %': 0.9, 'standard deviation': 0.3}},
+ });
+ });
+ it('rejects requests from a read user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000002',
+ auth: {basic: 'user'},
+ httpStatus: 403,
+ req: {values: {'weight %': 0.9, 'standard deviation': 0.3}},
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/800000000000000000000002',
+ httpStatus: 401,
+ req: {values: {'weight %': 0.9, 'standard deviation': 0.3}},
+ });
+ });
+ });
- describe('DELETE /measurement/{id}', () => {
- it('sets the status to deleted', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/measurement/800000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- MeasurementModel.findById('800000000000000000000001').lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.property('status','deleted');
- done();
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/measurement/800000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- log: {
- collection: 'measurements',
- dataAdd: {
- status: 'deleted'
- }
- }
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/measurement/800000000000000000000001',
- auth: {key: 'janedoe'},
- httpStatus: 401,
- });
- });
- it('rejects requests from a read user', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/measurement/800000000000000000000001',
- auth: {basic: 'user'},
- httpStatus: 403,
- });
- });
- it('rejects deleting a measurement for a write user who did not create this measurement', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/measurement/800000000000000000000003',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- });
- });
- it('accepts deleting a measurement of another user for a dev/admin user', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/measurement/800000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- res: {status: 'OK'}
- });
- });
- it('rejects an invalid id', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/measurement/800000000h00000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 404,
- });
- });
- it('rejects an unknown id', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/measurement/000000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 404,
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/measurement/800000000000000000000001',
- httpStatus: 401,
- });
- });
- });
+ describe('DELETE /measurement/{id}', () => {
+ it('sets the status to deleted', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/measurement/800000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ MeasurementModel.findById('800000000000000000000001').lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.property('status','deleted');
+ done();
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/measurement/800000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ log: {
+ collection: 'measurements',
+ dataAdd: {
+ status: 'deleted'
+ }
+ }
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/measurement/800000000000000000000001',
+ auth: {key: 'janedoe'},
+ httpStatus: 401,
+ });
+ });
+ it('rejects requests from a read user', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/measurement/800000000000000000000001',
+ auth: {basic: 'user'},
+ httpStatus: 403,
+ });
+ });
+ it('rejects deleting a measurement for a write user who did not create this measurement', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/measurement/800000000000000000000003',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ });
+ });
+ it('accepts deleting a measurement of another user for a dev/admin user', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/measurement/800000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ res: {status: 'OK'}
+ });
+ });
+ it('rejects an invalid id', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/measurement/800000000h00000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404,
+ });
+ });
+ it('rejects an unknown id', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/measurement/000000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404,
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/measurement/800000000000000000000001',
+ httpStatus: 401,
+ });
+ });
+ });
- describe('GET /measurement/sample/{id}', () => {
- it('returns the right measurements', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/measurement/sample/400000000000000000000003',
- auth: {basic: 'admin'},
- httpStatus: 200,
- res: [
- {_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003', status: 'new'},
- {_id: '800000000000000000000004', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003', status: 'deleted'}
- ]
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/measurement/sample/400000000000000000000003',
- auth: {key: 'admin'},
- httpStatus: 401
- });
- });
- it('rejects a write user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/measurement/sample/400000000000000000000003',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('rejects an invalid id', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/measurement/sample/4000000000h0000000000003',
- auth: {basic: 'admin'},
- httpStatus: 404
- });
- });
- it('rejects an unknown id', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/measurement/sample/000000000000000000000003',
- auth: {basic: 'admin'},
- httpStatus: 404
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/measurement/sample/400000000000000000000003',
- httpStatus: 401
- });
- });
- });
+ describe('GET /measurement/sample/{id}', () => {
+ it('returns the right measurements', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/measurement/sample/400000000000000000000003',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ res: [
+ {_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003', status: 'new'},
+ {_id: '800000000000000000000004', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003', status: 'deleted'}
+ ]
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/measurement/sample/400000000000000000000003',
+ auth: {key: 'admin'},
+ httpStatus: 401
+ });
+ });
+ it('rejects a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/measurement/sample/400000000000000000000003',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('rejects an invalid id', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/measurement/sample/4000000000h0000000000003',
+ auth: {basic: 'admin'},
+ httpStatus: 404
+ });
+ });
+ it('rejects an unknown id', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/measurement/sample/000000000000000000000003',
+ auth: {basic: 'admin'},
+ httpStatus: 404
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/measurement/sample/400000000000000000000003',
+ httpStatus: 401
+ });
+ });
+ });
- describe('PUT /measurement/restore/{id}', () => {
- it('sets the status', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/restore/800000000000000000000004',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- MeasurementModel.findById('800000000000000000000004').lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).have.property('status','new');
- done();
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/restore/800000000000000000000004',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {},
- log: {
- collection: 'measurements',
- dataAdd: {
- status: 'new'
- }
- }
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/restore/800000000000000000000004',
- auth: {key: 'admin'},
- httpStatus: 401,
- req: {}
- });
- });
- it('rejects a write user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/restore/800000000000000000000004',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {}
- });
- });
- it('returns 404 for an unknown sample', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/restore/000000000000000000000004',
- auth: {basic: 'admin'},
- httpStatus: 404,
- req: {}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/restore/800000000000000000000004',
- httpStatus: 401,
- req: {}
- });
- });
- });
+ describe('PUT /measurement/restore/{id}', () => {
+ it('sets the status', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/restore/800000000000000000000004',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ MeasurementModel.findById('800000000000000000000004').lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).have.property('status','new');
+ done();
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/restore/800000000000000000000004',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {},
+ log: {
+ collection: 'measurements',
+ dataAdd: {
+ status: 'new'
+ }
+ }
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/restore/800000000000000000000004',
+ auth: {key: 'admin'},
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ it('rejects a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/restore/800000000000000000000004',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {}
+ });
+ });
+ it('returns 404 for an unknown sample', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/restore/000000000000000000000004',
+ auth: {basic: 'admin'},
+ httpStatus: 404,
+ req: {}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/restore/800000000000000000000004',
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ });
- describe('PUT /measurement/validate/{id}', () => {
- it('sets the status', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/validate/800000000000000000000003',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- MeasurementModel.findById('800000000000000000000003').lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).have.property('status','validated');
- done();
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/validate/800000000000000000000003',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {},
- log: {
- collection: 'measurements',
- dataAdd: {
- status: 'validated'
- }
- }
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/validate/800000000000000000000003',
- auth: {key: 'admin'},
- httpStatus: 401,
- req: {}
- });
- });
- it('rejects a write user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/validate/800000000000000000000003',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {}
- });
- });
- it('returns 404 for an unknown sample', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/validate/000000000000000000000003',
- auth: {basic: 'admin'},
- httpStatus: 404,
- req: {}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/measurement/validate/800000000000000000000003',
- httpStatus: 401,
- req: {}
- });
- });
- });
+ describe('PUT /measurement/validate/{id}', () => {
+ it('sets the status', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/validate/800000000000000000000003',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ MeasurementModel.findById('800000000000000000000003').lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).have.property('status','validated');
+ done();
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/validate/800000000000000000000003',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {},
+ log: {
+ collection: 'measurements',
+ dataAdd: {
+ status: 'validated'
+ }
+ }
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/validate/800000000000000000000003',
+ auth: {key: 'admin'},
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ it('rejects a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/validate/800000000000000000000003',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {}
+ });
+ });
+ it('returns 404 for an unknown sample', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/validate/000000000000000000000003',
+ auth: {basic: 'admin'},
+ httpStatus: 404,
+ req: {}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/measurement/validate/800000000000000000000003',
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ });
- describe('POST /measurement/new', () => {
- it('returns the right measurement', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).have.only.keys('_id', 'sample_id', 'values', 'measurement_template');
- should(res.body).have.property('_id').be.type('string');
- should(res.body).have.property('sample_id', '400000000000000000000001');
- should(res.body).have.property('measurement_template', '300000000000000000000002');
- should(res.body).have.property('values');
- should(res.body.values).have.property('weight %', 0.8);
- should(res.body.values).have.property('standard deviation', 0.1);
- done();
- });
- });
- it('stores the measurement', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'}
- }).end((err, res) => {
- if (err) return done(err);
- MeasurementModel.findById(res.body._id).lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).have.only.keys('_id', 'sample_id', 'values', 'measurement_template', 'status', '__v');
- should(data.sample_id.toString()).be.eql('400000000000000000000001');
- should(data.measurement_template.toString()).be.eql('300000000000000000000002');
- should(data).have.property('status', 'new');
- should(data).have.property('values');
- should(data.values).have.property('weight %', 0.8);
- should(data.values).have.property('standard deviation', 0.1);
- done();
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'},
- log: {
- collection: 'measurements',
- dataAdd: {
- status: 'new'
- }
- }
- });
- });
- it('rejects an invalid sample id', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {sample_id: '400000000000h00000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'},
- res: {status: 'Invalid body format', details: 'Invalid object id'}
- });
- });
- it('rejects a sample id not available', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {sample_id: '000000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'},
- res: {status: 'Sample id not available'}
- });
- });
- it('rejects an invalid measurement_template id', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '30000000000h000000000002'},
- res: {status: 'Invalid body format', details: 'Invalid object id'}
- });
- });
- it('rejects a measurement_template not available', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '000000000000000000000002'},
- res: {status: 'Measurement template not available'}
- });
- });
- it('rejects not specified values', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1, xx: 44}, measurement_template: '300000000000000000000002'},
- res: {status: 'Invalid body format', details: '"xx" is not allowed'}
- });
- });
- it('accepts missing values', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8}, measurement_template: '300000000000000000000002'}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).have.only.keys('_id', 'sample_id', 'values', 'measurement_template');
- should(res.body).have.property('_id').be.type('string');
- should(res.body).have.property('sample_id', '400000000000000000000001');
- should(res.body).have.property('measurement_template', '300000000000000000000002');
- should(res.body).have.property('values');
- should(res.body.values).have.property('weight %', 0.8);
- should(res.body.values).have.property('standard deviation', null);
- done();
- });
- });
- it('rejects no values', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {sample_id: '400000000000000000000001', values: {}, measurement_template: '300000000000000000000002'},
- res: {status: 'At least one value is required'}
- });
- });
- it('rejects a value not in the value range', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {sample_id: '400000000000000000000001', values: {val2: 5}, measurement_template: '300000000000000000000004'},
- res: {status: 'Invalid body format', details: '"val2" must be one of [1, 2, 3, 4, null]'}
- });
- });
- it('rejects a value below minimum range', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {sample_id: '400000000000000000000001', values: {'weight %': -1, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'},
- res: {status: 'Invalid body format', details: '"weight %" must be greater than or equal to 0'}
- });
- });
- it('rejects a value above maximum range', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 2}, measurement_template: '300000000000000000000002'},
- res: {status: 'Invalid body format', details: '"standard deviation" must be less than or equal to 0.5'}
- });
- });
- it('rejects a missing sample id', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'},
- res: {status: 'Invalid body format', details: '"sample_id" is required'}
- });
- });
- it('rejects a missing measurement_template', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}},
- res: {status: 'Invalid body format', details: '"measurement_template" is required'}
- });
- });
- it('rejects adding a measurement to the sample of another user for a write user', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {sample_id: '400000000000000000000003', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'}
- });
- });
- it('accepts adding a measurement to the sample of another user for a dev/admin user', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).have.only.keys('_id', 'sample_id', 'values', 'measurement_template');
- should(res.body).have.property('_id').be.type('string');
- should(res.body).have.property('sample_id', '400000000000000000000001');
- should(res.body).have.property('measurement_template', '300000000000000000000002');
- should(res.body).have.property('values');
- should(res.body.values).have.property('weight %', 0.8);
- should(res.body.values).have.property('standard deviation', 0.1);
- done();
- });
- });
- it('rejects an old version of a measurement template', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {sample_id: '400000000000000000000001', values: {val1: 2}, measurement_template: '300000000000000000000003'},
- res: {status: 'Old template version not allowed'}
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {key: 'janedoe'},
- httpStatus: 401,
- req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'}
- });
- });
- it('rejects requests from a read user', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- auth: {basic: 'user'},
- httpStatus: 403,
- req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/measurement/new',
- httpStatus: 401,
- req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'}
- });
- });
- });
-});
\ No newline at end of file
+ describe('POST /measurement/new', () => {
+ it('returns the right measurement', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).have.only.keys('_id', 'sample_id', 'values', 'measurement_template');
+ should(res.body).have.property('_id').be.type('string');
+ should(res.body).have.property('sample_id', '400000000000000000000001');
+ should(res.body).have.property('measurement_template', '300000000000000000000002');
+ should(res.body).have.property('values');
+ should(res.body.values).have.property('weight %', 0.8);
+ should(res.body.values).have.property('standard deviation', 0.1);
+ done();
+ });
+ });
+ it('stores the measurement', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'}
+ }).end((err, res) => {
+ if (err) return done(err);
+ MeasurementModel.findById(res.body._id).lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).have.only.keys('_id', 'sample_id', 'values', 'measurement_template', 'status', '__v');
+ should(data.sample_id.toString()).be.eql('400000000000000000000001');
+ should(data.measurement_template.toString()).be.eql('300000000000000000000002');
+ should(data).have.property('status', 'new');
+ should(data).have.property('values');
+ should(data.values).have.property('weight %', 0.8);
+ should(data.values).have.property('standard deviation', 0.1);
+ done();
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'},
+ log: {
+ collection: 'measurements',
+ dataAdd: {
+ status: 'new'
+ }
+ }
+ });
+ });
+ it('rejects an invalid sample id', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {sample_id: '400000000000h00000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'},
+ res: {status: 'Invalid body format', details: 'Invalid object id'}
+ });
+ });
+ it('rejects a sample id not available', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {sample_id: '000000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'},
+ res: {status: 'Sample id not available'}
+ });
+ });
+ it('rejects an invalid measurement_template id', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '30000000000h000000000002'},
+ res: {status: 'Invalid body format', details: 'Invalid object id'}
+ });
+ });
+ it('rejects a measurement_template not available', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '000000000000000000000002'},
+ res: {status: 'Measurement template not available'}
+ });
+ });
+ it('rejects not specified values', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1, xx: 44}, measurement_template: '300000000000000000000002'},
+ res: {status: 'Invalid body format', details: '"xx" is not allowed'}
+ });
+ });
+ it('accepts missing values', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8}, measurement_template: '300000000000000000000002'}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).have.only.keys('_id', 'sample_id', 'values', 'measurement_template');
+ should(res.body).have.property('_id').be.type('string');
+ should(res.body).have.property('sample_id', '400000000000000000000001');
+ should(res.body).have.property('measurement_template', '300000000000000000000002');
+ should(res.body).have.property('values');
+ should(res.body.values).have.property('weight %', 0.8);
+ should(res.body.values).have.property('standard deviation', null);
+ done();
+ });
+ });
+ it('rejects no values', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {sample_id: '400000000000000000000001', values: {}, measurement_template: '300000000000000000000002'},
+ res: {status: 'At least one value is required'}
+ });
+ });
+ it('rejects a value not in the value range', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {sample_id: '400000000000000000000001', values: {val2: 5}, measurement_template: '300000000000000000000004'},
+ res: {status: 'Invalid body format', details: '"val2" must be one of [1, 2, 3, 4, null]'}
+ });
+ });
+ it('rejects a value below minimum range', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {sample_id: '400000000000000000000001', values: {'weight %': -1, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'},
+ res: {status: 'Invalid body format', details: '"weight %" must be greater than or equal to 0'}
+ });
+ });
+ it('rejects a value above maximum range', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 2}, measurement_template: '300000000000000000000002'},
+ res: {status: 'Invalid body format', details: '"standard deviation" must be less than or equal to 0.5'}
+ });
+ });
+ it('rejects a missing sample id', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'},
+ res: {status: 'Invalid body format', details: '"sample_id" is required'}
+ });
+ });
+ it('rejects a missing measurement_template', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}},
+ res: {status: 'Invalid body format', details: '"measurement_template" is required'}
+ });
+ });
+ it('rejects adding a measurement to the sample of another user for a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {sample_id: '400000000000000000000003', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'}
+ });
+ });
+ it('accepts adding a measurement to the sample of another user for a dev/admin user', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).have.only.keys('_id', 'sample_id', 'values', 'measurement_template');
+ should(res.body).have.property('_id').be.type('string');
+ should(res.body).have.property('sample_id', '400000000000000000000001');
+ should(res.body).have.property('measurement_template', '300000000000000000000002');
+ should(res.body).have.property('values');
+ should(res.body.values).have.property('weight %', 0.8);
+ should(res.body.values).have.property('standard deviation', 0.1);
+ done();
+ });
+ });
+ it('rejects an old version of a measurement template', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {sample_id: '400000000000000000000001', values: {val1: 2}, measurement_template: '300000000000000000000003'},
+ res: {status: 'Old template version not allowed'}
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {key: 'janedoe'},
+ httpStatus: 401,
+ req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'}
+ });
+ });
+ it('rejects requests from a read user', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ auth: {basic: 'user'},
+ httpStatus: 403,
+ req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/measurement/new',
+ httpStatus: 401,
+ req: {sample_id: '400000000000000000000001', values: {'weight %': 0.8, 'standard deviation': 0.1}, measurement_template: '300000000000000000000002'}
+ });
+ });
+ });
+});
diff --git a/src/routes/measurement.ts b/src/routes/measurement.ts
index 701cf8a..863a067 100644
--- a/src/routes/measurement.ts
+++ b/src/routes/measurement.ts
@@ -16,114 +16,114 @@ import mongoose from "mongoose";
const router = express.Router();
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) => {
- if (err) return next(err);
- if (!data) {
- return res.status(404).json({status: 'Not found'});
- }
- // deleted measurements only available for dev/admin
- if (data.status === globals.status.del && !req.auth(res, ['dev', 'admin'], 'all')) return;
+ MeasurementModel.findById(req.params.id).lean().exec((err, data: any) => {
+ if (err) return next(err);
+ if (!data) {
+ return res.status(404).json({status: 'Not found'});
+ }
+ // deleted measurements only available for dev/admin
+ 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) => {
- 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');
- if (error) return res400(error, res);
+ const {error, value: measurement} = MeasurementValidate.input(req.body, 'change');
+ if (error) return res400(error, res);
- const data = await MeasurementModel.findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
- if (data instanceof Error) return;
- if (!data) {
- return res.status(404).json({status: 'Not found'});
- }
- if (data.status === 'deleted') {
- return res.status(403).json({status: 'Forbidden'});
- }
+ const data = await MeasurementModel.findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
+ if (data instanceof Error) return;
+ if (!data) {
+ return res.status(404).json({status: 'Not found'});
+ }
+ if (data.status === 'deleted') {
+ return res.status(403).json({status: 'Forbidden'});
+ }
- // add properties needed for sampleIdCheck
- measurement.measurement_template = data.measurement_template;
- measurement.sample_id = data.sample_id;
- if (!await sampleIdCheck(measurement, req, res, next)) return;
+ // add properties needed for sampleIdCheck
+ measurement.measurement_template = data.measurement_template;
+measurement.sample_id = data.sample_id;
+if (!await sampleIdCheck(measurement, req, res, next)) return;
- // check for changes
- if (measurement.values) { // fill not changed values from database
- measurement.values = _.assign({}, data.values, measurement.values);
- if (!_.isEqual(measurement.values, data.values)) {
- measurement.status = globals.status.new; // set status to new
- }
- }
+// check for changes
+if (measurement.values) { // fill not changed values from database
+ measurement.values = _.assign({}, data.values, measurement.values);
+ if (!_.isEqual(measurement.values, data.values)) {
+ measurement.status = globals.status.new; // set status to new
+ }
+}
- if (!await templateCheck(measurement, 'change', res, next)) return;
- await MeasurementModel.findByIdAndUpdate(req.params.id, measurement, {new: true})
- .log(req).lean().exec((err, data) => {
- if (err) return next(err);
- res.json(MeasurementValidate.output(data, req));
- });
+if (!await templateCheck(measurement, 'change', res, next)) return;
+await MeasurementModel.findByIdAndUpdate(req.params.id, measurement, {new: true})
+.log(req).lean().exec((err, data) => {
+ if (err) return next(err);
+ res.json(MeasurementValidate.output(data, req));
+});
});
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) => {
- if (err) return next(err);
- if (!data) {
- return res.status(404).json({status: 'Not found'});
- }
- if (!await sampleIdCheck(data, req, res, next)) return;
- await MeasurementModel.findByIdAndUpdate(req.params.id, {status: globals.status.del})
- .log(req).lean().exec(err => {
- if (err) return next(err);
- return res.json({status: 'OK'});
- });
- });
+ MeasurementModel.findById(req.params.id).lean().exec(async (err, data) => {
+ if (err) return next(err);
+ if (!data) {
+ return res.status(404).json({status: 'Not found'});
+ }
+ if (!await sampleIdCheck(data, req, res, next)) return;
+ await MeasurementModel.findByIdAndUpdate(req.params.id, {status: globals.status.del})
+ .log(req).lean().exec(err => {
+ if (err) return next(err);
+ return res.json({status: 'OK'});
+ });
+ });
});
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) => {
- if (err) return next(err);
- if (!data.length) {
- return res.status(404).json({status: 'Not found'});
- }
+ MeasurementModel.find({sample_id: mongoose.Types.ObjectId(req.params.id)}).lean().exec((err, data: any) => {
+ if (err) return next(err);
+ if (!data.length) {
+ 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) => {
- 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) => {
- 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) => {
- 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');
- if (error) return res400(error, res);
+ const {error, value: measurement} = MeasurementValidate.input(req.body, 'new');
+ if (error) return res400(error, res);
- if (!await sampleIdCheck(measurement, req, res, next)) return;
- measurement.values = await templateCheck(measurement, 'new', res, next);
- if (!measurement.values) return;
+ if (!await sampleIdCheck(measurement, req, res, next)) return;
+ measurement.values = await templateCheck(measurement, 'new', res, next);
+ if (!measurement.values) return;
- measurement.status = globals.status.new;
- await new MeasurementModel(measurement).save((err, data) => {
- if (err) return next(err);
- db.log(req, 'measurements', {_id: data._id}, data.toObject());
- res.json(MeasurementValidate.output(data.toObject(), req));
- });
+ measurement.status = globals.status.new;
+ await new MeasurementModel(measurement).save((err, data) => {
+ if (err) return next(err);
+ db.log(req, 'measurements', {_id: data._id}, data.toObject());
+ 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
async function sampleIdCheck (measurement, req, res, next) {
- const sampleData = await SampleModel.findById(measurement.sample_id)
- .lean().exec().catch(err => {next(err); return false;}) as any;
- if (!sampleData) { // sample_id not found
- res.status(400).json({status: 'Sample id not available'});
- return false
- }
- // sample does not belong to user
- return !(sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['dev', 'admin'], 'basic'));
+ const sampleData = await SampleModel.findById(measurement.sample_id)
+ .lean().exec().catch(err => {next(err); return false;}) as any;
+ if (!sampleData) { // sample_id not found
+ res.status(400).json({status: 'Sample id not available'});
+ return false
+ }
+ // sample does not belong to user
+ 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,
// param for 'new'/'change'
async function templateCheck (measurement, param, res, next) {
- const templateData = await MeasurementTemplateModel.findById(measurement.measurement_template)
- .lean().exec().catch(err => {next(err); return false;}) as any;
- if (!templateData) { // template not found
- res.status(400).json({status: 'Measurement template not available'});
- return false
- }
+ const templateData = await MeasurementTemplateModel.findById(measurement.measurement_template)
+ .lean().exec().catch(err => {next(err); return false;}) as any;
+ if (!templateData) { // template not found
+ res.status(400).json({status: 'Measurement template not available'});
+ return false
+ }
- // fill not given values for new measurements
- if (param === 'new') {
- // get all template versions and check if given is latest
- const templateVersions = await MeasurementTemplateModel.find({first_id: templateData.first_id}).sort({version: -1})
- .lean().exec().catch(err => next(err)) as any;
- if (templateVersions instanceof Error) return false;
- if (measurement.measurement_template !== templateVersions[0]._id.toString()) { // template not latest
- res.status(400).json({status: 'Old template version not allowed'});
- return false;
- }
+ // fill not given values for new measurements
+ if (param === 'new') {
+ // get all template versions and check if given is latest
+ const templateVersions = await MeasurementTemplateModel.find({first_id: templateData.first_id}).sort({version: -1})
+ .lean().exec().catch(err => next(err)) as any;
+ if (templateVersions instanceof Error) return false;
+ if (measurement.measurement_template !== templateVersions[0]._id.toString()) { // template not latest
+ res.status(400).json({status: 'Old template version not allowed'});
+ return false;
+ }
- if (Object.keys(measurement.values).length === 0) {
- res.status(400).json({status: 'At least one value is required'});
- return false
- }
- const fillValues = {}; // initialize not given values with null
- templateData.parameters.forEach(parameter => {
- fillValues[parameter.name] = null;
- });
- measurement.values = _.assign({}, fillValues, measurement.values);
- }
+ if (Object.keys(measurement.values).length === 0) {
+ res.status(400).json({status: 'At least one value is required'});
+ return false
+ }
+ const fillValues = {}; // initialize not given values with null
+ templateData.parameters.forEach(parameter => {
+ fillValues[parameter.name] = null;
+ });
+ measurement.values = _.assign({}, fillValues, measurement.values);
+ }
- // validate values
- const {error, value} = ParametersValidate.input(measurement.values, templateData.parameters, 'null');
- if (error) {res400(error, res); return false;}
- return value || true;
+ // validate values
+ const {error, value} = ParametersValidate.input(measurement.values, templateData.parameters, 'null');
+ if (error) {res400(error, res); return false;}
+ return value || true;
}
function setStatus (status, req, res, next) { // set measurement status
- MeasurementModel.findByIdAndUpdate(req.params.id, {status: status}).log(req).lean().exec((err, data) => {
- if (err) return next(err);
+ MeasurementModel.findByIdAndUpdate(req.params.id, {status: status}).log(req).lean().exec((err, data) => {
+ if (err) return next(err);
- if (!data) {
- return res.status(404).json({status: 'Not found'});
- }
- res.json({status: 'OK'});
- });
-}
\ No newline at end of file
+ if (!data) {
+ return res.status(404).json({status: 'Not found'});
+ }
+ res.json({status: 'OK'});
+ });
+}
diff --git a/src/routes/model.spec.ts b/src/routes/model.spec.ts
index 88a963e..66820e9 100644
--- a/src/routes/model.spec.ts
+++ b/src/routes/model.spec.ts
@@ -7,542 +7,542 @@ import mongoose from 'mongoose';
describe('/model', () => {
- let server;
- before(done => TestHelper.before(done));
- beforeEach(done => server = TestHelper.beforeEach(server, done));
- afterEach(done => TestHelper.afterEach(server, done));
- after(done => TestHelper.after(done));
+ let server;
+ before(done => TestHelper.before(done));
+ beforeEach(done => server = TestHelper.beforeEach(server, done));
+ afterEach(done => TestHelper.afterEach(server, done));
+ after(done => TestHelper.after(done));
- describe('GET /model/groups', () => {
- it('returns all groups for an admin user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/groups',
- auth: {basic: 'admin'},
- httpStatus: 200,
- }).end((err, res) => {
- if (err) return done (err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.models.length);
- should(res.body).matchEach(group => {
- should(group).have.only.keys('group', 'models');
- should(group).have.property('group').be.type('string');
- should(group.models).matchEach(model => {
- should(model).have.only.keys('_id', 'name', 'url');
- should(model).have.property('_id').be.type('string');
- should(model).have.property('name').be.type('string');
- should(model).have.property('url').be.type('string');
- });
- });
- done();
- });
- });
- it('returns all allowed groups for a predict user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/groups',
- auth: {basic: 'customer'},
- httpStatus: 200,
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).have.lengthOf(1);
- should(res.body).matchEach(group => {
- should(group).have.only.keys('group', 'models');
- should(group).have.property('group').be.type('string');
- should(group).have.property('models', [{_id: '120000000000000000000001', name: 'Model A', url: 'http://model-a.com'}]);
- });
- done();
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/groups',
- auth: {key: 'janedoe'},
- httpStatus: 401,
- });
- });
- it('rejects an unauthorized request', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/groups',
- httpStatus: 401,
- });
- });
- });
+ describe('GET /model/groups', () => {
+ it('returns all groups for an admin user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/groups',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ }).end((err, res) => {
+ if (err) return done (err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.models.length);
+ should(res.body).matchEach(group => {
+ should(group).have.only.keys('group', 'models');
+ should(group).have.property('group').be.type('string');
+ should(group.models).matchEach(model => {
+ should(model).have.only.keys('_id', 'name', 'url');
+ should(model).have.property('_id').be.type('string');
+ should(model).have.property('name').be.type('string');
+ should(model).have.property('url').be.type('string');
+ });
+ });
+ done();
+ });
+ });
+ it('returns all allowed groups for a predict user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/groups',
+ auth: {basic: 'customer'},
+ httpStatus: 200,
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).have.lengthOf(1);
+ should(res.body).matchEach(group => {
+ should(group).have.only.keys('group', 'models');
+ should(group).have.property('group').be.type('string');
+ should(group).have.property('models', [{_id: '120000000000000000000001', name: 'Model A', url: 'http://model-a.com'}]);
+ });
+ done();
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/groups',
+ auth: {key: 'janedoe'},
+ httpStatus: 401,
+ });
+ });
+ it('rejects an unauthorized request', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/groups',
+ httpStatus: 401,
+ });
+ });
+ });
- describe('POST /model/{group}', () => {
- it('adds a new model', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/model/VN',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {name: 'Model C', url: 'http://model-c.com'}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- ModelModel.findOne({group: 'VN'}).lean().exec((err, res) => {
- if (err) return done(err);
- const model = res.models.find(e => e.name === 'Model C');
- should(model).have.property('url', 'http://model-c.com');
- done();
- });
- });
- });
- it('adds a new group', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/model/classification',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {name: 'Model 0.1', url: 'http://model-0-1.com'}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- ModelModel.findOne({group: 'classification'}).lean().exec((err, res) => {
- if (err) return done(err);
- should(res).have.only.keys('_id', 'group', 'models', '__v');
- should(res).have.property('group', 'classification');
- should(res.models[0]).have.only.keys('_id', 'name', 'url');
- should(res.models[0]).have.property('name', 'Model 0.1');
- should(res.models[0]).have.property('url', 'http://model-0-1.com');
- done();
- });
- });
- });
- it('replaces a model', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/model/VN',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {name: 'Model A', url: 'http://model-a-new.com'}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- ModelModel.findOne({group: 'VN'}).lean().exec((err, res) => {
- if (err) return done(err);
- const model = res.models.find(e => e.name === 'Model A');
- should(model).have.property('url', 'http://model-a-new.com');
- done();
- });
- });
- });
- it('rejects an empty name', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/model/VN',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {name: '', url: 'http://model-c.com'},
- res:{status: 'Invalid body format', details: '"name" is not allowed to be empty'}
- });
- });
- it('rejects a missing name', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/model/VN',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {url: 'http://model-c.com'},
- res:{status: 'Invalid body format', details: '"name" is required'}
- });
- });
- it('rejects an invalid URL', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/model/VN',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {name: 'Model C', url: 'model-c'},
- res:{status: 'Invalid body format', details: '"url" must be a valid uri'}
- });
- });
- it('rejects a missing URL', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/model/VN',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {name: 'Model C'},
- res:{status: 'Invalid body format', details: '"url" is required'}
- });
- });
- it('rejects a write user', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/model/VN',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {name: 'Model C', url: 'http://model-c.com'}
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/model/VN',
- auth: {key: 'admin'},
- httpStatus: 401,
- req: {name: 'Model C', url: 'http://model-c.com'}
- });
- });
- it('rejects an unauthorized request', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/model/VN',
- httpStatus: 401,
- req: {name: 'Model C', url: 'http://model-c.com'}
- });
- });
- });
+ describe('POST /model/{group}', () => {
+ it('adds a new model', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/model/VN',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {name: 'Model C', url: 'http://model-c.com'}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ ModelModel.findOne({group: 'VN'}).lean().exec((err, res) => {
+ if (err) return done(err);
+ const model = res.models.find(e => e.name === 'Model C');
+ should(model).have.property('url', 'http://model-c.com');
+ done();
+ });
+ });
+ });
+ it('adds a new group', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/model/classification',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {name: 'Model 0.1', url: 'http://model-0-1.com'}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ ModelModel.findOne({group: 'classification'}).lean().exec((err, res) => {
+ if (err) return done(err);
+ should(res).have.only.keys('_id', 'group', 'models', '__v');
+ should(res).have.property('group', 'classification');
+ should(res.models[0]).have.only.keys('_id', 'name', 'url');
+ should(res.models[0]).have.property('name', 'Model 0.1');
+ should(res.models[0]).have.property('url', 'http://model-0-1.com');
+ done();
+ });
+ });
+ });
+ it('replaces a model', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/model/VN',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {name: 'Model A', url: 'http://model-a-new.com'}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ ModelModel.findOne({group: 'VN'}).lean().exec((err, res) => {
+ if (err) return done(err);
+ const model = res.models.find(e => e.name === 'Model A');
+ should(model).have.property('url', 'http://model-a-new.com');
+ done();
+ });
+ });
+ });
+ it('rejects an empty name', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/model/VN',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {name: '', url: 'http://model-c.com'},
+ res:{status: 'Invalid body format', details: '"name" is not allowed to be empty'}
+ });
+ });
+ it('rejects a missing name', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/model/VN',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {url: 'http://model-c.com'},
+ res:{status: 'Invalid body format', details: '"name" is required'}
+ });
+ });
+ it('rejects an invalid URL', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/model/VN',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {name: 'Model C', url: 'model-c'},
+ res:{status: 'Invalid body format', details: '"url" must be a valid uri'}
+ });
+ });
+ it('rejects a missing URL', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/model/VN',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {name: 'Model C'},
+ res:{status: 'Invalid body format', details: '"url" is required'}
+ });
+ });
+ it('rejects a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/model/VN',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {name: 'Model C', url: 'http://model-c.com'}
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/model/VN',
+ auth: {key: 'admin'},
+ httpStatus: 401,
+ req: {name: 'Model C', url: 'http://model-c.com'}
+ });
+ });
+ it('rejects an unauthorized request', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/model/VN',
+ httpStatus: 401,
+ req: {name: 'Model C', url: 'http://model-c.com'}
+ });
+ });
+ });
- describe('DELETE /model/{group}/{name}', () => {
- it('deletes the model', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/model/VN/Model%20A',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- ModelModel.findOne({group: 'VN'}).lean().exec((err, res) => {
- if (err) return done(err);
- should(res).have.only.keys('_id', 'group', 'models');
- should(res).have.property('group', 'VN');
- should(res.models[0]).have.only.keys('_id', 'name', 'url');
- should(res.models[0]).have.property('name', 'Model B');
- should(res.models[0]).have.property('url', 'http://model-b.com');
- done();
- });
- });
- });
- it('deletes the group, if empty afterwards', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/model/Moisture/Model%201',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- ModelModel.find({group: 'Moisture'}).lean().exec((err, res) => {
- if (err) return done(err);
- should(res).have.lengthOf(0);
- done();
- });
- });
- });
- it ('removes the model_id from all user.models', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/model/VN/Model%20A',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- UserModel.find({models: mongoose.Types.ObjectId("120000000000000000000001")}).lean().exec((err, res) => {
- if (err) return done(err);
- should(res).have.lengthOf(0);
- done();
- });
- });
- });
- it('returns 404 for an unknown group', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/model/xxx/Model%201',
- auth: {basic: 'admin'},
- httpStatus: 404
- });
- });
- it('returns 404 for an unknown model', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/model/VN/xxx',
- auth: {basic: 'admin'},
- httpStatus: 404
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/model/VN/Model%20A',
- auth: {key: 'admin'},
- httpStatus: 401
- });
- });
- it('rejects a write user', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/model/VN/Model%20A',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('rejects an unauthorized request', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/model/VN/Model%20A',
- httpStatus: 401
- });
- });
- });
+ describe('DELETE /model/{group}/{name}', () => {
+ it('deletes the model', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/model/VN/Model%20A',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ ModelModel.findOne({group: 'VN'}).lean().exec((err, res) => {
+ if (err) return done(err);
+ should(res).have.only.keys('_id', 'group', 'models');
+ should(res).have.property('group', 'VN');
+ should(res.models[0]).have.only.keys('_id', 'name', 'url');
+ should(res.models[0]).have.property('name', 'Model B');
+ should(res.models[0]).have.property('url', 'http://model-b.com');
+ done();
+ });
+ });
+ });
+ it('deletes the group, if empty afterwards', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/model/Moisture/Model%201',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ ModelModel.find({group: 'Moisture'}).lean().exec((err, res) => {
+ if (err) return done(err);
+ should(res).have.lengthOf(0);
+ done();
+ });
+ });
+ });
+ it ('removes the model_id from all user.models', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/model/VN/Model%20A',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ UserModel.find({models: mongoose.Types.ObjectId("120000000000000000000001")}).lean().exec((err, res) => {
+ if (err) return done(err);
+ should(res).have.lengthOf(0);
+ done();
+ });
+ });
+ });
+ it('returns 404 for an unknown group', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/model/xxx/Model%201',
+ auth: {basic: 'admin'},
+ httpStatus: 404
+ });
+ });
+ it('returns 404 for an unknown model', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/model/VN/xxx',
+ auth: {basic: 'admin'},
+ httpStatus: 404
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/model/VN/Model%20A',
+ auth: {key: 'admin'},
+ httpStatus: 401
+ });
+ });
+ it('rejects a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/model/VN/Model%20A',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('rejects an unauthorized request', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/model/VN/Model%20A',
+ httpStatus: 401
+ });
+ });
+ });
- describe('GET /model/files', () => {
- it('rejects a write user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/files',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/files',
- auth: {key: 'admin'},
- httpStatus: 401,
- });
- });
- it('rejects an unauthorized request', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/files',
- httpStatus: 401,
- });
- });
- });
+ describe('GET /model/files', () => {
+ it('rejects a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/files',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/files',
+ auth: {key: 'admin'},
+ httpStatus: 401,
+ });
+ });
+ it('rejects an unauthorized request', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/files',
+ httpStatus: 401,
+ });
+ });
+ });
- describe('GET /model/file/{name}', (() => {
- it('returns the binary data', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/file/modela',
- auth: {basic: 'admin'},
- httpStatus: 200,
- contentType: 'application/octet-stream; charset=utf-8',
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body.toString()).be.eql('binary data');
- done();
- });
- });
- it('returns the binary data for an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/file/modela',
- auth: {key: 'admin'},
- httpStatus: 200,
- contentType: 'application/octet-stream; charset=utf-8',
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body.toString()).be.eql('binary data');
- done();
- });
- });
- it('returns 404 for an unknown name', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/file/modelx',
- auth: {basic: 'admin'},
- httpStatus: 404
- })
- });
- it('rejects requests from a write user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/file/modela',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- })
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/file/modela',
- httpStatus: 401
- })
- });
- }));
+ describe('GET /model/file/{name}', (() => {
+ it('returns the binary data', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/file/modela',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ contentType: 'application/octet-stream; charset=utf-8',
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body.toString()).be.eql('binary data');
+ done();
+ });
+ });
+ it('returns the binary data for an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/file/modela',
+ auth: {key: 'admin'},
+ httpStatus: 200,
+ contentType: 'application/octet-stream; charset=utf-8',
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body.toString()).be.eql('binary data');
+ done();
+ });
+ });
+ it('returns 404 for an unknown name', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/file/modelx',
+ auth: {basic: 'admin'},
+ httpStatus: 404
+ })
+ });
+ it('rejects requests from a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/file/modela',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ })
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/file/modela',
+ httpStatus: 401
+ })
+ });
+ }));
- describe('POST /model/file/{name}', () => {
- it('stores the data', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/model/file/modelb',
- auth: {basic: 'admin'},
- httpStatus: 200,
- reqContentType: 'application/octet-stream',
- req: 'another binary data'
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- ModelFileModel.find({name: 'modelb'}).lean().exec((err, data) => {
- if (err) return done (err);
- should(data).have.lengthOf(1);
- should(data[0]).have.only.keys('_id', 'name', 'data', '__v');
- should(data[0]).have.property('name', 'modelb');
- should(data[0].data.buffer.toString()).be.eql('another binary data');
- done();
- });
- });
- });
- it('stores the data with an API key', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/model/file/modelb',
- auth: {key: 'admin'},
- httpStatus: 200,
- reqContentType: 'application/octet-stream',
- req: 'another binary data'
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- ModelFileModel.find({name: 'modelb'}).lean().exec((err, data) => {
- if (err) return done (err);
- should(data).have.lengthOf(1);
- should(data[0]).have.only.keys('_id', 'name', 'data', '__v');
- should(data[0]).have.property('name', 'modelb');
- should(data[0].data.buffer.toString()).be.eql('another binary data');
- done();
- });
- });
- });
- it('overwrites existing data', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/model/file/modela',
- auth: {basic: 'admin'},
- httpStatus: 200,
- reqContentType: 'application/octet-stream',
- req: 'another binary data'
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- ModelFileModel.find({name: 'modela'}).lean().exec((err, data) => {
- if (err) return done (err);
- should(data).have.lengthOf(1);
- should(data[0]).have.only.keys('_id', 'name', 'data', '__v');
- should(data[0]).have.property('name', 'modela');
- should(data[0].data.buffer.toString()).be.eql('another binary data');
- done();
- });
- });
- });
- it('rejects requests from a write user', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/model/file/modelb',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: 'another binary data'
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/model/file/modelb',
- httpStatus: 401,
- req: 'another binary data'
- });
- });
- });
+ describe('POST /model/file/{name}', () => {
+ it('stores the data', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/model/file/modelb',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ reqContentType: 'application/octet-stream',
+ req: 'another binary data'
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ ModelFileModel.find({name: 'modelb'}).lean().exec((err, data) => {
+ if (err) return done (err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.only.keys('_id', 'name', 'data', '__v');
+ should(data[0]).have.property('name', 'modelb');
+ should(data[0].data.buffer.toString()).be.eql('another binary data');
+ done();
+ });
+ });
+ });
+ it('stores the data with an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/model/file/modelb',
+ auth: {key: 'admin'},
+ httpStatus: 200,
+ reqContentType: 'application/octet-stream',
+ req: 'another binary data'
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ ModelFileModel.find({name: 'modelb'}).lean().exec((err, data) => {
+ if (err) return done (err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.only.keys('_id', 'name', 'data', '__v');
+ should(data[0]).have.property('name', 'modelb');
+ should(data[0].data.buffer.toString()).be.eql('another binary data');
+ done();
+ });
+ });
+ });
+ it('overwrites existing data', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/model/file/modela',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ reqContentType: 'application/octet-stream',
+ req: 'another binary data'
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ ModelFileModel.find({name: 'modela'}).lean().exec((err, data) => {
+ if (err) return done (err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.only.keys('_id', 'name', 'data', '__v');
+ should(data[0]).have.property('name', 'modela');
+ should(data[0].data.buffer.toString()).be.eql('another binary data');
+ done();
+ });
+ });
+ });
+ it('rejects requests from a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/model/file/modelb',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: 'another binary data'
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/model/file/modelb',
+ httpStatus: 401,
+ req: 'another binary data'
+ });
+ });
+ });
- describe('DELETE /model/file/{name}', () => {
- it('deletes the data', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/model/file/modela',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- ModelFileModel.find({name: 'modela'}).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(0);
- done();
- });
- });
- });
- it('returns 404 for an unknown name', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/model/file/modelx',
- auth: {basic: 'admin'},
- httpStatus: 404
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/model/file/modela',
- auth: {key: 'admin'},
- httpStatus: 401
- });
- });
- it('rejects a write user', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/model/file/modela',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('rejects an unauthorized request', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/model/file/modela',
- httpStatus: 401
- });
- });
- });
+ describe('DELETE /model/file/{name}', () => {
+ it('deletes the data', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/model/file/modela',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ ModelFileModel.find({name: 'modela'}).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(0);
+ done();
+ });
+ });
+ });
+ it('returns 404 for an unknown name', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/model/file/modelx',
+ auth: {basic: 'admin'},
+ httpStatus: 404
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/model/file/modela',
+ auth: {key: 'admin'},
+ httpStatus: 401
+ });
+ });
+ it('rejects a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/model/file/modela',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('rejects an unauthorized request', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/model/file/modela',
+ httpStatus: 401
+ });
+ });
+ });
- describe('GET /model/authorized/{url}', () => {
- it('returns OK for every model for admins', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/authorized/xx',
- auth: {basic: 'admin'},
- httpStatus: 200,
- res: {status: 'OK'}
- });
- });
- it('returns OK for a specified model for a predict user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/authorized/http%3A%2F%2Fmodel-a.com',
- auth: {basic: 'customer'},
- httpStatus: 200,
- res: {status: 'OK'}
- });
- });
- it('rejects a model not specified for a predict user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/authorized/http%3A%2F%2Fmodel-b.com',
- auth: {basic: 'customer'},
- httpStatus: 403
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/authorized/xx',
- auth: {key: 'admin'},
- httpStatus: 401
- });
- });
- it('rejects an unauthorized request', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/model/authorized/http%3A%2F%2Fmodel-b.com',
- httpStatus: 401
- });
- });
- });
-});
\ No newline at end of file
+ describe('GET /model/authorized/{url}', () => {
+ it('returns OK for every model for admins', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/authorized/xx',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ res: {status: 'OK'}
+ });
+ });
+ it('returns OK for a specified model for a predict user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/authorized/http%3A%2F%2Fmodel-a.com',
+ auth: {basic: 'customer'},
+ httpStatus: 200,
+ res: {status: 'OK'}
+ });
+ });
+ it('rejects a model not specified for a predict user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/authorized/http%3A%2F%2Fmodel-b.com',
+ auth: {basic: 'customer'},
+ httpStatus: 403
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/authorized/xx',
+ auth: {key: 'admin'},
+ httpStatus: 401
+ });
+ });
+ it('rejects an unauthorized request', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/model/authorized/http%3A%2F%2Fmodel-b.com',
+ httpStatus: 401
+ });
+ });
+ });
+});
diff --git a/src/routes/model.ts b/src/routes/model.ts
index 70bf5ac..89a3f85 100644
--- a/src/routes/model.ts
+++ b/src/routes/model.ts
@@ -13,162 +13,162 @@ import mongoose from "mongoose";
const router = express.Router();
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 = [{}, {}];
- if (['dev', 'admin'].indexOf(req.authDetails.level) < 0) { // if not dev or admin, user has to possess model rights
- conditions = [
- {'models._id': {$in: req.authDetails.models.map(e => mongoose.Types.ObjectId(e))}},
- {group: true, 'models.$': true}
- ]
- }
- ModelModel.find(...conditions).lean().exec((err, data) => {
- if (err) return next(err);
+ let conditions: any = [{}, {}];
+ if (['dev', 'admin'].indexOf(req.authDetails.level) < 0) { // if not dev or admin, user has to possess model rights
+ conditions = [
+ {'models._id': {$in: req.authDetails.models.map(e => mongoose.Types.ObjectId(e))}},
+ {group: true, 'models.$': true}
+ ]
+ }
+ ModelModel.find(...conditions).lean().exec((err, data) => {
+ if (err) return next(err);
- // validate all and filter null values from validation errors
- res.json(_.compact(data.map(e => ModelValidate.output(e))));
- });
+ // validate all and filter null values from validation errors
+ res.json(_.compact(data.map(e => ModelValidate.output(e))));
+ });
});
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);
- if (error) return res400(error, res);
+ const {error, value: model} = ModelValidate.input(req.body);
+ if (error) return res400(error, res);
- ModelModel.findOne({group: req.params.group}).lean().exec((err, data) => {
- if (err) return next(err);
+ ModelModel.findOne({group: req.params.group}).lean().exec((err, data) => {
+ if (err) return next(err);
- if (data) { // group exists
- if (data.models.find(e => e.name === model.name)) { // name exists, overwrite
- ModelModel.findOneAndUpdate(
- {$and: [{group: req.params.group}, {'models.name': model.name}]},
- {'models.$': model},
- {upsert: true}).log(req).lean().exec(err => {
- if (err) return next(err);
- res.json({status: 'OK'})
- });
- }
- else { // create new
- ModelModel.findOneAndUpdate(
- {group: req.params.group},
- {$push: {models: model as never}}
- ).log(req).lean().exec(err => {
- if (err) return next(err);
- res.json({status: 'OK'});
- });
- }
- }
- else { // create new group
- new ModelModel({group: req.params.group, models: [model]}).save((err, data) => {
- if (err) return next(err);
- db.log(req, 'models', {_id: data._id}, data.toObject());
- res.json({status: 'OK'});
- });
- }
- });
+ if (data) { // group exists
+ if (data.models.find(e => e.name === model.name)) { // name exists, overwrite
+ ModelModel.findOneAndUpdate(
+ {$and: [{group: req.params.group}, {'models.name': model.name}]},
+ {'models.$': model},
+ {upsert: true}).log(req).lean().exec(err => {
+ if (err) return next(err);
+ res.json({status: 'OK'})
+ });
+ }
+ else { // create new
+ ModelModel.findOneAndUpdate(
+ {group: req.params.group},
+ {$push: {models: model as never}}
+ ).log(req).lean().exec(err => {
+ if (err) return next(err);
+ res.json({status: 'OK'});
+ });
+ }
+ }
+ else { // create new group
+ new ModelModel({group: req.params.group, models: [model]}).save((err, data) => {
+ if (err) return next(err);
+ db.log(req, 'models', {_id: data._id}, data.toObject());
+ res.json({status: 'OK'});
+ });
+ }
+ });
});
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) => {
- if (err) return next(err);
+ ModelModel.findOne({group: req.params.group}).lean().exec((err, data) => {
+ if (err) return next(err);
- if (!data || !data.models.find(e => e.name === req.params.name)) {
- return res.status(404).json({status: 'Not found'});
- }
- // delete all references in user.models
- UserModel.updateMany({}, {$pull: {models: data.models.find(e => e.name === req.params.name)._id as never}},
- { multi: true }).log(req).lean().exec(err => {
- if (err) return next(err);
- if (data.models.length > 1) { // only remove model
- ModelModel.findOneAndUpdate(
- {group: req.params.group},
- {$pull: {models: data.models.find(e => e.name === req.params.name) as never}}
- ).log(req).lean().exec(err => {
- if (err) return next(err);
- res.json({status: 'OK'})
- });
- }
- else { // remove document
- ModelModel.findOneAndDelete({group: req.params.group}).log(req).lean().exec(err => {
- if (err) return next(err);
- res.json({status: 'OK'})
- });
- }
- });
- });
+ if (!data || !data.models.find(e => e.name === req.params.name)) {
+ return res.status(404).json({status: 'Not found'});
+ }
+ // delete all references in user.models
+ UserModel.updateMany({}, {$pull: {models: data.models.find(e => e.name === req.params.name)._id as never}},
+ { multi: true }).log(req).lean().exec(err => {
+ if (err) return next(err);
+ if (data.models.length > 1) { // only remove model
+ ModelModel.findOneAndUpdate(
+ {group: req.params.group},
+ {$pull: {models: data.models.find(e => e.name === req.params.name) as never}}
+ ).log(req).lean().exec(err => {
+ if (err) return next(err);
+ res.json({status: 'OK'})
+ });
+ }
+ else { // remove document
+ ModelModel.findOneAndDelete({group: req.params.group}).log(req).lean().exec(err => {
+ if (err) return next(err);
+ res.json({status: 'OK'})
+ });
+ }
+ });
+ });
});
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) => {
- if (err) return next(err);
- res.json(data.map(e => ModelValidate.fileOutput(e)));
- });
+ ModelFileModel.find().exec((err, data) => {
+ if (err) return next(err);
+ res.json(data.map(e => ModelValidate.fileOutput(e)));
+ });
});
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) => {
- if (err) return next(err);
- if (data) {
- res.set('Content-Type', 'application/octet-stream');
- res.send(data.data.buffer);
- }
- else {
- res.status(404).json({status: 'Not found'});
- }
- });
+ ModelFileModel.findOne({name: req.params.name}).lean().exec((err, data) => {
+ if (err) return next(err);
+ if (data) {
+ res.set('Content-Type', 'application/octet-stream');
+ res.send(data.data.buffer);
+ }
+ else {
+ res.status(404).json({status: 'Not found'});
+ }
+ });
});
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})
- .lean().exec(err => {
- if (err) return next(err);
- res.json({status: 'OK'});
- });
+ ModelFileModel.replaceOne({name: req.params.name}, {name: req.params.name, data: req.body}).setOptions({upsert: true})
+ .lean().exec(err => {
+ if (err) return next(err);
+ res.json({status: 'OK'});
+ });
});
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) => {
- if (err) return next(err);
- if (data) {
- res.json({status: 'OK'});
- }
- else {
- res.status(404).json({status: 'Not found'});
- }
- });
+ ModelFileModel.findOneAndDelete({name: req.params.name}).lean().exec((err, data) => {
+ if (err) return next(err);
+ if (data) {
+ res.json({status: 'OK'});
+ }
+ else {
+ res.status(404).json({status: 'Not found'});
+ }
+ });
});
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
- ModelModel.findOne({models: { $elemMatch: {
- url: decodeURIComponent(req.params.url),
- '_id': {$in: req.authDetails.models.map(e => mongoose.Types.ObjectId(e))}
- }}}).lean().exec((err, data) => {
- if (err) return next(err);
- if (data) {
- res.json({status: 'OK'});
- }
- else {
- res.status(403).json({status: 'Forbidden'});
- }
- });
- }
- else {
- res.json({status: 'OK'});
- }
+ if (['dev', 'admin'].indexOf(req.authDetails.level) < 0) { // if not dev or admin, user has to possess model rights
+ ModelModel.findOne({models: { $elemMatch: {
+ url: decodeURIComponent(req.params.url),
+ '_id': {$in: req.authDetails.models.map(e => mongoose.Types.ObjectId(e))}
+ }}}).lean().exec((err, data) => {
+ if (err) return next(err);
+ if (data) {
+ res.json({status: 'OK'});
+ }
+ else {
+ res.status(403).json({status: 'Forbidden'});
+ }
+ });
+ }
+ else {
+ res.json({status: 'OK'});
+ }
});
-module.exports = router;
\ No newline at end of file
+module.exports = router;
diff --git a/src/routes/root.spec.ts b/src/routes/root.spec.ts
index 337fc77..d38546c 100644
--- a/src/routes/root.spec.ts
+++ b/src/routes/root.spec.ts
@@ -4,254 +4,254 @@ import db from '../db';
describe('/', () => {
- let server;
- before(done => TestHelper.before(done));
- beforeEach(done => server = TestHelper.beforeEach(server, done));
- afterEach(done => TestHelper.afterEach(server, done));
- after(done => TestHelper.after(done));
+ let server;
+ before(done => TestHelper.before(done));
+ beforeEach(done => server = TestHelper.beforeEach(server, done));
+ afterEach(done => TestHelper.afterEach(server, done));
+ after(done => TestHelper.after(done));
- describe('GET /', () => {
- it('returns the root message', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/',
- httpStatus: 200,
- res: {status: 'API server up and running!'}
- });
- });
- });
+ describe('GET /', () => {
+ it('returns the root message', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/',
+ httpStatus: 200,
+ res: {status: 'API server up and running!'}
+ });
+ });
+ });
- describe('GET /changelog/{timestamp}/{page}/{pagesize}', () => {
- it('returns the first page', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/changelog/120000030000000000000000/0/2',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).have.lengthOf(2);
- 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).matchEach(log => {
- should(log).have.only.keys('_id', 'date', 'action', 'collection', 'conditions', 'data');
- should(log).have.property('_id').be.type('string');
- should(log).have.property('action', 'PUT /sample/400000000000000000000001');
- should(log).have.property('collection', 'samples');
- should(log).have.property('conditions', {_id: '400000000000000000000001'});
- should(log).have.property('data', {type: 'processed', status: 0});
- });
- done();
- });
- });
- it('returns another page', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/changelog/120000030000000000000000/1/2',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).have.lengthOf(1);
- should(res.body[0].date).be.eql('1979-07-28T06:04:49.000Z');
- should(res.body).matchEach(log => {
- should(log).have.only.keys('_id', 'date', 'action', 'collection', 'conditions', 'data');
- should(log).have.property('_id').be.type('string');
- should(log).have.property('action', 'PUT /sample/400000000000000000000001');
- should(log).have.property('collection', 'samples');
- should(log).have.property('conditions', {_id: '400000000000000000000001'});
- should(log).have.property('data', {type: 'processed', status: 0});
- done();
- });
- });
- });
- it('returns an empty array for a page with no results', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/changelog/120000030000000000000000/10/2',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).have.lengthOf(0);
- done();
- });
- });
- it('rejects invalid ids', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/changelog/12000003000000h000000000/10/2',
- auth: {basic: 'admin'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: 'Invalid object id'}
- });
- });
- it('rejects negative page numbers', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/changelog/120000030000000000000000/-10/2',
- auth: {basic: 'admin'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: '"page" must be greater than or equal to 0'}
- });
- });
- it('rejects negative pagesizes', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/changelog/120000030000000000000000/10/-2',
- auth: {basic: 'admin'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: '"pagesize" must be greater than or equal to 0'}
- });
- });
- it('rejects request from a write user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/changelog/120000030000000000000000/10/2',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('rejects requests from an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/changelog/120000030000000000000000/10/2',
- auth: {key: 'admin'},
- httpStatus: 401
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/changelog/120000030000000000000000/10/2',
- httpStatus: 401
- });
- });
- });
+ describe('GET /changelog/{timestamp}/{page}/{pagesize}', () => {
+ it('returns the first page', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/changelog/120000030000000000000000/0/2',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).have.lengthOf(2);
+ 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).matchEach(log => {
+ should(log).have.only.keys('_id', 'date', 'action', 'collection', 'conditions', 'data');
+ should(log).have.property('_id').be.type('string');
+ should(log).have.property('action', 'PUT /sample/400000000000000000000001');
+ should(log).have.property('collection', 'samples');
+ should(log).have.property('conditions', {_id: '400000000000000000000001'});
+ should(log).have.property('data', {type: 'processed', status: 0});
+ });
+ done();
+ });
+ });
+ it('returns another page', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/changelog/120000030000000000000000/1/2',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).have.lengthOf(1);
+ should(res.body[0].date).be.eql('1979-07-28T06:04:49.000Z');
+ should(res.body).matchEach(log => {
+ should(log).have.only.keys('_id', 'date', 'action', 'collection', 'conditions', 'data');
+ should(log).have.property('_id').be.type('string');
+ should(log).have.property('action', 'PUT /sample/400000000000000000000001');
+ should(log).have.property('collection', 'samples');
+ should(log).have.property('conditions', {_id: '400000000000000000000001'});
+ should(log).have.property('data', {type: 'processed', status: 0});
+ done();
+ });
+ });
+ });
+ it('returns an empty array for a page with no results', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/changelog/120000030000000000000000/10/2',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).have.lengthOf(0);
+ done();
+ });
+ });
+ it('rejects invalid ids', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/changelog/12000003000000h000000000/10/2',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: 'Invalid object id'}
+ });
+ });
+ it('rejects negative page numbers', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/changelog/120000030000000000000000/-10/2',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: '"page" must be greater than or equal to 0'}
+ });
+ });
+ it('rejects negative pagesizes', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/changelog/120000030000000000000000/10/-2',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: '"pagesize" must be greater than or equal to 0'}
+ });
+ });
+ it('rejects request from a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/changelog/120000030000000000000000/10/2',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('rejects requests from an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/changelog/120000030000000000000000/10/2',
+ auth: {key: 'admin'},
+ httpStatus: 401
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/changelog/120000030000000000000000/10/2',
+ httpStatus: 401
+ });
+ });
+ });
- describe('Unknown routes', () => {
- it('return a 404 message', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/unknownroute',
- httpStatus: 404
- });
- });
- });
+ describe('Unknown routes', () => {
+ it('return a 404 message', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/unknownroute',
+ httpStatus: 404
+ });
+ });
+ });
- describe('An unauthorized request', () => {
- it('returns a 401 message', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/authorized',
- httpStatus: 401
- });
- });
- it('does not work with correct username', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/authorized',
- auth: {basic: {name: 'admin', pass: 'Abc123!!'}},
- httpStatus: 401
- });
- });
- it('does not work with incorrect username', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/authorized',
- auth: {basic: {name: 'adminxx', pass: 'Abc123!!'}},
- httpStatus: 401
- });
- });
- it('does not work with a deleted user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/authorized',
- auth: {basic: {name: 'customerold', pass: 'Xyz890*)'}},
- httpStatus: 401
- });
- });
- });
+ describe('An unauthorized request', () => {
+ it('returns a 401 message', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/authorized',
+ httpStatus: 401
+ });
+ });
+ it('does not work with correct username', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/authorized',
+ auth: {basic: {name: 'admin', pass: 'Abc123!!'}},
+ httpStatus: 401
+ });
+ });
+ it('does not work with incorrect username', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/authorized',
+ auth: {basic: {name: 'adminxx', pass: 'Abc123!!'}},
+ httpStatus: 401
+ });
+ });
+ it('does not work with a deleted user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/authorized',
+ auth: {basic: {name: 'customerold', pass: 'Xyz890*)'}},
+ httpStatus: 401
+ });
+ });
+ });
- describe('An authorized request', () => {
- it('works with an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/authorized',
- auth: {key: 'admin'},
- httpStatus: 200,
- res: {status: 'Authorization successful', method: 'key', level: 'admin', user_id: '000000000000000000000003'}
- });
- });
- it('works with basic auth', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/authorized',
- auth: {basic: 'admin'},
- httpStatus: 200,
- res: {status: 'Authorization successful', method: 'basic', level: 'admin', user_id: '000000000000000000000003'}
- });
- });
- });
+ describe('An authorized request', () => {
+ it('works with an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/authorized',
+ auth: {key: 'admin'},
+ httpStatus: 200,
+ res: {status: 'Authorization successful', method: 'key', level: 'admin', user_id: '000000000000000000000003'}
+ });
+ });
+ it('works with basic auth', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/authorized',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ res: {status: 'Authorization successful', method: 'basic', level: 'admin', user_id: '000000000000000000000003'}
+ });
+ });
+ });
- describe('An invalid JSON body', () => {
- it('is rejected', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/',
- httpStatus: 400,
- reqType: 'json',
- req: '{"xxx"}',
- res: {status: 'Invalid JSON body'}
- });
+ describe('An invalid JSON body', () => {
+ it('is rejected', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/',
+ httpStatus: 400,
+ reqType: 'json',
+ req: '{"xxx"}',
+ res: {status: 'Invalid JSON body'}
+ });
- });
- });
+ });
+ });
- // describe('A not connected database', () => { // RUN AS LAST OR RECONNECT DATABASE!!
- // it('resolves to an 500 error', done => {
- // db.disconnect(() => {
- // TestHelper.request(server, done, {
- // method: 'get',
- // url: '/',
- // httpStatus: 500
- // });
- // });
- // });
- // });
+ // describe('A not connected database', () => { // RUN AS LAST OR RECONNECT DATABASE!!
+ // it('resolves to an 500 error', done => {
+ // db.disconnect(() => {
+ // TestHelper.request(server, done, {
+ // method: 'get',
+ // url: '/',
+ // httpStatus: 500
+ // });
+ // });
+ // });
+ // });
});
describe('The /api/{url} redirect', () => {
- let server;
- let counter = 0; // count number of current test method
- before(done => {
- process.env.port = '2999';
- db.connect('test', done);
- });
- beforeEach(done => {
- process.env.NODE_ENV = counter === 1 ? 'production' : 'test';
- counter ++;
- server = TestHelper.beforeEach(server, done);
- });
- afterEach(done => TestHelper.afterEach(server, done));
- after(done => TestHelper.after(done));
+ let server;
+ let counter = 0; // count number of current test method
+ before(done => {
+ process.env.port = '2999';
+ db.connect('test', done);
+ });
+ beforeEach(done => {
+ process.env.NODE_ENV = counter === 1 ? 'production' : 'test';
+ counter ++;
+ server = TestHelper.beforeEach(server, done);
+ });
+ afterEach(done => TestHelper.afterEach(server, done));
+ after(done => TestHelper.after(done));
- it('returns the right method', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/api/authorized',
- auth: {basic: 'admin'},
- httpStatus: 200,
- res: {status: 'Authorization successful', method: 'basic', level: 'admin', user_id: '000000000000000000000003'}
- });
- });
- // it('is disabled in production', done => {
- // TestHelper.request(server, done, {
- // method: 'get',
- // url: '/api/authorized',
- // auth: {basic: 'admin'},
- // httpStatus: 404
- // });
- // });
-});
\ No newline at end of file
+ it('returns the right method', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/api/authorized',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ res: {status: 'Authorization successful', method: 'basic', level: 'admin', user_id: '000000000000000000000003'}
+ });
+ });
+ // it('is disabled in production', done => {
+ // TestHelper.request(server, done, {
+ // method: 'get',
+ // url: '/api/authorized',
+ // auth: {basic: 'admin'},
+ // httpStatus: 404
+ // });
+ // });
+});
diff --git a/src/routes/root.ts b/src/routes/root.ts
index 165da7e..86b76af 100644
--- a/src/routes/root.ts
+++ b/src/routes/root.ts
@@ -9,37 +9,37 @@ import _ from 'lodash';
const router = express.Router();
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) => {
- if (!req.auth(res, Object.values(globals.levels))) return;
- res.json({
- status: 'Authorization successful',
- method: req.authDetails.method,
- level: req.authDetails.level,
- user_id: req.authDetails.id
- });
+ if (!req.auth(res, Object.values(globals.levels))) return;
+ res.json({
+ status: 'Authorization successful',
+ method: req.authDetails.method,
+ level: req.authDetails.level,
+ user_id: req.authDetails.id
+ });
});
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({
- id: req.params.id,
- page: req.params.page,
- pagesize: req.params.pagesize
- });
- if (error) return res400(error, res);
+ const {error, value: options} = RootValidate.changelogParams({
+ id: req.params.id,
+ page: req.params.page,
+ pagesize: req.params.pagesize
+ });
+ if (error) return res400(error, res);
- ChangelogModel.find({_id: {$lte: mongoose.Types.ObjectId(options.id)}})
- .sort({_id: -1}).skip(options.page * options.pagesize).limit(options.pagesize)
- .lean().exec((err, data) => {
- if (err) return next(err);
+ ChangelogModel.find({_id: {$lte: mongoose.Types.ObjectId(options.id)}})
+ .sort({_id: -1}).skip(options.page * options.pagesize).limit(options.pagesize)
+ .lean().exec((err, data) => {
+ if (err) return next(err);
- // validate all and filter null values from validation errors
- res.json(_.compact(data.map(e => RootValidate.changelogOutput(e))));
- });
+ // validate all and filter null values from validation errors
+ res.json(_.compact(data.map(e => RootValidate.changelogOutput(e))));
+ });
});
module.exports = router;
diff --git a/src/routes/sample.spec.ts b/src/routes/sample.spec.ts
index 9dec0fa..38ccfe3 100644
--- a/src/routes/sample.spec.ts
+++ b/src/routes/sample.spec.ts
@@ -8,2286 +8,2286 @@ import mongoose from 'mongoose';
describe('/sample', () => {
- let server;
- before(done => TestHelper.before(done));
- beforeEach(done => server = TestHelper.beforeEach(server, done));
- afterEach(done => TestHelper.afterEach(server, done));
- after(done => TestHelper.after(done));
+ let server;
+ before(done => TestHelper.before(done));
+ beforeEach(done => server = TestHelper.beforeEach(server, done));
+ afterEach(done => TestHelper.afterEach(server, done));
+ after(done => TestHelper.after(done));
- describe('GET /samples', () => {
- it('returns all samples', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ==='validated').length);
- should(res.body).matchEach(sample => {
- should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
- should(sample).have.property('_id').be.type('string');
- should(sample).have.property('number').be.type('string');
- should(sample).have.property('type').be.type('string');
- should(sample).have.property('color').be.type('string');
- should(sample).have.property('batch').be.type('string');
- should(sample).have.property('condition').be.type('object');
- should(sample.condition).have.property('condition_template').be.type('string');
- should(sample).have.property('material_id').be.type('string');
- should(sample).have.property('note_id');
- should(sample).have.property('user_id').be.type('string');
- should(sample).have.property('added').be.type('string');
- });
- done();
- });
- });
- it('returns deleted samples for admin', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=deleted&fields[]=number&fields=status',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ==='deleted').length);
- should(res.body).matchEach(sample => {
- should(sample).have.property('status', 'deleted').be.type('string');
- });
- done();
- });
- });
- it('works with an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples',
- auth: {key: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ==='validated').length);
- should(res.body).matchEach(sample => {
- should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
- should(sample).have.property('_id').be.type('string');
- should(sample).have.property('number').be.type('string');
- should(sample).have.property('type').be.type('string');
- should(sample).have.property('color').be.type('string');
- should(sample).have.property('batch').be.type('string');
- should(sample).have.property('condition').be.type('object');
- should(sample.condition).have.property('condition_template').be.type('string');
- should(sample).have.property('material_id').be.type('string');
- should(sample).have.property('note_id');
- should(sample).have.property('user_id').be.type('string');
- should(sample).have.property('added').be.type('string');
- });
- done();
- });
- });
- it('allows filtering by state', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ==='new').length);
- should(res.body).matchEach(sample => {
- should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
- should(sample).have.property('_id').be.type('string');
- should(sample).have.property('number').be.type('string');
- should(sample).have.property('type').be.type('string');
- should(sample).have.property('color').be.type('string');
- should(sample).have.property('batch').be.type('string');
- should(sample).have.property('condition').be.type('object');
- should(sample).have.property('material_id').be.type('string');
- should(sample).have.property('note_id');
- should(sample).have.property('user_id').be.type('string');
- should(sample).have.property('added').be.type('string');
- });
- done();
- });
- });
- it('uses the given page size', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&page-size=3',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).have.lengthOf(3);
- done();
- });
- });
- it('returns results starting from first-id', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&from-id=400000000000000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body[0]).have.property('_id', '400000000000000000000002');
- should(res.body[1]).have.property('_id', '400000000000000000000003');
- done();
- });
- });
- it('returns the right page number', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&to-page=2&page-size=2',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body[0]).have.property('_id', '400000000000000000000006');
- done();
- });
- });
- it('works with negative page numbers', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&to-page=-1&page-size=2&from-id=400000000000000000000004',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body[0]).have.property('_id', '400000000000000000000002');
- should(res.body[1]).have.property('_id', '400000000000000000000003');
- done();
- });
- });
- it('returns an empty array for a page number out of range', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&to-page=100&page-size=2',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).have.lengthOf(0);
- should(res.body).be.eql([]);
- done();
- });
- });
- it('returns an empty array for a page number out of negative range', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&to-page=-100&page-size=3&from-id=400000000000000000000004',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).have.lengthOf(0);
- should(res.body).be.eql([]);
- done();
- });
- });
- it('sorts the samples ascending', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&sort=color-asc',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body[0]).have.property('color', 'black');
- should(res.body[res.body.length - 1]).have.property('color', 'natural');
- done();
- });
- });
- it('sorts the samples descending', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&sort=number-desc',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body[0]).have.property('number', 'Rng36');
- should(res.body[1]).have.property('number', '34');
- should(res.body[res.body.length - 1]).have.property('number', '1');
- done();
- });
- });
- it('sorts the samples correctly in combination with paging', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&sort=color-asc&page-size=2&from-id=400000000000000000000006',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body[0]).have.property('_id', '400000000000000000000006');
- should(res.body[1]).have.property('_id', '400000000000000000000007');
- done();
- });
- });
- it('sorts the samples correctly in combination with going pages backward', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&sort=color-desc&page-size=2&from-id=400000000000000000000004&to-page=-1',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body[0]).have.property('_id', '400000000000000000000007');
- should(res.body[1]).have.property('_id', '400000000000000000000006');
- done();
- });
- });
- it('sorts the samples correctly for material keys', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&sort=material.name-desc',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body[0]).have.property('_id', '400000000000000000000002');
- should(res.body[1]).have.property('_id', '400000000000000000000006');
- should(res.body[2]).have.property('_id', '400000000000000000000001');
- done();
- });
- });
- it('unwinds only once when filtering for spectrum measurements and including the dpt field', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.spectrum.device&fields[]=measurements.spectrum.filename&fields=measurements.spectrum.dpt&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22measurements.spectrum.device%22%2C%22values%22%3A%5B%22Alpha%20II%22%5D%7D',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.measurements.filter(e => e.values.device === 'Alpha II' && e.status !== 'deleted').length);
- should(res.body).matchEach(sample => {
- should(sample).have.only.keys('number', 'spectrum');
- should(sample.spectrum).have.only.keys('device', 'dpt', 'filename');
- should(sample.spectrum).have.property('device', 'Alpha II');
- });
- done();
- });
- })
- it('adds the status if specified', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=status',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body.find(e => e.number === '1')).have.property('status', 'validated');
- should(res.body.find(e => e.number === 'Rng36')).have.property('status', 'new');
- done();
- });
- });
- it('adds the specified measurements', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.kf.weight%20%25&fields[]=measurements.kf.standard%20deviation',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body.find(e => e.number === '1')).have.property('kf', {'weight %': null, 'standard deviation': null});
- should(res.body.find(e => e.number === 'Rng36')).have.property('kf', {'weight %': 0.6, 'standard deviation': null});
- done();
- });
- });
- it('multiplies the sample information for each spectrum', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.spectrum.dpt',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body.filter(e => e.spectrum.dpt)).have.lengthOf(3);
- should(res.body[0].spectrum).have.property('dpt', [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]]);
- should(res.body[1].spectrum).have.property('dpt', [[3996.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]]);
- done();
- });
- });
- it('filters a sample property', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=type&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22type%22%2C%22values%22%3A%5B%22processed%22%5D%7D',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.samples.filter(e => e.type === 'processed').length);
- should(res.body).matchEach(sample => {
- should(sample).have.property('type', 'processed');
- });
- done();
- });
- });
- it('filters a material property', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&filters[]=%7B%22mode%22%3A%22in%22%2C%22field%22%3A%22material.name%22%2C%22values%22%3A%5B%22Schulamid%2066%20GF%2025%20H%22%2C%22Stanyl%20TW%20200%20F8%22%5D%7D',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.samples.filter(e => e.material_id == '100000000000000000000004' || e.material_id == '100000000000000000000001').length);
- should(res.body).matchEach(sample => {
- should(sample.material.name).be.equalOneOf('Schulamid 66 GF 25 H', 'Stanyl TW 200 F8');
- });
- done();
- });
- });
- it('filters by measurement value', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&fields[]=measurements.kf.weight%20%25&filters[]=%7B%22mode%22%3A%22gt%22%2C%22field%22%3A%22measurements.kf.weight%20%25%22%2C%22values%22%3A%5B0.5%5D%7D',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.measurements.filter(e => e.measurement_template == '300000000000000000000002' && e.values['weight %'] > 0.5).length);
- should(res.body).matchEach(sample => {
- should(sample.kf['weight %']).be.above(0.5);
- });
- done();
- });
- });
- it('filters by measurement value not in the fields', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&filters[]=%7B%22mode%22%3A%22gt%22%2C%22field%22%3A%22measurements.kf.weight%20%25%22%2C%22values%22%3A%5B0.5%5D%7D',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.measurements.filter(e => e.measurement_template == '300000000000000000000002' && e.values['weight %'] > 0.5).length);
- should(res.body[0]).have.property('number', 'Rng36');
- done();
- });
- });
- it('filters by a measurement properties property', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&fields[]=material.properties.glass_fiber&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22material.properties.glass_fiber%22%2C%22values%22%3A%5B%2225%22%5D%7D',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).have.lengthOf(2);
- should(res.body).matchEach(sample => {
- should(sample.material.properties.glass_fiber).be.eql(25);
- });
- done();
- });
- });
- it('filters and sorts by a measurement properties property', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&sort=material.properties.glass_fiber-desc&fields[]=number&fields[]=material.name&fields[]=material.properties.glass_fiber&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22material.properties.glass_fiber%22%2C%22values%22%3A%5B%2225%22%5D%7D',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).have.lengthOf(2);
- should(res.body[0].number).be.eql('Rng36');
- should(res.body[1].number).be.eql('1');
- should(res.body).matchEach(sample => {
- should(sample.material.properties.glass_fiber).be.eql(25);
- });
- done();
- });
- });
- it('filters multiple properties', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=batch&filters[]=%7B%22mode%22%3A%22lte%22%2C%22field%22%3A%22number%22%2C%22values%22%3A%5B%22Rng33%22%5D%7D&filters[]=%7B%22mode%22%3A%22nin%22%2C%22field%22%3A%22batch%22%2C%22values%22%3A%5B%221704-005%22%5D%7D',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).have.lengthOf(4);
- should(res.body[0]).be.eql({number: '1', batch: ''});
- done();
- });
- });
- it('filters for empty comments', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=notes.comment&filters[]=%7B%22mode%22%3A%22in%22%2C%22field%22%3A%22notes.comment%22%2C%22values%22%3A%5Bnull%2C%22%22%5D%7D',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(
- json.collections.samples
- .filter(e => e.status !== 'deleted')
- .filter(e => e.note_id === null || json.collections.notes.find(el => el._id.toString() == e.note_id.toString()).comment === '')
- .length
- );
- should(res.body).matchEach(sample => {
- should(sample.notes.comment).be.equalOneOf(null, '');
- });
- done();
- });
- });
- it('filters for empty conditions', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=condition&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22condition%22%2C%22values%22%3A%5B%7B%7D%5D%7D',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(
- json.collections.samples
- .filter(e => e.status !== 'deleted')
- .filter(e => Object.keys(e.condition).length === 0)
- .length
- );
- should(res.body).matchEach(sample => {
- should(sample.condition).be.eql({});
- });
- done();
- });
- });
- it('filters for samples without measurements', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=_id&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22measurements%22%2C%22values%22%3A%5Bnull%5D%7D',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(
- json.collections.samples
- .filter(e => e.status !== 'deleted')
- .filter(e => !json.collections.measurements.find(el => el.sample_id.toString() === e._id.toString()))
- .length
- );
- should(res.body).matchEach(sample => {
- should(json.collections.measurements.find(el => el.sample_id.toString() === sample._id)).be.eql(undefined);
- });
- done();
- });
- });
- it('returns comment fields', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=notes.comment',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status !== 'deleted').length);
- should(res.body).matchEach(sample => {
- should(sample).have.only.keys('number', 'notes');
- should(sample.notes).have.only.keys('comment');
- });
- done();
- });
- });
- it('rejects returning spectral data for a write user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.spectrum.dpt',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('rejects an invalid JSON string as a filters parameter', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.glass_fiber&fields[]=batch&filters[]=xx',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: 'Invalid JSON string for filter parameter'}
- });
- });
- it('rejects an invalid filter mode', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=batch&filters[]=%7B%22mode%22%3A%22xx%22%2C%22field%22%3A%22batch%22%2C%22values%22%3A%5B%221704-005%22%5D%7D',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: '"filters[0].mode" must be one of [eq, ne, lt, lte, gt, gte, in, nin, stringin]'}
- });
- });
- it('rejects an filter field not existing', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.glass_fiber&fields[]=batch&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22xx%22%2C%22values%22%3A%5B%221704-005%22%5D%7D',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: 'Invalid JSON string for filter parameter'}
- });
- });
- it('rejects unknown measurement names', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.xx',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: 'Measurement key not found'}
- });
- });
- it('returns a correct csv file for admins if specified', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&page-size=2&output=csv',
- contentType: /text\/csv/,
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.text).be.eql('"_id","number","type","color","batch","condition.material","condition.weeks","condition.condition_template","material_id","note_id","user_id","added"\r\n' +
- '"400000000000000000000001","1","as-delivered/raw","black","","copper",3,"200000000000000000000001","100000000000000000000004",,"000000000000000000000002","2004-01-10T13:37:04.000Z"\r\n' +
- '"400000000000000000000002","21","as-delivered/raw","natural","1560237365","copper",3,"200000000000000000000001","100000000000000000000001","500000000000000000000001","000000000000000000000002","2004-01-10T13:37:04.000Z"');
- done();
- });
- });
- it('rejects returning a csv file for a write user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&page-size=2&output=csv',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('returns the object flattened if specified', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.spectrum.device&fields[]=measurements.spectrum.dpt&page-size=1&output=flatten',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body[0]).have.only.keys('number', 'spectrum.device', 'spectrum.dpt.labels', 'spectrum.dpt.values');
- should(res.body[0]).have.property('number', '1');
- should(res.body[0]).have.property('spectrum.device', 'Alpha I');
- should(res.body[0]).have.property('spectrum.dpt.labels', [3997.12558, 3995.08519, 3993.0448]);
- should(res.body[0]).have.property('spectrum.dpt.values', [98.00555, 98.03253, 98.02657]);
- done();
- });
- });
- it('returns only the fields specified', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&page-size=1&fields[]=number&fields[]=condition&fields[]=color&fields[]=material.name&fields[]=material.supplier',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- res: [{number: '1', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, color: 'black', material: {name: 'Schulamid 66 GF 25 H', supplier: 'Schulmann'}}]
- });
- });
- it('returns specified material properties fields', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.properties.glass_fiber&fields[]=material.name',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).matchEach(sample => {
- const materialId = json.collections.samples.find(e => e.number === sample.number).material_id;
- const material = json.collections.materials.find(e => e._id.toString() == materialId);
- should(sample).have.only.keys('number', 'material');
- should(sample.material.name).be.eql(material.name);
- should(sample.material.properties.glass_fiber).be.eql(material.properties.glass_fiber);
- });
- done()
- });
- });
- it('rejects a from-id not in the database', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?from-id=5ea0450ed851c30a90e70894&sort=color-asc',
- auth: {basic: 'admin'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: 'from-id not found'}
- });
- });
- it('rejects an invalid fields parameter', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&page-size=1&fields=number',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: '"fields" must be an array'}
- });
- });
- it('rejects an unknown field name', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=new&status[]=validated&page-size=1&fields[]=xx',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: 'Invalid field name'}
- });
- });
- it('rejects a negative page size', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?page-size=-3',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: '"page-size" must be greater than or equal to 1'}
- });
- });
- it('rejects an invalid from-id', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?from-id=40000000000h000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: 'Invalid object id'}
- });
- });
- it('rejects a to-page without page-size', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?to-page=3',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: '"to-page" missing required peer "page-size"'}
- });
- });
- it('rejects an invalid state name', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples?status[]=xxx',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- res: {status: 'Invalid body format', details: '"status[0]" must be one of [validated, new]'}
- });
- });
- it('rejects requests from a predict user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples',
- auth: {basic: 'customer'},
- httpStatus: 403
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples',
- httpStatus: 401
- });
- });
- });
+ describe('GET /samples', () => {
+ it('returns all samples', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ==='validated').length);
+ should(res.body).matchEach(sample => {
+ should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
+ should(sample).have.property('_id').be.type('string');
+ should(sample).have.property('number').be.type('string');
+ should(sample).have.property('type').be.type('string');
+ should(sample).have.property('color').be.type('string');
+ should(sample).have.property('batch').be.type('string');
+ should(sample).have.property('condition').be.type('object');
+ should(sample.condition).have.property('condition_template').be.type('string');
+ should(sample).have.property('material_id').be.type('string');
+ should(sample).have.property('note_id');
+ should(sample).have.property('user_id').be.type('string');
+ should(sample).have.property('added').be.type('string');
+ });
+ done();
+ });
+ });
+ it('returns deleted samples for admin', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=deleted&fields[]=number&fields=status',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ==='deleted').length);
+ should(res.body).matchEach(sample => {
+ should(sample).have.property('status', 'deleted').be.type('string');
+ });
+ done();
+ });
+ });
+ it('works with an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples',
+ auth: {key: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ==='validated').length);
+ should(res.body).matchEach(sample => {
+ should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
+ should(sample).have.property('_id').be.type('string');
+ should(sample).have.property('number').be.type('string');
+ should(sample).have.property('type').be.type('string');
+ should(sample).have.property('color').be.type('string');
+ should(sample).have.property('batch').be.type('string');
+ should(sample).have.property('condition').be.type('object');
+ should(sample.condition).have.property('condition_template').be.type('string');
+ should(sample).have.property('material_id').be.type('string');
+ should(sample).have.property('note_id');
+ should(sample).have.property('user_id').be.type('string');
+ should(sample).have.property('added').be.type('string');
+ });
+ done();
+ });
+ });
+ it('allows filtering by state', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ==='new').length);
+ should(res.body).matchEach(sample => {
+ should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
+ should(sample).have.property('_id').be.type('string');
+ should(sample).have.property('number').be.type('string');
+ should(sample).have.property('type').be.type('string');
+ should(sample).have.property('color').be.type('string');
+ should(sample).have.property('batch').be.type('string');
+ should(sample).have.property('condition').be.type('object');
+ should(sample).have.property('material_id').be.type('string');
+ should(sample).have.property('note_id');
+ should(sample).have.property('user_id').be.type('string');
+ should(sample).have.property('added').be.type('string');
+ });
+ done();
+ });
+ });
+ it('uses the given page size', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&page-size=3',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).have.lengthOf(3);
+ done();
+ });
+ });
+ it('returns results starting from first-id', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&from-id=400000000000000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body[0]).have.property('_id', '400000000000000000000002');
+ should(res.body[1]).have.property('_id', '400000000000000000000003');
+ done();
+ });
+ });
+ it('returns the right page number', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&to-page=2&page-size=2',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body[0]).have.property('_id', '400000000000000000000006');
+ done();
+ });
+ });
+ it('works with negative page numbers', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&to-page=-1&page-size=2&from-id=400000000000000000000004',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body[0]).have.property('_id', '400000000000000000000002');
+ should(res.body[1]).have.property('_id', '400000000000000000000003');
+ done();
+ });
+ });
+ it('returns an empty array for a page number out of range', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&to-page=100&page-size=2',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).have.lengthOf(0);
+ should(res.body).be.eql([]);
+ done();
+ });
+ });
+ it('returns an empty array for a page number out of negative range', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&to-page=-100&page-size=3&from-id=400000000000000000000004',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).have.lengthOf(0);
+ should(res.body).be.eql([]);
+ done();
+ });
+ });
+ it('sorts the samples ascending', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&sort=color-asc',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body[0]).have.property('color', 'black');
+ should(res.body[res.body.length - 1]).have.property('color', 'natural');
+ done();
+ });
+ });
+ it('sorts the samples descending', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&sort=number-desc',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body[0]).have.property('number', 'Rng36');
+ should(res.body[1]).have.property('number', '34');
+ should(res.body[res.body.length - 1]).have.property('number', '1');
+ done();
+ });
+ });
+ it('sorts the samples correctly in combination with paging', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&sort=color-asc&page-size=2&from-id=400000000000000000000006',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body[0]).have.property('_id', '400000000000000000000006');
+ should(res.body[1]).have.property('_id', '400000000000000000000007');
+ done();
+ });
+ });
+ it('sorts the samples correctly in combination with going pages backward', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&sort=color-desc&page-size=2&from-id=400000000000000000000004&to-page=-1',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body[0]).have.property('_id', '400000000000000000000007');
+ should(res.body[1]).have.property('_id', '400000000000000000000006');
+ done();
+ });
+ });
+ it('sorts the samples correctly for material keys', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&sort=material.name-desc',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body[0]).have.property('_id', '400000000000000000000002');
+ should(res.body[1]).have.property('_id', '400000000000000000000006');
+ should(res.body[2]).have.property('_id', '400000000000000000000001');
+ done();
+ });
+ });
+ it('unwinds only once when filtering for spectrum measurements and including the dpt field', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.spectrum.device&fields[]=measurements.spectrum.filename&fields=measurements.spectrum.dpt&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22measurements.spectrum.device%22%2C%22values%22%3A%5B%22Alpha%20II%22%5D%7D',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.measurements.filter(e => e.values.device === 'Alpha II' && e.status !== 'deleted').length);
+ should(res.body).matchEach(sample => {
+ should(sample).have.only.keys('number', 'spectrum');
+ should(sample.spectrum).have.only.keys('device', 'dpt', 'filename');
+ should(sample.spectrum).have.property('device', 'Alpha II');
+ });
+ done();
+ });
+ })
+ it('adds the status if specified', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=status',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body.find(e => e.number === '1')).have.property('status', 'validated');
+ should(res.body.find(e => e.number === 'Rng36')).have.property('status', 'new');
+ done();
+ });
+ });
+ it('adds the specified measurements', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.kf.weight%20%25&fields[]=measurements.kf.standard%20deviation',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body.find(e => e.number === '1')).have.property('kf', {'weight %': null, 'standard deviation': null});
+ should(res.body.find(e => e.number === 'Rng36')).have.property('kf', {'weight %': 0.6, 'standard deviation': null});
+ done();
+ });
+ });
+ it('multiplies the sample information for each spectrum', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.spectrum.dpt',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body.filter(e => e.spectrum.dpt)).have.lengthOf(3);
+ should(res.body[0].spectrum).have.property('dpt', [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]]);
+ should(res.body[1].spectrum).have.property('dpt', [[3996.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]]);
+ done();
+ });
+ });
+ it('filters a sample property', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=type&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22type%22%2C%22values%22%3A%5B%22processed%22%5D%7D',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.samples.filter(e => e.type === 'processed').length);
+ should(res.body).matchEach(sample => {
+ should(sample).have.property('type', 'processed');
+ });
+ done();
+ });
+ });
+ it('filters a material property', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&filters[]=%7B%22mode%22%3A%22in%22%2C%22field%22%3A%22material.name%22%2C%22values%22%3A%5B%22Schulamid%2066%20GF%2025%20H%22%2C%22Stanyl%20TW%20200%20F8%22%5D%7D',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.samples.filter(e => e.material_id == '100000000000000000000004' || e.material_id == '100000000000000000000001').length);
+ should(res.body).matchEach(sample => {
+ should(sample.material.name).be.equalOneOf('Schulamid 66 GF 25 H', 'Stanyl TW 200 F8');
+ });
+ done();
+ });
+ });
+ it('filters by measurement value', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&fields[]=measurements.kf.weight%20%25&filters[]=%7B%22mode%22%3A%22gt%22%2C%22field%22%3A%22measurements.kf.weight%20%25%22%2C%22values%22%3A%5B0.5%5D%7D',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.measurements.filter(e => e.measurement_template == '300000000000000000000002' && e.values['weight %'] > 0.5).length);
+ should(res.body).matchEach(sample => {
+ should(sample.kf['weight %']).be.above(0.5);
+ });
+ done();
+ });
+ });
+ it('filters by measurement value not in the fields', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&filters[]=%7B%22mode%22%3A%22gt%22%2C%22field%22%3A%22measurements.kf.weight%20%25%22%2C%22values%22%3A%5B0.5%5D%7D',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.measurements.filter(e => e.measurement_template == '300000000000000000000002' && e.values['weight %'] > 0.5).length);
+ should(res.body[0]).have.property('number', 'Rng36');
+ done();
+ });
+ });
+ it('filters by a measurement properties property', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&fields[]=material.properties.glass_fiber&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22material.properties.glass_fiber%22%2C%22values%22%3A%5B%2225%22%5D%7D',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).have.lengthOf(2);
+ should(res.body).matchEach(sample => {
+ should(sample.material.properties.glass_fiber).be.eql(25);
+ });
+ done();
+ });
+ });
+ it('filters and sorts by a measurement properties property', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&sort=material.properties.glass_fiber-desc&fields[]=number&fields[]=material.name&fields[]=material.properties.glass_fiber&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22material.properties.glass_fiber%22%2C%22values%22%3A%5B%2225%22%5D%7D',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).have.lengthOf(2);
+ should(res.body[0].number).be.eql('Rng36');
+ should(res.body[1].number).be.eql('1');
+ should(res.body).matchEach(sample => {
+ should(sample.material.properties.glass_fiber).be.eql(25);
+ });
+ done();
+ });
+ });
+ it('filters multiple properties', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=batch&filters[]=%7B%22mode%22%3A%22lte%22%2C%22field%22%3A%22number%22%2C%22values%22%3A%5B%22Rng33%22%5D%7D&filters[]=%7B%22mode%22%3A%22nin%22%2C%22field%22%3A%22batch%22%2C%22values%22%3A%5B%221704-005%22%5D%7D',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).have.lengthOf(4);
+ should(res.body[0]).be.eql({number: '1', batch: ''});
+ done();
+ });
+ });
+ it('filters for empty comments', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=notes.comment&filters[]=%7B%22mode%22%3A%22in%22%2C%22field%22%3A%22notes.comment%22%2C%22values%22%3A%5Bnull%2C%22%22%5D%7D',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(
+ json.collections.samples
+ .filter(e => e.status !== 'deleted')
+ .filter(e => e.note_id === null || json.collections.notes.find(el => el._id.toString() == e.note_id.toString()).comment === '')
+ .length
+ );
+ should(res.body).matchEach(sample => {
+ should(sample.notes.comment).be.equalOneOf(null, '');
+ });
+ done();
+ });
+ });
+ it('filters for empty conditions', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=condition&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22condition%22%2C%22values%22%3A%5B%7B%7D%5D%7D',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(
+ json.collections.samples
+ .filter(e => e.status !== 'deleted')
+ .filter(e => Object.keys(e.condition).length === 0)
+ .length
+ );
+ should(res.body).matchEach(sample => {
+ should(sample.condition).be.eql({});
+ });
+ done();
+ });
+ });
+ it('filters for samples without measurements', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=_id&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22measurements%22%2C%22values%22%3A%5Bnull%5D%7D',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(
+ json.collections.samples
+ .filter(e => e.status !== 'deleted')
+ .filter(e => !json.collections.measurements.find(el => el.sample_id.toString() === e._id.toString()))
+ .length
+ );
+ should(res.body).matchEach(sample => {
+ should(json.collections.measurements.find(el => el.sample_id.toString() === sample._id)).be.eql(undefined);
+ });
+ done();
+ });
+ });
+ it('returns comment fields', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=notes.comment',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status !== 'deleted').length);
+ should(res.body).matchEach(sample => {
+ should(sample).have.only.keys('number', 'notes');
+ should(sample.notes).have.only.keys('comment');
+ });
+ done();
+ });
+ });
+ it('rejects returning spectral data for a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.spectrum.dpt',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('rejects an invalid JSON string as a filters parameter', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.glass_fiber&fields[]=batch&filters[]=xx',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: 'Invalid JSON string for filter parameter'}
+ });
+ });
+ it('rejects an invalid filter mode', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=batch&filters[]=%7B%22mode%22%3A%22xx%22%2C%22field%22%3A%22batch%22%2C%22values%22%3A%5B%221704-005%22%5D%7D',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: '"filters[0].mode" must be one of [eq, ne, lt, lte, gt, gte, in, nin, stringin]'}
+ });
+ });
+ it('rejects an filter field not existing', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.glass_fiber&fields[]=batch&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22xx%22%2C%22values%22%3A%5B%221704-005%22%5D%7D',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: 'Invalid JSON string for filter parameter'}
+ });
+ });
+ it('rejects unknown measurement names', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.xx',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: 'Measurement key not found'}
+ });
+ });
+ it('returns a correct csv file for admins if specified', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&page-size=2&output=csv',
+ contentType: /text\/csv/,
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.text).be.eql('"_id","number","type","color","batch","condition.material","condition.weeks","condition.condition_template","material_id","note_id","user_id","added"\r\n' +
+ '"400000000000000000000001","1","as-delivered/raw","black","","copper",3,"200000000000000000000001","100000000000000000000004",,"000000000000000000000002","2004-01-10T13:37:04.000Z"\r\n' +
+ '"400000000000000000000002","21","as-delivered/raw","natural","1560237365","copper",3,"200000000000000000000001","100000000000000000000001","500000000000000000000001","000000000000000000000002","2004-01-10T13:37:04.000Z"');
+ done();
+ });
+ });
+ it('rejects returning a csv file for a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&page-size=2&output=csv',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('returns the object flattened if specified', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.spectrum.device&fields[]=measurements.spectrum.dpt&page-size=1&output=flatten',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body[0]).have.only.keys('number', 'spectrum.device', 'spectrum.dpt.labels', 'spectrum.dpt.values');
+ should(res.body[0]).have.property('number', '1');
+ should(res.body[0]).have.property('spectrum.device', 'Alpha I');
+ should(res.body[0]).have.property('spectrum.dpt.labels', [3997.12558, 3995.08519, 3993.0448]);
+ should(res.body[0]).have.property('spectrum.dpt.values', [98.00555, 98.03253, 98.02657]);
+ done();
+ });
+ });
+ it('returns only the fields specified', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&page-size=1&fields[]=number&fields[]=condition&fields[]=color&fields[]=material.name&fields[]=material.supplier',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ res: [{number: '1', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, color: 'black', material: {name: 'Schulamid 66 GF 25 H', supplier: 'Schulmann'}}]
+ });
+ });
+ it('returns specified material properties fields', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.properties.glass_fiber&fields[]=material.name',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).matchEach(sample => {
+ const materialId = json.collections.samples.find(e => e.number === sample.number).material_id;
+ const material = json.collections.materials.find(e => e._id.toString() == materialId);
+ should(sample).have.only.keys('number', 'material');
+ should(sample.material.name).be.eql(material.name);
+ should(sample.material.properties.glass_fiber).be.eql(material.properties.glass_fiber);
+ });
+ done()
+ });
+ });
+ it('rejects a from-id not in the database', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?from-id=5ea0450ed851c30a90e70894&sort=color-asc',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: 'from-id not found'}
+ });
+ });
+ it('rejects an invalid fields parameter', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&page-size=1&fields=number',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: '"fields" must be an array'}
+ });
+ });
+ it('rejects an unknown field name', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=new&status[]=validated&page-size=1&fields[]=xx',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: 'Invalid field name'}
+ });
+ });
+ it('rejects a negative page size', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?page-size=-3',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: '"page-size" must be greater than or equal to 1'}
+ });
+ });
+ it('rejects an invalid from-id', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?from-id=40000000000h000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: 'Invalid object id'}
+ });
+ });
+ it('rejects a to-page without page-size', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?to-page=3',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: '"to-page" missing required peer "page-size"'}
+ });
+ });
+ it('rejects an invalid state name', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples?status[]=xxx',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ res: {status: 'Invalid body format', details: '"status[0]" must be one of [validated, new]'}
+ });
+ });
+ it('rejects requests from a predict user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples',
+ auth: {basic: 'customer'},
+ httpStatus: 403
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples',
+ httpStatus: 401
+ });
+ });
+ });
- describe('GET /samples/{state}', () => {
- it('returns all new samples', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples/new',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- let asyncCounter = res.body.length;
- should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ==='new').length);
- should(res.body).matchEach(sample => {
- should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added');
- should(sample).have.property('_id').be.type('string');
- should(sample).have.property('number').be.type('string');
- should(sample).have.property('type').be.type('string');
- should(sample).have.property('color').be.type('string');
- should(sample).have.property('batch').be.type('string');
- should(sample).have.property('condition').be.type('object');
- if (Object.keys(sample.condition).length > 0) {
- should(sample.condition).have.property('condition_template').be.type('string');
- }
- should(sample).have.property('material_id').be.type('string');
- should(sample).have.property('note_id');
- should(sample).have.property('user_id').be.type('string');
- should(sample).have.property('added').be.type('string');
- should(sample).have.property('status').be.type('string');
- SampleModel.findById(sample._id).lean().exec((err, data) => {
- should(data).have.property('status','new');
- if (--asyncCounter === 0) {
- done();
- }
- });
- });
- });
- });
- it('returns all deleted samples', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples/deleted',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- let asyncCounter = res.body.length;
- should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status === 'deleted').length);
- should(res.body).matchEach(sample => {
- should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added');
- should(sample).have.property('_id').be.type('string');
- should(sample).have.property('number').be.type('string');
- should(sample).have.property('type').be.type('string');
- should(sample).have.property('color').be.type('string');
- should(sample).have.property('batch').be.type('string');
- should(sample).have.property('condition').be.type('object');
- should(sample.condition).have.property('condition_template').be.type('string');
- should(sample.condition).have.property('condition_template').be.type('string');
- should(sample.condition).have.property('condition_template').be.type('string');
- should(sample).have.property('material_id').be.type('string');
- should(sample).have.property('note_id');
- should(sample).have.property('user_id').be.type('string');
- should(sample).have.property('added').be.type('string');
- should(sample).have.property('status').be.type('string');
- SampleModel.findById(sample._id).lean().exec((err, data) => {
- should(data).have.property('status','deleted');
- if (--asyncCounter === 0) {
- done();
- }
- });
- });
- });
- });
- it('rejects requests from a write user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples/new',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples/new',
- auth: {key: 'admin'},
- httpStatus: 401
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples/new',
- httpStatus: 401
- });
- });
- });
+ describe('GET /samples/{state}', () => {
+ it('returns all new samples', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples/new',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ let asyncCounter = res.body.length;
+ should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ==='new').length);
+ should(res.body).matchEach(sample => {
+ should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added');
+ should(sample).have.property('_id').be.type('string');
+ should(sample).have.property('number').be.type('string');
+ should(sample).have.property('type').be.type('string');
+ should(sample).have.property('color').be.type('string');
+ should(sample).have.property('batch').be.type('string');
+ should(sample).have.property('condition').be.type('object');
+ if (Object.keys(sample.condition).length > 0) {
+ should(sample.condition).have.property('condition_template').be.type('string');
+ }
+ should(sample).have.property('material_id').be.type('string');
+ should(sample).have.property('note_id');
+ should(sample).have.property('user_id').be.type('string');
+ should(sample).have.property('added').be.type('string');
+ should(sample).have.property('status').be.type('string');
+ SampleModel.findById(sample._id).lean().exec((err, data) => {
+ should(data).have.property('status','new');
+ if (--asyncCounter === 0) {
+ done();
+ }
+ });
+ });
+ });
+ });
+ it('returns all deleted samples', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples/deleted',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ let asyncCounter = res.body.length;
+ should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status === 'deleted').length);
+ should(res.body).matchEach(sample => {
+ should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added');
+ should(sample).have.property('_id').be.type('string');
+ should(sample).have.property('number').be.type('string');
+ should(sample).have.property('type').be.type('string');
+ should(sample).have.property('color').be.type('string');
+ should(sample).have.property('batch').be.type('string');
+ should(sample).have.property('condition').be.type('object');
+ should(sample.condition).have.property('condition_template').be.type('string');
+ should(sample.condition).have.property('condition_template').be.type('string');
+ should(sample.condition).have.property('condition_template').be.type('string');
+ should(sample).have.property('material_id').be.type('string');
+ should(sample).have.property('note_id');
+ should(sample).have.property('user_id').be.type('string');
+ should(sample).have.property('added').be.type('string');
+ should(sample).have.property('status').be.type('string');
+ SampleModel.findById(sample._id).lean().exec((err, data) => {
+ should(data).have.property('status','deleted');
+ if (--asyncCounter === 0) {
+ done();
+ }
+ });
+ });
+ });
+ });
+ it('rejects requests from a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples/new',
+ auth: {key: 'admin'},
+ httpStatus: 401
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples/new',
+ httpStatus: 401
+ });
+ });
+ });
- describe('GET /samples/count', () => {
- it('returns the correct number of samples', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples/count',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body.count).be.eql(json.collections.samples.length);
- done();
- });
- });
- it('works with an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples/count',
- auth: {key: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body.count).be.eql(json.collections.samples.length);
- done();
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/samples/count',
- httpStatus: 401
- });
- });
- });
+ describe('GET /samples/count', () => {
+ it('returns the correct number of samples', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples/count',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body.count).be.eql(json.collections.samples.length);
+ done();
+ });
+ });
+ it('works with an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples/count',
+ auth: {key: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body.count).be.eql(json.collections.samples.length);
+ done();
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/samples/count',
+ httpStatus: 401
+ });
+ });
+ });
- describe('GET /sample/{id}', () => {
- it('returns the right sample', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/400000000000000000000003',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- res: {_id: '400000000000000000000003', number: '33', type: 'processed', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'as-delivered/raw to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', user: 'admin'}
- });
- });
- it('works with an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/400000000000000000000003',
- auth: {key: 'janedoe'},
- httpStatus: 200,
- res: {_id: '400000000000000000000003', number: '33', type: 'processed', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'as-delivered/raw to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', user: 'admin'}
- });
- });
- it ('filters out spectral data for a write user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {device: 'Alpha II', filename: '1_1.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000009', sample_id: '400000000000000000000001', values: {device: 'Alpha II', filename: '1_2.DPT'}, measurement_template: '300000000000000000000001'}], status: 'validated'}
- });
- });
- it ('returns spectral data for an admin user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[ 3997.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]],device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {dpt: [[ 3996.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]], device: 'Alpha II', filename: '1_1.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000009', sample_id: '400000000000000000000001', values: {dpt: [[ 3996.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]], device: 'Alpha II', filename: '1_2.DPT'}, measurement_template: '300000000000000000000001'}], status: 'validated'}
- });
- });
- it('returns a deleted sample for a dev/admin user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/400000000000000000000005',
- auth: {basic: 'admin'},
- httpStatus: 200,
- res: {_id: '400000000000000000000005', number: 'Rng33', type: 'as-delivered/raw', color: 'black', batch: '1653000308', condition: {condition_template: '200000000000000000000003'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {}, measurements: [], status: 'deleted', user: 'admin'}
- });
- });
- it('returns 403 for a write user when requesting a deleted sample', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/400000000000000000000005',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('returns 404 for an unknown sample', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/000000000000000000000005',
- auth: {basic: 'janedoe'},
- httpStatus: 404
- });
- });
- it('rejects an invalid id', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/400000000h00000000000005',
- auth: {basic: 'janedoe'},
- httpStatus: 404
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/400000000000000000000005',
- httpStatus: 401
- });
- });
- });
+ describe('GET /sample/{id}', () => {
+ it('returns the right sample', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/400000000000000000000003',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ res: {_id: '400000000000000000000003', number: '33', type: 'processed', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'as-delivered/raw to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', user: 'admin'}
+ });
+ });
+ it('works with an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/400000000000000000000003',
+ auth: {key: 'janedoe'},
+ httpStatus: 200,
+ res: {_id: '400000000000000000000003', number: '33', type: 'processed', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'as-delivered/raw to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', user: 'admin'}
+ });
+ });
+ it ('filters out spectral data for a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {device: 'Alpha II', filename: '1_1.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000009', sample_id: '400000000000000000000001', values: {device: 'Alpha II', filename: '1_2.DPT'}, measurement_template: '300000000000000000000001'}], status: 'validated'}
+ });
+ });
+ it ('returns spectral data for an admin user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[ 3997.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]],device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {dpt: [[ 3996.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]], device: 'Alpha II', filename: '1_1.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000009', sample_id: '400000000000000000000001', values: {dpt: [[ 3996.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]], device: 'Alpha II', filename: '1_2.DPT'}, measurement_template: '300000000000000000000001'}], status: 'validated'}
+ });
+ });
+ it('returns a deleted sample for a dev/admin user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/400000000000000000000005',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ res: {_id: '400000000000000000000005', number: 'Rng33', type: 'as-delivered/raw', color: 'black', batch: '1653000308', condition: {condition_template: '200000000000000000000003'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {}, measurements: [], status: 'deleted', user: 'admin'}
+ });
+ });
+ it('returns 403 for a write user when requesting a deleted sample', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/400000000000000000000005',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('returns 404 for an unknown sample', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/000000000000000000000005',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404
+ });
+ });
+ it('rejects an invalid id', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/400000000h00000000000005',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/400000000000000000000005',
+ httpStatus: 401
+ });
+ });
+ });
- describe('PUT /sample/{id}', () => {
- it('returns the right sample', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {},
- res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'}
- });
- });
- it('keeps unchanged properties', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', notes: {}}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'});
- SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
- if (err) return done (err);
- should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
- should(data).have.property('_id');
- should(data).have.property('number', '1');
- should(data).have.property('color', 'black');
- should(data).have.property('type', 'as-delivered/raw');
- should(data).have.property('batch', '');
- should(data).have.property('condition', {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'});
- should(data.material_id.toString()).be.eql('100000000000000000000004');
- should(data.user_id.toString()).be.eql('000000000000000000000002');
- should(data).have.property('status','validated');
- should(data).have.property('note_id', null);
- done();
- });
- });
- });
- it('keeps only one unchanged parameter', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {type: 'as-delivered/raw'}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'});
- SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
- if (err) return done (err);
- should(data).have.property('status','validated');
- done();
- });
- });
- });
- it('keeps an unchanged condition', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'});
- SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
- if (err) return done (err);
- should(data).have.property('status','validated');
- done();
- });
- });
- });
- it('keeps unchanged notes', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {notes: {comment: 'Stoff gesperrt', sample_references: []}}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({_id: '400000000000000000000002', number: '21', type: 'as-delivered/raw', color: 'natural', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', note_id: '500000000000000000000001', user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'});
- SampleModel.findById('400000000000000000000002').lean().exec((err, data: any) => {
- if (err) return done (err);
- should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
- should(data).have.property('_id');
- should(data).have.property('number', '21');
- should(data).have.property('color', 'natural');
- should(data).have.property('type', 'as-delivered/raw');
- should(data).have.property('batch', '1560237365');
- should(data.condition).have.property('material', 'copper');
- should(data.condition).have.property('weeks', 3);
- should(data.condition.condition_template.toString()).be.eql('200000000000000000000001');
- should(data.material_id.toString()).be.eql('100000000000000000000001');
- should(data.user_id.toString()).be.eql('000000000000000000000002');
- should(data).have.property('status','validated');
- should(data.note_id.toString()).be.eql('500000000000000000000001');
- done();
- });
- });
- });
- it('changes the given properties', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {type: 'processed', color: 'signalviolet', batch: '114531', condition: {condition_template: '200000000000000000000003'}, material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
- }).end(err => {
- if (err) return done (err);
- SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
- if (err) return done (err);
- should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
- should(data).have.property('_id');
- should(data).have.property('number', '1');
- should(data).have.property('color', 'signalviolet');
- should(data).have.property('type', 'processed');
- should(data).have.property('batch', '114531');
- should(data).have.property('condition', {condition_template: '200000000000000000000003'});
- should(data.material_id.toString()).be.eql('100000000000000000000002');
- should(data.user_id.toString()).be.eql('000000000000000000000002');
- should(data).have.property('status','new');
- should(data).have.property('note_id');
- NoteModel.findById(data.note_id).lean().exec((err, data: any) => {
- if (err) return done (err);
- should(data).have.property('_id');
- should(data).have.property('comment', 'Testcomment');
- should(data).have.property('sample_references');
- should(data.sample_references).have.lengthOf(1);
- should(data.sample_references[0].sample_id.toString()).be.eql('400000000000000000000003');
- should(data.sample_references[0]).have.property('relation', 'part to this sample');
- done();
- });
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {type: 'processed', color: 'signalviolet', batch: '114531', condition: {condition_template: '200000000000000000000003'}, material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- log: {
- collection: 'samples',
- dataAdd: {
- status: 'new'
- },
- dataIgn: ['notes', 'note_id']
- }
- });
- });
- it('adjusts the note_fields correctly', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000003',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {notes: {comment: 'Testcomment', sample_references: [], custom_fields: {field1: 'value 1'}}}
- }).end(err => {
- if (err) return done(err);
- NoteFieldModel.findOne({name: 'not allowed for new applications'}).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.property('qty', 1);
- NoteFieldModel.findOne({name: 'field1'}).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.property('qty', 1);
- done();
- });
- });
- });
- });
- it('deletes old note_fields', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000004',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {notes: {comment: 'Testcomment', sample_references: []}}
- }).end(err => {
- if (err) return done (err);
- NoteFieldModel.findOne({name: 'another_field'}).lean().exec((err, data) => {
- if (err) return done (err);
- should(data).be.null();
- done();
- });
- });
- });
- it('keeps untouched notes', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {type: 'processed'}
- }).end((err, res) => {
- if (err) return done (err);
- NoteModel.findById(res.body.note_id).lean().exec((err, data) => {
- if (err) return done (err);
- should(data).not.be.null();
- should(data).have.property('comment', 'Stoff gesperrt');
- should(data).have.property('sample_references').have.lengthOf(0);
- done();
- });
- });
- });
- it('deletes old notes', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000004',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {notes: {comment: 'Testcomment', sample_references: []}}
- }).end(err => {
- if (err) return done (err);
- NoteModel.findById('500000000000000000000003').lean().exec((err, data) => {
- if (err) return done (err);
- should(data).be.null();
- done();
- });
- });
- });
- it('rejects an unknown material id', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '000000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Material not available'}
- });
- });
- it('rejects a sample number', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {number: 25, type: 'processed', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Invalid body format', details: '"number" is not allowed'}
- });
- });
- it('rejects an invalid sample reference', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '000000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Sample reference not available'}
- });
- });
- it('rejects an invalid material id', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Invalid body format', details: 'Invalid object id'}
- });
- });
- it('rejects an invalid id', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/10000000000h000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 404,
- req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- });
- });
- it('rejects not specified condition parameters', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {condition: {material: 'copper', weeks: 3, xxx: 44, condition_template: '200000000000000000000001'}},
- res: {status: 'Invalid body format', details: '"xxx" is not allowed'}
- });
- });
- it('rejects a condition parameter not in the value range', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {condition: {material: 'xx', weeks: 3, condition_template: '200000000000000000000001'}},
- res: {status: 'Invalid body format', details: '"material" must be one of [copper, hot air]'}
- });
- });
- it('rejects a condition parameter below minimum range', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {condition: {material: 'copper', weeks: 0, condition_template: '200000000000000000000001'}},
- res: {status: 'Invalid body format', details: '"weeks" must be greater than or equal to 1'}
- });
- });
- it('rejects a condition parameter above maximum range', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {condition: {material: 'copper', weeks: 10.5, condition_template: '200000000000000000000001'}},
- res: {status: 'Invalid body format', details: '"weeks" must be less than or equal to 10'}
- });
- });
- it('rejects a missing condition parameter marked as required', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {condition: {material: 'copper', condition_template: '200000000000000000000001'}},
- res: {status: 'Invalid body format', details: '"weeks" is required'}
- });
- });
- it('accepts a missing condition parameter not marked as required', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {condition: {weeks: 10, condition_template: '200000000000000000000001'}}
- }).end(err => {
- if (err) return done(err);
- done();
- });
- });
- it('rejects an invalid condition template', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {condition: {material: 'copper', weeks: 3, condition_template: '200000000000h00000000001'}},
- res: {status: 'Condition template not available'}
- });
- });
- it('rejects an unknown condition template', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {condition: {material: 'copper', weeks: 3, condition_template: '000000000000000000000001'}},
- res: {status: 'Condition template not available'}
- });
- });
- it('rejects a not accepted type', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {type: 'xx'},
- res: {status: 'Invalid body format', details: '"type" must be one of [as-delivered/raw, processed]'}
- });
- });
- it('allows keeping an empty condition empty', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000006',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {condition: {}},
- res: {_id: '400000000000000000000006', number: 'Rng36', type: 'as-delivered/raw', color: 'black', batch: '', condition: {}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'new', added: '2004-01-10T13:37:04.000Z'}
- });
- });
- it('rejects an old version of a condition template', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {condition: {p1: 36, condition_template: '200000000000000000000004'}},
- res: {status: 'Old template version not allowed'}
- });
- });
- it('allows keeping an old version of a condition template', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000004',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {condition: {p1: 36, condition_template: '200000000000000000000004'}},
- res: {_id: '400000000000000000000004', number: '32', type: 'as-delivered/raw', color: 'black', batch: '1653000308', condition: {p1: 36, condition_template: '200000000000000000000004'}, material_id: '100000000000000000000005', note_id: '500000000000000000000003', user_id: '000000000000000000000003', status: 'new', added: '2004-01-10T13:37:04.000Z'}
- });
- });
- it('rejects changing back to an empty condition', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {condition: {}},
- res: {status: 'Condition template not available'}
- });
- });
- it('rejects editing a deleted sample', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000005',
- auth: {basic: 'admin'},
- httpStatus: 403,
- req: {}
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {key: 'janedoe'},
- httpStatus: 401,
- req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- });
- });
- it('rejects changes for samples from another user for a write user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000003',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {}
- });
- });
- it('accepts changes for samples from another user for a dev/admin user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {},
- res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {condition_template: '200000000000000000000001', material: 'copper', weeks: 3}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'}
- });
- });
- it('rejects requests from a read user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'user'},
- httpStatus: 403,
- req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- });
- });
- it('returns 404 for an unknown sample', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/000000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 404,
- req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
- });
- })
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/400000000000000000000001',
- httpStatus: 401,
- req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- });
- });
- });
+ describe('PUT /sample/{id}', () => {
+ it('returns the right sample', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {},
+ res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'}
+ });
+ });
+ it('keeps unchanged properties', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', notes: {}}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'});
+ SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
+ if (err) return done (err);
+ should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
+ should(data).have.property('_id');
+ should(data).have.property('number', '1');
+ should(data).have.property('color', 'black');
+ should(data).have.property('type', 'as-delivered/raw');
+ should(data).have.property('batch', '');
+ should(data).have.property('condition', {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'});
+ should(data.material_id.toString()).be.eql('100000000000000000000004');
+ should(data.user_id.toString()).be.eql('000000000000000000000002');
+ should(data).have.property('status','validated');
+ should(data).have.property('note_id', null);
+ done();
+ });
+ });
+ });
+ it('keeps only one unchanged parameter', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {type: 'as-delivered/raw'}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'});
+ SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
+ if (err) return done (err);
+ should(data).have.property('status','validated');
+ done();
+ });
+ });
+ });
+ it('keeps an unchanged condition', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'});
+ SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
+ if (err) return done (err);
+ should(data).have.property('status','validated');
+ done();
+ });
+ });
+ });
+ it('keeps unchanged notes', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {notes: {comment: 'Stoff gesperrt', sample_references: []}}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({_id: '400000000000000000000002', number: '21', type: 'as-delivered/raw', color: 'natural', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', note_id: '500000000000000000000001', user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'});
+ SampleModel.findById('400000000000000000000002').lean().exec((err, data: any) => {
+ if (err) return done (err);
+ should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
+ should(data).have.property('_id');
+ should(data).have.property('number', '21');
+ should(data).have.property('color', 'natural');
+ should(data).have.property('type', 'as-delivered/raw');
+ should(data).have.property('batch', '1560237365');
+ should(data.condition).have.property('material', 'copper');
+ should(data.condition).have.property('weeks', 3);
+ should(data.condition.condition_template.toString()).be.eql('200000000000000000000001');
+ should(data.material_id.toString()).be.eql('100000000000000000000001');
+ should(data.user_id.toString()).be.eql('000000000000000000000002');
+ should(data).have.property('status','validated');
+ should(data.note_id.toString()).be.eql('500000000000000000000001');
+ done();
+ });
+ });
+ });
+ it('changes the given properties', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {type: 'processed', color: 'signalviolet', batch: '114531', condition: {condition_template: '200000000000000000000003'}, material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
+ }).end(err => {
+ if (err) return done (err);
+ SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
+ if (err) return done (err);
+ should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
+ should(data).have.property('_id');
+ should(data).have.property('number', '1');
+ should(data).have.property('color', 'signalviolet');
+ should(data).have.property('type', 'processed');
+ should(data).have.property('batch', '114531');
+ should(data).have.property('condition', {condition_template: '200000000000000000000003'});
+ should(data.material_id.toString()).be.eql('100000000000000000000002');
+ should(data.user_id.toString()).be.eql('000000000000000000000002');
+ should(data).have.property('status','new');
+ should(data).have.property('note_id');
+ NoteModel.findById(data.note_id).lean().exec((err, data: any) => {
+ if (err) return done (err);
+ should(data).have.property('_id');
+ should(data).have.property('comment', 'Testcomment');
+ should(data).have.property('sample_references');
+ should(data.sample_references).have.lengthOf(1);
+ should(data.sample_references[0].sample_id.toString()).be.eql('400000000000000000000003');
+ should(data.sample_references[0]).have.property('relation', 'part to this sample');
+ done();
+ });
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {type: 'processed', color: 'signalviolet', batch: '114531', condition: {condition_template: '200000000000000000000003'}, material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ log: {
+ collection: 'samples',
+ dataAdd: {
+ status: 'new'
+ },
+ dataIgn: ['notes', 'note_id']
+ }
+ });
+ });
+ it('adjusts the note_fields correctly', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000003',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {notes: {comment: 'Testcomment', sample_references: [], custom_fields: {field1: 'value 1'}}}
+ }).end(err => {
+ if (err) return done(err);
+ NoteFieldModel.findOne({name: 'not allowed for new applications'}).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.property('qty', 1);
+ NoteFieldModel.findOne({name: 'field1'}).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.property('qty', 1);
+ done();
+ });
+ });
+ });
+ });
+ it('deletes old note_fields', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000004',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {notes: {comment: 'Testcomment', sample_references: []}}
+ }).end(err => {
+ if (err) return done (err);
+ NoteFieldModel.findOne({name: 'another_field'}).lean().exec((err, data) => {
+ if (err) return done (err);
+ should(data).be.null();
+ done();
+ });
+ });
+ });
+ it('keeps untouched notes', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {type: 'processed'}
+ }).end((err, res) => {
+ if (err) return done (err);
+ NoteModel.findById(res.body.note_id).lean().exec((err, data) => {
+ if (err) return done (err);
+ should(data).not.be.null();
+ should(data).have.property('comment', 'Stoff gesperrt');
+ should(data).have.property('sample_references').have.lengthOf(0);
+ done();
+ });
+ });
+ });
+ it('deletes old notes', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000004',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {notes: {comment: 'Testcomment', sample_references: []}}
+ }).end(err => {
+ if (err) return done (err);
+ NoteModel.findById('500000000000000000000003').lean().exec((err, data) => {
+ if (err) return done (err);
+ should(data).be.null();
+ done();
+ });
+ });
+ });
+ it('rejects an unknown material id', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '000000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Material not available'}
+ });
+ });
+ it('rejects a sample number', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {number: 25, type: 'processed', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Invalid body format', details: '"number" is not allowed'}
+ });
+ });
+ it('rejects an invalid sample reference', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '000000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Sample reference not available'}
+ });
+ });
+ it('rejects an invalid material id', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Invalid body format', details: 'Invalid object id'}
+ });
+ });
+ it('rejects an invalid id', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/10000000000h000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404,
+ req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ });
+ });
+ it('rejects not specified condition parameters', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {condition: {material: 'copper', weeks: 3, xxx: 44, condition_template: '200000000000000000000001'}},
+ res: {status: 'Invalid body format', details: '"xxx" is not allowed'}
+ });
+ });
+ it('rejects a condition parameter not in the value range', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {condition: {material: 'xx', weeks: 3, condition_template: '200000000000000000000001'}},
+ res: {status: 'Invalid body format', details: '"material" must be one of [copper, hot air]'}
+ });
+ });
+ it('rejects a condition parameter below minimum range', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {condition: {material: 'copper', weeks: 0, condition_template: '200000000000000000000001'}},
+ res: {status: 'Invalid body format', details: '"weeks" must be greater than or equal to 1'}
+ });
+ });
+ it('rejects a condition parameter above maximum range', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {condition: {material: 'copper', weeks: 10.5, condition_template: '200000000000000000000001'}},
+ res: {status: 'Invalid body format', details: '"weeks" must be less than or equal to 10'}
+ });
+ });
+ it('rejects a missing condition parameter marked as required', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {condition: {material: 'copper', condition_template: '200000000000000000000001'}},
+ res: {status: 'Invalid body format', details: '"weeks" is required'}
+ });
+ });
+ it('accepts a missing condition parameter not marked as required', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {condition: {weeks: 10, condition_template: '200000000000000000000001'}}
+ }).end(err => {
+ if (err) return done(err);
+ done();
+ });
+ });
+ it('rejects an invalid condition template', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {condition: {material: 'copper', weeks: 3, condition_template: '200000000000h00000000001'}},
+ res: {status: 'Condition template not available'}
+ });
+ });
+ it('rejects an unknown condition template', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {condition: {material: 'copper', weeks: 3, condition_template: '000000000000000000000001'}},
+ res: {status: 'Condition template not available'}
+ });
+ });
+ it('rejects a not accepted type', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {type: 'xx'},
+ res: {status: 'Invalid body format', details: '"type" must be one of [as-delivered/raw, processed]'}
+ });
+ });
+ it('allows keeping an empty condition empty', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000006',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {condition: {}},
+ res: {_id: '400000000000000000000006', number: 'Rng36', type: 'as-delivered/raw', color: 'black', batch: '', condition: {}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'new', added: '2004-01-10T13:37:04.000Z'}
+ });
+ });
+ it('rejects an old version of a condition template', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {condition: {p1: 36, condition_template: '200000000000000000000004'}},
+ res: {status: 'Old template version not allowed'}
+ });
+ });
+ it('allows keeping an old version of a condition template', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000004',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {condition: {p1: 36, condition_template: '200000000000000000000004'}},
+ res: {_id: '400000000000000000000004', number: '32', type: 'as-delivered/raw', color: 'black', batch: '1653000308', condition: {p1: 36, condition_template: '200000000000000000000004'}, material_id: '100000000000000000000005', note_id: '500000000000000000000003', user_id: '000000000000000000000003', status: 'new', added: '2004-01-10T13:37:04.000Z'}
+ });
+ });
+ it('rejects changing back to an empty condition', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {condition: {}},
+ res: {status: 'Condition template not available'}
+ });
+ });
+ it('rejects editing a deleted sample', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000005',
+ auth: {basic: 'admin'},
+ httpStatus: 403,
+ req: {}
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {key: 'janedoe'},
+ httpStatus: 401,
+ req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ });
+ });
+ it('rejects changes for samples from another user for a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000003',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {}
+ });
+ });
+ it('accepts changes for samples from another user for a dev/admin user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {},
+ res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {condition_template: '200000000000000000000001', material: 'copper', weeks: 3}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'}
+ });
+ });
+ it('rejects requests from a read user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'user'},
+ httpStatus: 403,
+ req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ });
+ });
+ it('returns 404 for an unknown sample', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/000000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404,
+ req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
+ });
+ })
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/400000000000000000000001',
+ httpStatus: 401,
+ req: {type: 'processed', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ });
+ });
+ });
- describe('DELETE /sample/{id}', () => {
- it('sets the status to deleted', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
- should(data).have.property('_id');
- should(data).have.property('number', '1');
- should(data).have.property('color', 'black');
- should(data).have.property('type', 'as-delivered/raw');
- should(data).have.property('batch', '');
- should(data.condition).have.property('material', 'copper');
- should(data.condition).have.property('weeks', 3);
- should(data.condition.condition_template.toString()).be.eql('200000000000000000000001');
- should(data.material_id.toString()).be.eql('100000000000000000000004');
- should(data.user_id.toString()).be.eql('000000000000000000000002');
- should(data).have.property('status','deleted');
- should(data).have.property('note_id', null);
- done();
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- log: {
- collection: 'samples',
- skip: 1,
- dataAdd: {status: 'deleted'}
- }
- });
- });
- it('keeps the notes of the sample', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/sample/400000000000000000000002',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- NoteModel.findById('500000000000000000000001').lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.only.keys('_id', 'comment', 'sample_references', '__v');
- should(data).have.property('comment', 'Stoff gesperrt');
- should(data).have.property('sample_references').with.lengthOf(0);
- done();
- });
- });
- });
- it('adjusts the note_fields correctly', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/sample/400000000000000000000004',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- NoteFieldModel.findOne({name: 'not allowed for new applications'}).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.property('qty', 1);
- NoteFieldModel.findOne({name: 'another_field'}).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).be.null();
- done();
- });
- });
- });
- });
- it('keeps references to this sample', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/sample/400000000000000000000003',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- setTimeout(() => { // background action takes some time before we can check
- NoteModel.findById('500000000000000000000003').lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).have.property('sample_references').with.lengthOf(1);
- should(data.sample_references[0].sample_id.toString()).be.eql('400000000000000000000003');
- should(data.sample_references[0]).have.property('relation', 'part to sample');
- done();
- });
- }, 100);
+ describe('DELETE /sample/{id}', () => {
+ it('sets the status to deleted', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
+ should(data).have.property('_id');
+ should(data).have.property('number', '1');
+ should(data).have.property('color', 'black');
+ should(data).have.property('type', 'as-delivered/raw');
+ should(data).have.property('batch', '');
+ should(data.condition).have.property('material', 'copper');
+ should(data.condition).have.property('weeks', 3);
+ should(data.condition.condition_template.toString()).be.eql('200000000000000000000001');
+ should(data.material_id.toString()).be.eql('100000000000000000000004');
+ should(data.user_id.toString()).be.eql('000000000000000000000002');
+ should(data).have.property('status','deleted');
+ should(data).have.property('note_id', null);
+ done();
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ log: {
+ collection: 'samples',
+ skip: 1,
+ dataAdd: {status: 'deleted'}
+ }
+ });
+ });
+ it('keeps the notes of the sample', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/sample/400000000000000000000002',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ NoteModel.findById('500000000000000000000001').lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.only.keys('_id', 'comment', 'sample_references', '__v');
+ should(data).have.property('comment', 'Stoff gesperrt');
+ should(data).have.property('sample_references').with.lengthOf(0);
+ done();
+ });
+ });
+ });
+ it('adjusts the note_fields correctly', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/sample/400000000000000000000004',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ NoteFieldModel.findOne({name: 'not allowed for new applications'}).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.property('qty', 1);
+ NoteFieldModel.findOne({name: 'another_field'}).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).be.null();
+ done();
+ });
+ });
+ });
+ });
+ it('keeps references to this sample', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/sample/400000000000000000000003',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ setTimeout(() => { // background action takes some time before we can check
+ NoteModel.findById('500000000000000000000003').lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).have.property('sample_references').with.lengthOf(1);
+ should(data.sample_references[0].sample_id.toString()).be.eql('400000000000000000000003');
+ should(data.sample_references[0]).have.property('relation', 'part to sample');
+ done();
+ });
+ }, 100);
- });
- });
- it('lets admin/dev users delete samples of other users', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- SampleModel.findById('400000000000000000000001').lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.property('status','deleted');
- done();
- });
- });
- });
- it('deletes associated measurements', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/sample/400000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- MeasurementModel.find({sample_id: mongoose.Types.ObjectId('400000000000000000000001')}).lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).matchEach(sample => {
- should(sample).have.property('status', 'deleted');
- });
- done();
- });
- });
- });
- it('rejects deleting samples of other users for write users', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/sample/400000000000000000000004',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('rejects an invalid id', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/sample/400000000000h00000000004',
- auth: {basic: 'janedoe'},
- httpStatus: 404
- });
- });
- it('rejects requests from a read user', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/sample/400000000000000000000004',
- auth: {basic: 'user'},
- httpStatus: 403
- });
- });
- it('returns 404 for an unknown id', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/sample/000000000000000000000004',
- auth: {basic: 'janedoe'},
- httpStatus: 404
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/sample/400000000000000000000001',
- auth: {key: 'janedoe'},
- httpStatus: 401
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/sample/400000000000000000000001',
- httpStatus: 401
- });
- });
- });
+ });
+ });
+ it('lets admin/dev users delete samples of other users', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ SampleModel.findById('400000000000000000000001').lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.property('status','deleted');
+ done();
+ });
+ });
+ });
+ it('deletes associated measurements', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/sample/400000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ MeasurementModel.find({sample_id: mongoose.Types.ObjectId('400000000000000000000001')}).lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).matchEach(sample => {
+ should(sample).have.property('status', 'deleted');
+ });
+ done();
+ });
+ });
+ });
+ it('rejects deleting samples of other users for write users', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/sample/400000000000000000000004',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('rejects an invalid id', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/sample/400000000000h00000000004',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404
+ });
+ });
+ it('rejects requests from a read user', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/sample/400000000000000000000004',
+ auth: {basic: 'user'},
+ httpStatus: 403
+ });
+ });
+ it('returns 404 for an unknown id', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/sample/000000000000000000000004',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/sample/400000000000000000000001',
+ auth: {key: 'janedoe'},
+ httpStatus: 401
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/sample/400000000000000000000001',
+ httpStatus: 401
+ });
+ });
+ });
- describe('GET /sample/number/{number}', () => {
- it('returns the right sample', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/number/33',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- res: {_id: '400000000000000000000003', number: '33', type: 'processed', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'as-delivered/raw to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', user: 'admin'}
- });
- });
- it('works with an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/number/33',
- auth: {key: 'janedoe'},
- httpStatus: 200,
- res: {_id: '400000000000000000000003', number: '33', type: 'processed', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'as-delivered/raw to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', user: 'admin'}
- });
- });
- it('returns a deleted sample for a dev/admin user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/number/Rng33',
- auth: {basic: 'admin'},
- httpStatus: 200,
- res: {_id: '400000000000000000000005', number: 'Rng33', type: 'as-delivered/raw', color: 'black', batch: '1653000308', condition: {condition_template: '200000000000000000000003'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {}, measurements: [], status: 'deleted', user: 'admin'}
- });
- });
- it ('filters out spectral data for a write user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/number/1',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {device: 'Alpha II', filename: '1_1.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000009', sample_id: '400000000000000000000001', values: {device: 'Alpha II', filename: '1_2.DPT'}, measurement_template: '300000000000000000000001'}], status: 'validated'}
- });
- });
- it ('returns spectral data for an admin user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/number/1',
- auth: {basic: 'admin'},
- httpStatus: 200,
- res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[ 3997.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]],device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {dpt: [[ 3996.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]], device: 'Alpha II', filename: '1_1.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000009', sample_id: '400000000000000000000001', values: {dpt: [[ 3996.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]], device: 'Alpha II', filename: '1_2.DPT'}, measurement_template: '300000000000000000000001'}], status: 'validated'}
- });
- });
- it('returns 403 for a write user when requesting a deleted sample', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/number/Rng33',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('returns 404 for an unknown sample', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/number/Rng883',
- auth: {basic: 'janedoe'},
- httpStatus: 404
- });
- });
- it('rejects an invalid id', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/number/xx-xx',
- auth: {basic: 'janedoe'},
- httpStatus: 404
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/number/33',
- httpStatus: 401
- });
- });
- });
+ describe('GET /sample/number/{number}', () => {
+ it('returns the right sample', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/number/33',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ res: {_id: '400000000000000000000003', number: '33', type: 'processed', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'as-delivered/raw to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', user: 'admin'}
+ });
+ });
+ it('works with an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/number/33',
+ auth: {key: 'janedoe'},
+ httpStatus: 200,
+ res: {_id: '400000000000000000000003', number: '33', type: 'processed', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'as-delivered/raw to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', user: 'admin'}
+ });
+ });
+ it('returns a deleted sample for a dev/admin user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/number/Rng33',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ res: {_id: '400000000000000000000005', number: 'Rng33', type: 'as-delivered/raw', color: 'black', batch: '1653000308', condition: {condition_template: '200000000000000000000003'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {}, measurements: [], status: 'deleted', user: 'admin'}
+ });
+ });
+ it ('filters out spectral data for a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/number/1',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {device: 'Alpha II', filename: '1_1.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000009', sample_id: '400000000000000000000001', values: {device: 'Alpha II', filename: '1_2.DPT'}, measurement_template: '300000000000000000000001'}], status: 'validated'}
+ });
+ });
+ it ('returns spectral data for an admin user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/number/1',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[ 3997.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]],device: 'Alpha I', filename: '1_0.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {dpt: [[ 3996.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]], device: 'Alpha II', filename: '1_1.DPT'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000009', sample_id: '400000000000000000000001', values: {dpt: [[ 3996.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]], device: 'Alpha II', filename: '1_2.DPT'}, measurement_template: '300000000000000000000001'}], status: 'validated'}
+ });
+ });
+ it('returns 403 for a write user when requesting a deleted sample', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/number/Rng33',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('returns 404 for an unknown sample', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/number/Rng883',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404
+ });
+ });
+ it('rejects an invalid id', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/number/xx-xx',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/number/33',
+ httpStatus: 401
+ });
+ });
+ });
- describe('PUT /sample/restore/{id}', () => {
- it('sets the status', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/restore/400000000000000000000005',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- SampleModel.findById('400000000000000000000005').lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).have.property('status','new');
- done();
- });
- });
- });
- it('restores associated measurements', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/restore/400000000000000000000005',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- MeasurementModel.find({sample_id: mongoose.Types.ObjectId('400000000000000000000005')}).lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).matchEach(measurement => {
- should(measurement).have.property('status', 'new')
- });
- done();
- });
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/restore/400000000000000000000005',
- auth: {key: 'admin'},
- httpStatus: 401,
- req: {}
- });
- });
- it('rejects a write user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/restore/400000000000000000000005',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {}
- });
- });
- it('returns 404 for an unknown sample', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/restore/000000000000000000000005',
- auth: {basic: 'admin'},
- httpStatus: 404,
- req: {}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/restore/400000000000000000000005',
- httpStatus: 401,
- req: {}
- });
- });
- });
+ describe('PUT /sample/restore/{id}', () => {
+ it('sets the status', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/restore/400000000000000000000005',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ SampleModel.findById('400000000000000000000005').lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).have.property('status','new');
+ done();
+ });
+ });
+ });
+ it('restores associated measurements', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/restore/400000000000000000000005',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ MeasurementModel.find({sample_id: mongoose.Types.ObjectId('400000000000000000000005')}).lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).matchEach(measurement => {
+ should(measurement).have.property('status', 'new')
+ });
+ done();
+ });
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/restore/400000000000000000000005',
+ auth: {key: 'admin'},
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ it('rejects a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/restore/400000000000000000000005',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {}
+ });
+ });
+ it('returns 404 for an unknown sample', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/restore/000000000000000000000005',
+ auth: {basic: 'admin'},
+ httpStatus: 404,
+ req: {}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/restore/400000000000000000000005',
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ });
- describe('PUT /sample/validate/{id}', () => {
- it('sets the status', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/validate/400000000000000000000003',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- SampleModel.findById('400000000000000000000003').lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).have.property('status','validated');
- done();
- });
- });
- });
- it('validates associated measurements', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/validate/400000000000000000000003',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- MeasurementModel.find({sample_id: mongoose.Types.ObjectId('400000000000000000000003')}).lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).matchEach(measurement => {
- should(measurement).have.property('status', 'validated')
- });
- done();
- });
- });
- });
- it('allows validating a sample without condition', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/validate/400000000000000000000006',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- SampleModel.findById('400000000000000000000006').lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).have.property('status','validated');
- done();
- });
- });
- });
- it('allows validating a sample without measurements', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/validate/400000000000000000000004',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- SampleModel.findById('400000000000000000000004').lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).have.property('status','validated');
- done();
- });
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/validate/400000000000000000000003',
- auth: {key: 'admin'},
- httpStatus: 401,
- req: {}
- });
- });
- it('rejects a write user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/validate/400000000000000000000003',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {}
- });
- });
- it('returns 404 for an unknown sample', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/validate/000000000000000000000003',
- auth: {basic: 'admin'},
- httpStatus: 404,
- req: {}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/sample/validate/400000000000000000000003',
- httpStatus: 401,
- req: {}
- });
- });
- });
+ describe('PUT /sample/validate/{id}', () => {
+ it('sets the status', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/validate/400000000000000000000003',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ SampleModel.findById('400000000000000000000003').lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).have.property('status','validated');
+ done();
+ });
+ });
+ });
+ it('validates associated measurements', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/validate/400000000000000000000003',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ MeasurementModel.find({sample_id: mongoose.Types.ObjectId('400000000000000000000003')}).lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).matchEach(measurement => {
+ should(measurement).have.property('status', 'validated')
+ });
+ done();
+ });
+ });
+ });
+ it('allows validating a sample without condition', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/validate/400000000000000000000006',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ SampleModel.findById('400000000000000000000006').lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).have.property('status','validated');
+ done();
+ });
+ });
+ });
+ it('allows validating a sample without measurements', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/validate/400000000000000000000004',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ SampleModel.findById('400000000000000000000004').lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).have.property('status','validated');
+ done();
+ });
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/validate/400000000000000000000003',
+ auth: {key: 'admin'},
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ it('rejects a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/validate/400000000000000000000003',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {}
+ });
+ });
+ it('returns 404 for an unknown sample', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/validate/000000000000000000000003',
+ auth: {basic: 'admin'},
+ httpStatus: 404,
+ req: {}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/sample/validate/400000000000000000000003',
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ });
- describe('POST /sample/new', () => {
- it('returns the right sample', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added');
- should(res.body).have.property('_id').be.type('string');
- should(res.body).have.property('number', 'Rng37');
- should(res.body).have.property('color', 'black');
- should(res.body).have.property('type', 'as-delivered/raw');
- should(res.body).have.property('batch', '1560237365');
- should(res.body).have.property('condition', {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'});
- should(res.body).have.property('material_id', '100000000000000000000001');
- should(res.body).have.property('note_id').be.type('string');
- should(res.body).have.property('user_id', '000000000000000000000002');
- should(res.body).have.property('status', 'new');
- should(res.body).have.property('added').be.type('string');
- should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(2000);
- done();
- });
- });
- it('stores the sample', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
- }).end(err => {
- if (err) return done (err);
- SampleModel.find({number: 'Rng37'}).lean().exec((err, data: any) => {
- if (err) return done (err);
- should(data).have.lengthOf(1);
- should(data[0]).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
- should(data[0]).have.property('_id');
- should(data[0]).have.property('number', 'Rng37');
- should(data[0]).have.property('color', 'black');
- should(data[0]).have.property('type', 'as-delivered/raw');
- should(data[0]).have.property('batch', '1560237365');
- should(data[0]).have.property('condition', {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'});
- should(data[0].material_id.toString()).be.eql('100000000000000000000001');
- should(data[0].user_id.toString()).be.eql('000000000000000000000002');
- should(data[0]).have.property('status','new');
- should(data[0]).have.property('note_id');
- NoteModel.findById(data[0].note_id).lean().exec((err, data: any) => {
- if (err) return done (err);
- should(data).have.property('_id');
- should(data).have.property('comment', 'Testcomment');
- should(data).have.property('sample_references');
- should(data.sample_references).have.lengthOf(1);
- should(data.sample_references[0].sample_id.toString()).be.eql('400000000000000000000003');
- should(data.sample_references[0]).have.property('relation', 'part to this sample');
- done();
- });
- })
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- log: {
- collection: 'samples',
- dataAdd: {
- number: 'Rng37',
- user_id: '000000000000000000000002',
- status: 'new'
- },
- dataIgn: ['notes', 'note_id']
- }
- });
- });
- it('stores the custom fields', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [], custom_fields: {field1: 'a', field2: 'b', 'not allowed for new applications': true}}}
- }).end((err, res) => {
- if (err) return done (err);
- NoteModel.findById(res.body.note_id).lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).have.property('_id');
- should(data).have.property('comment', 'Testcomment');
- should(data).have.property('sample_references').have.lengthOf(0);
- should(data).have.property('custom_fields');
- should(data.custom_fields).have.property('field1', 'a');
- should(data.custom_fields).have.property('field2', 'b');
- should(data.custom_fields).have.property('not allowed for new applications', true);
- NoteFieldModel.find({name: 'field1'}).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]).have.property('qty', 1);
- NoteFieldModel.find({name: 'field2'}).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]).have.property('qty', 1);
- NoteFieldModel.find({name: 'not allowed for new applications'}).lean().exec((err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]).have.property('qty', 3);
- done();
- });
- });
- });
- });
- });
- });
- it('stores a new sample location as 1', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'johnnydoe'},
- httpStatus: 200,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added');
- should(res.body).have.property('_id').be.type('string');
- should(res.body).have.property('number', 'Fe1');
- should(res.body).have.property('color', 'black');
- should(res.body).have.property('type', 'as-delivered/raw');
- should(res.body).have.property('batch', '1560237365');
- should(res.body).have.property('material_id', '100000000000000000000001');
- should(res.body).have.property('note_id').be.type('string');
- should(res.body).have.property('user_id', '000000000000000000000004');
- should(res.body).have.property('status', 'new');
- should(res.body).have.property('added').be.type('string');
- should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1500);
- done();
- });
- });
- it('accepts a sample without condition', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added');
- should(res.body).have.property('_id').be.type('string');
- should(res.body).have.property('number', 'Rng37');
- should(res.body).have.property('color', 'black');
- should(res.body).have.property('type', 'as-delivered/raw');
- should(res.body).have.property('batch', '1560237365');
- should(res.body).have.property('condition', {});
- should(res.body).have.property('material_id', '100000000000000000000001');
- should(res.body).have.property('note_id').be.type('string');
- should(res.body).have.property('user_id', '000000000000000000000002');
- should(res.body).have.property('status', 'new');
- should(res.body).have.property('added').be.type('string');
- should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1000);
- done();
- });
- });
- it('rejects an unknown material id', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '000000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Material not available'}
- });
- });
- it('rejects a sample number for a write user', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {number: 'Rng34', color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Invalid body format', details: '"number" is not allowed'}
- });
- });
- it('allows a sample number for an admin user', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {number: 'Rng34', color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added');
- should(res.body).have.property('_id').be.type('string');
- should(res.body).have.property('number', 'Rng34');
- should(res.body).have.property('color', 'black');
- should(res.body).have.property('type', 'as-delivered/raw');
- should(res.body).have.property('batch', '1560237365');
- should(res.body).have.property('condition', {});
- should(res.body).have.property('material_id', '100000000000000000000001');
- should(res.body).have.property('note_id').be.type('string');
- should(res.body).have.property('user_id', '000000000000000000000003');
- should(res.body).have.property('status', 'new');
- should(res.body).have.property('added').be.type('string');
- should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1000);
- done();
- });
- });
- it('rejects an existing sample number for an admin user', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {number: 'Rng33', color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Sample number already taken'}
- });
- });
- it('rejects an invalid sample reference', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '000000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Sample reference not available'}
- });
- });
- it('rejects an invalid condition_template id', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '20000h000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Condition template not available'}
- });
- });
- it('rejects a not existing condition_template id', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '000000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Condition template not available'}
- });
- });
- it('rejects not specified condition parameters', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 3, xxx: 23, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Invalid body format', details: '"xxx" is not allowed'}
- });
- });
- it('rejects missing condition parameters', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: { status: 'Invalid body format', details: '"weeks" is required'}
- });
- });
- it('rejects condition parameters not in the value range', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'xxx', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Invalid body format', details: '"material" must be one of [copper, hot air]'}
- });
- });
- it('rejects a condition parameter below minimum range', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 0, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Invalid body format', details: '"weeks" must be greater than or equal to 1'}
- });
- });
- it('rejects a condition parameter above maximum range', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 11, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Invalid body format', details: '"weeks" must be less than or equal to 10'}
- });
- });
- it('rejects a missing condition parameter marked as required', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Invalid body format', details: '"weeks" is required'}
- });
- });
- it('accepts a missing condition parameter not marked as required', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: { weeks: 10, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- }).end(err => {
- if (err) return done(err);
- done();
- });
- });
- it('rejects a condition without condition template', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 3}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Condition template not available'}
- });
- });
- it('rejects an old version of a condition template', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {p1: 36, condition_template: '200000000000000000000004'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Old template version not allowed'}
- });
- });
- it('rejects a missing color', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Invalid body format', details: '"color" is required'}
- });
- });
- it('rejects a missing type', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Invalid body format', details: '"type" is required'}
- });
- });
- it('rejects a missing batch', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'as-delivered/raw', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Invalid body format', details: '"batch" is required'}
- });
- });
- it('rejects a missing material id', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Invalid body format', details: '"material_id" is required'}
- });
- });
- it('rejects an invalid material id', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
- res: {status: 'Invalid body format', details: 'Invalid object id'}
- });
- });
- it('rejects a not accepted type', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'janedoe'},
- httpStatus: 400,
- req: {color: 'black', type: 'xx', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment'}},
- res: {status: 'Invalid body format', details: '"type" must be one of [as-delivered/raw, processed]'}
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {key: 'janedoe'},
- httpStatus: 401,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
- });
- });
- it('rejects requests from a read user', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- auth: {basic: 'user'},
- httpStatus: 403,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/sample/new',
- httpStatus: 401,
- req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
- });
- });
- });
+ describe('POST /sample/new', () => {
+ it('returns the right sample', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added');
+ should(res.body).have.property('_id').be.type('string');
+ should(res.body).have.property('number', 'Rng37');
+ should(res.body).have.property('color', 'black');
+ should(res.body).have.property('type', 'as-delivered/raw');
+ should(res.body).have.property('batch', '1560237365');
+ should(res.body).have.property('condition', {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'});
+ should(res.body).have.property('material_id', '100000000000000000000001');
+ should(res.body).have.property('note_id').be.type('string');
+ should(res.body).have.property('user_id', '000000000000000000000002');
+ should(res.body).have.property('status', 'new');
+ should(res.body).have.property('added').be.type('string');
+ should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(2000);
+ done();
+ });
+ });
+ it('stores the sample', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
+ }).end(err => {
+ if (err) return done (err);
+ SampleModel.find({number: 'Rng37'}).lean().exec((err, data: any) => {
+ if (err) return done (err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
+ should(data[0]).have.property('_id');
+ should(data[0]).have.property('number', 'Rng37');
+ should(data[0]).have.property('color', 'black');
+ should(data[0]).have.property('type', 'as-delivered/raw');
+ should(data[0]).have.property('batch', '1560237365');
+ should(data[0]).have.property('condition', {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'});
+ should(data[0].material_id.toString()).be.eql('100000000000000000000001');
+ should(data[0].user_id.toString()).be.eql('000000000000000000000002');
+ should(data[0]).have.property('status','new');
+ should(data[0]).have.property('note_id');
+ NoteModel.findById(data[0].note_id).lean().exec((err, data: any) => {
+ if (err) return done (err);
+ should(data).have.property('_id');
+ should(data).have.property('comment', 'Testcomment');
+ should(data).have.property('sample_references');
+ should(data.sample_references).have.lengthOf(1);
+ should(data.sample_references[0].sample_id.toString()).be.eql('400000000000000000000003');
+ should(data.sample_references[0]).have.property('relation', 'part to this sample');
+ done();
+ });
+ })
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ log: {
+ collection: 'samples',
+ dataAdd: {
+ number: 'Rng37',
+ user_id: '000000000000000000000002',
+ status: 'new'
+ },
+ dataIgn: ['notes', 'note_id']
+ }
+ });
+ });
+ it('stores the custom fields', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [], custom_fields: {field1: 'a', field2: 'b', 'not allowed for new applications': true}}}
+ }).end((err, res) => {
+ if (err) return done (err);
+ NoteModel.findById(res.body.note_id).lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).have.property('_id');
+ should(data).have.property('comment', 'Testcomment');
+ should(data).have.property('sample_references').have.lengthOf(0);
+ should(data).have.property('custom_fields');
+ should(data.custom_fields).have.property('field1', 'a');
+ should(data.custom_fields).have.property('field2', 'b');
+ should(data.custom_fields).have.property('not allowed for new applications', true);
+ NoteFieldModel.find({name: 'field1'}).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.property('qty', 1);
+ NoteFieldModel.find({name: 'field2'}).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.property('qty', 1);
+ NoteFieldModel.find({name: 'not allowed for new applications'}).lean().exec((err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.property('qty', 3);
+ done();
+ });
+ });
+ });
+ });
+ });
+ });
+ it('stores a new sample location as 1', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'johnnydoe'},
+ httpStatus: 200,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added');
+ should(res.body).have.property('_id').be.type('string');
+ should(res.body).have.property('number', 'Fe1');
+ should(res.body).have.property('color', 'black');
+ should(res.body).have.property('type', 'as-delivered/raw');
+ should(res.body).have.property('batch', '1560237365');
+ should(res.body).have.property('material_id', '100000000000000000000001');
+ should(res.body).have.property('note_id').be.type('string');
+ should(res.body).have.property('user_id', '000000000000000000000004');
+ should(res.body).have.property('status', 'new');
+ should(res.body).have.property('added').be.type('string');
+ should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1500);
+ done();
+ });
+ });
+ it('accepts a sample without condition', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added');
+ should(res.body).have.property('_id').be.type('string');
+ should(res.body).have.property('number', 'Rng37');
+ should(res.body).have.property('color', 'black');
+ should(res.body).have.property('type', 'as-delivered/raw');
+ should(res.body).have.property('batch', '1560237365');
+ should(res.body).have.property('condition', {});
+ should(res.body).have.property('material_id', '100000000000000000000001');
+ should(res.body).have.property('note_id').be.type('string');
+ should(res.body).have.property('user_id', '000000000000000000000002');
+ should(res.body).have.property('status', 'new');
+ should(res.body).have.property('added').be.type('string');
+ should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1000);
+ done();
+ });
+ });
+ it('rejects an unknown material id', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '000000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Material not available'}
+ });
+ });
+ it('rejects a sample number for a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {number: 'Rng34', color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Invalid body format', details: '"number" is not allowed'}
+ });
+ });
+ it('allows a sample number for an admin user', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {number: 'Rng34', color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added');
+ should(res.body).have.property('_id').be.type('string');
+ should(res.body).have.property('number', 'Rng34');
+ should(res.body).have.property('color', 'black');
+ should(res.body).have.property('type', 'as-delivered/raw');
+ should(res.body).have.property('batch', '1560237365');
+ should(res.body).have.property('condition', {});
+ should(res.body).have.property('material_id', '100000000000000000000001');
+ should(res.body).have.property('note_id').be.type('string');
+ should(res.body).have.property('user_id', '000000000000000000000003');
+ should(res.body).have.property('status', 'new');
+ should(res.body).have.property('added').be.type('string');
+ should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1000);
+ done();
+ });
+ });
+ it('rejects an existing sample number for an admin user', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {number: 'Rng33', color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Sample number already taken'}
+ });
+ });
+ it('rejects an invalid sample reference', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '000000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Sample reference not available'}
+ });
+ });
+ it('rejects an invalid condition_template id', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '20000h000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Condition template not available'}
+ });
+ });
+ it('rejects a not existing condition_template id', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '000000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Condition template not available'}
+ });
+ });
+ it('rejects not specified condition parameters', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 3, xxx: 23, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Invalid body format', details: '"xxx" is not allowed'}
+ });
+ });
+ it('rejects missing condition parameters', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: { status: 'Invalid body format', details: '"weeks" is required'}
+ });
+ });
+ it('rejects condition parameters not in the value range', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'xxx', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Invalid body format', details: '"material" must be one of [copper, hot air]'}
+ });
+ });
+ it('rejects a condition parameter below minimum range', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 0, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Invalid body format', details: '"weeks" must be greater than or equal to 1'}
+ });
+ });
+ it('rejects a condition parameter above maximum range', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 11, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Invalid body format', details: '"weeks" must be less than or equal to 10'}
+ });
+ });
+ it('rejects a missing condition parameter marked as required', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Invalid body format', details: '"weeks" is required'}
+ });
+ });
+ it('accepts a missing condition parameter not marked as required', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: { weeks: 10, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ }).end(err => {
+ if (err) return done(err);
+ done();
+ });
+ });
+ it('rejects a condition without condition template', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {material: 'copper', weeks: 3}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Condition template not available'}
+ });
+ });
+ it('rejects an old version of a condition template', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {p1: 36, condition_template: '200000000000000000000004'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Old template version not allowed'}
+ });
+ });
+ it('rejects a missing color', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Invalid body format', details: '"color" is required'}
+ });
+ });
+ it('rejects a missing type', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Invalid body format', details: '"type" is required'}
+ });
+ });
+ it('rejects a missing batch', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'as-delivered/raw', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Invalid body format', details: '"batch" is required'}
+ });
+ });
+ it('rejects a missing material id', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Invalid body format', details: '"material_id" is required'}
+ });
+ });
+ it('rejects an invalid material id', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
+ res: {status: 'Invalid body format', details: 'Invalid object id'}
+ });
+ });
+ it('rejects a not accepted type', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400,
+ req: {color: 'black', type: 'xx', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment'}},
+ res: {status: 'Invalid body format', details: '"type" must be one of [as-delivered/raw, processed]'}
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {key: 'janedoe'},
+ httpStatus: 401,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
+ });
+ });
+ it('rejects requests from a read user', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ auth: {basic: 'user'},
+ httpStatus: 403,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/sample/new',
+ httpStatus: 401,
+ req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
+ });
+ });
+ });
- describe('GET /sample/notes/fields', () => {
- it('returns all fields', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/notes/fields',
- auth: {basic: 'user'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.note_fields.length);
- should(res.body).matchEach(material => {
- should(material).have.only.keys('name', 'qty');
- should(material).have.property('qty').be.type('number');
- });
- done();
- });
- });
- it('works with an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/notes/fields',
- auth: {key: 'user'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.note_fields.length);
- should(res.body).matchEach(material => {
- should(material).have.only.keys('name', 'qty');
- should(material).have.property('qty').be.type('number');
- });
- done();
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/sample/notes/fields',
- httpStatus: 401
- });
- });
- });
+ describe('GET /sample/notes/fields', () => {
+ it('returns all fields', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/notes/fields',
+ auth: {basic: 'user'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.note_fields.length);
+ should(res.body).matchEach(material => {
+ should(material).have.only.keys('name', 'qty');
+ should(material).have.property('qty').be.type('number');
+ });
+ done();
+ });
+ });
+ it('works with an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/notes/fields',
+ auth: {key: 'user'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.note_fields.length);
+ should(res.body).matchEach(material => {
+ should(material).have.only.keys('name', 'qty');
+ should(material).have.property('qty').be.type('number');
+ });
+ done();
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/sample/notes/fields',
+ httpStatus: 401
+ });
+ });
+ });
});
diff --git a/src/routes/sample.ts b/src/routes/sample.ts
index cd97c01..8d47279 100644
--- a/src/routes/sample.ts
+++ b/src/routes/sample.ts
@@ -24,650 +24,650 @@ const router = express.Router();
router.get('/samples', async (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} = SampleValidate.query(req.query, ['dev', 'admin'].indexOf(req.authDetails.level) >= 0);
- if (error) return res400(error, res);
+ const {error, value: filters} = SampleValidate.query(req.query, ['dev', 'admin'].indexOf(req.authDetails.level) >= 0);
+ if (error) return res400(error, res);
- // spectral data and csv not allowed for read/write users
- if ((filters.fields.find(e => e.indexOf('.' + globals.spectrum.dpt) >= 0) || filters.output !== 'json') &&
- !req.auth(res, ['dev', 'admin'], 'all')) return;
+ // spectral data and csv not allowed for read/write users
+ if ((filters.fields.find(e => e.indexOf('.' + globals.spectrum.dpt) >= 0) || filters.output !== 'json') &&
+ !req.auth(res, ['dev', 'admin'], 'all')) return;
- // evaluate sort parameter from 'color-asc' to ['color', 1]
- filters.sort = filters.sort.split('-');
- filters.sort[0] = filters.sort[0] === 'added' ? '_id' : filters.sort[0]; // route added sorting criteria to _id
- filters.sort[1] = filters.sort[1] === 'desc' ? -1 : 1;
- if (!filters['to-page']) { // set to-page default
- filters['to-page'] = 0;
- }
- const addedFilter = filters.filters.find(e => e.field === 'added');
- if (addedFilter) { // convert added filter to object id
- filters.filters.splice(filters.filters.findIndex(e => e.field === 'added'), 1);
- if (addedFilter.mode === 'in') {
- const v = []; // query value
- addedFilter.values.forEach(value => {
- const date = [new Date(value).setHours(0,0,0,0), new Date(value).setHours(23,59,59,999)];
- v.push({$and: [{ _id: { '$gte': dateToOId(date[0])}}, { _id: { '$lte': dateToOId(date[1])}}]});
- });
- filters.filters.push({mode: 'or', field: '_id', values: v});
- }
- else if (addedFilter.mode === 'nin') {
- addedFilter.values = addedFilter.values.sort();
- const v = []; // query value
+ // evaluate sort parameter from 'color-asc' to ['color', 1]
+ filters.sort = filters.sort.split('-');
+ filters.sort[0] = filters.sort[0] === 'added' ? '_id' : filters.sort[0]; // route added sorting criteria to _id
+ filters.sort[1] = filters.sort[1] === 'desc' ? -1 : 1;
+ if (!filters['to-page']) { // set to-page default
+ filters['to-page'] = 0;
+ }
+ const addedFilter = filters.filters.find(e => e.field === 'added');
+ if (addedFilter) { // convert added filter to object id
+ filters.filters.splice(filters.filters.findIndex(e => e.field === 'added'), 1);
+ if (addedFilter.mode === 'in') {
+ const v = []; // query value
+ addedFilter.values.forEach(value => {
+ const date = [new Date(value).setHours(0,0,0,0), new Date(value).setHours(23,59,59,999)];
+ v.push({$and: [{ _id: { '$gte': dateToOId(date[0])}}, { _id: { '$lte': dateToOId(date[1])}}]});
+ });
+ filters.filters.push({mode: 'or', field: '_id', values: v});
+ }
+ else if (addedFilter.mode === 'nin') {
+ addedFilter.values = addedFilter.values.sort();
+ const v = []; // query value
- for (let i = 0; i <= addedFilter.values.length; i ++) {
- v[i] = {$and: []};
- if (i > 0) {
- const date = new Date(addedFilter.values[i - 1]).setHours(23,59,59,999);
- v[i].$and.push({ _id: { '$gt': dateToOId(date)}}) ;
- }
- if (i < addedFilter.values.length) {
- const date = new Date(addedFilter.values[i]).setHours(0,0,0,0);
- v[i].$and.push({ _id: { '$lt': dateToOId(date)}}) ;
- }
- }
- filters.filters.push({mode: 'or', field: '_id', values: v});
- }
- else {
- // start and end of day
- const date = [new Date(addedFilter.values[0]).setHours(0,0,0,0),
- new Date(addedFilter.values[0]).setHours(23,59,59,999)];
- if (addedFilter.mode === 'lt') { // lt start
- filters.filters.push({mode: 'lt', field: '_id', values: [dateToOId(date[0])]});
- }
- if (addedFilter.mode === 'eq' || addedFilter.mode === 'lte') { // lte end
- filters.filters.push({mode: 'lte', field: '_id', values: [dateToOId(date[1])]});
- }
- if (addedFilter.mode === 'gt') { // gt end
- filters.filters.push({mode: 'gt', field: '_id', values: [dateToOId(date[1])]});
- }
- if (addedFilter.mode === 'eq' || addedFilter.mode === 'gte') { // gte start
- filters.filters.push({mode: 'gte', field: '_id', values: [dateToOId(date[0])]});
- }
- if (addedFilter.mode === 'ne') {
- filters.filters.push({mode: 'or', field: '_id',
- values: [{ _id: { '$lt': dateToOId(date[0])}}, { _id: { '$gt': dateToOId(date[1])}}]});
- }
- }
- }
+ for (let i = 0; i <= addedFilter.values.length; i ++) {
+ v[i] = {$and: []};
+ if (i > 0) {
+ const date = new Date(addedFilter.values[i - 1]).setHours(23,59,59,999);
+ v[i].$and.push({ _id: { '$gt': dateToOId(date)}}) ;
+ }
+ if (i < addedFilter.values.length) {
+ const date = new Date(addedFilter.values[i]).setHours(0,0,0,0);
+ v[i].$and.push({ _id: { '$lt': dateToOId(date)}}) ;
+ }
+ }
+ filters.filters.push({mode: 'or', field: '_id', values: v});
+ }
+ else {
+ // start and end of day
+ const date = [new Date(addedFilter.values[0]).setHours(0,0,0,0),
+ new Date(addedFilter.values[0]).setHours(23,59,59,999)];
+ if (addedFilter.mode === 'lt') { // lt start
+ filters.filters.push({mode: 'lt', field: '_id', values: [dateToOId(date[0])]});
+ }
+ if (addedFilter.mode === 'eq' || addedFilter.mode === 'lte') { // lte end
+ filters.filters.push({mode: 'lte', field: '_id', values: [dateToOId(date[1])]});
+ }
+ if (addedFilter.mode === 'gt') { // gt end
+ filters.filters.push({mode: 'gt', field: '_id', values: [dateToOId(date[1])]});
+ }
+ if (addedFilter.mode === 'eq' || addedFilter.mode === 'gte') { // gte start
+ filters.filters.push({mode: 'gte', field: '_id', values: [dateToOId(date[0])]});
+ }
+ if (addedFilter.mode === 'ne') {
+ filters.filters.push({mode: 'or', field: '_id',
+ values: [{ _id: { '$lt': dateToOId(date[0])}}, { _id: { '$gt': dateToOId(date[1])}}]});
+ }
+ }
+ }
- const sortFilterKeys = filters.filters.map(e => e.field);
+ const sortFilterKeys = filters.filters.map(e => e.field);
- let collection;
- const query = [];
- let queryPtr = query;
- queryPtr.push({$match: {$and: []}});
+ let collection;
+ const query = [];
+ let queryPtr = query;
+ queryPtr.push({$match: {$and: []}});
- if (filters.sort[0].indexOf('measurements.') >= 0) { // sorting with measurements as starting collection
- collection = MeasurementModel;
- const [,measurementName, measurementParam] = filters.sort[0].split('.');
- const measurementTemplates = await MeasurementTemplateModel.find({name: measurementName})
- .lean().exec().catch(err => {next(err);});
- if (measurementTemplates instanceof Error) return;
- if (!measurementTemplates) {
- return res.status(400).json({status: 'Invalid body format', details: filters.sort[0] + ' not found'});
- }
- let sortStartValue = null;
- if (filters['from-id']) { // from-id specified, fetch values for sorting
- const fromSample = await MeasurementModel.findOne({sample_id: mongoose.Types.ObjectId(filters['from-id'])})
- .lean().exec().catch(err => {next(err);});
- if (fromSample instanceof Error) return;
- if (!fromSample) {
- return res.status(400).json({status: 'Invalid body format', details: 'from-id not found'});
- }
- sortStartValue = fromSample.values[measurementParam];
- }
- // find measurements to sort
- queryPtr[0].$match.$and.push({measurement_template: {$in: measurementTemplates.map(e => e._id)}});
- if (filters.filters.find(e => e.field === filters.sort[0])) { // sorted measurement should also be filtered
- queryPtr[0].$match.$and.push(...filterQueries(filters.filters.filter(e => e.field === filters.sort[0])
- .map(e => {e.field = 'values.' + e.field.split('.')[2]; return e; })));
- }
- queryPtr.push(
- ...sortQuery(filters, ['values.' + measurementParam, 'sample_id'], sortStartValue), // sort measurements
- {$replaceRoot: {newRoot: {measurement: '$$ROOT'}}}, // fetch samples and restructure them to fit sample structure
- {$lookup: {from: 'samples', localField: 'measurement.sample_id', foreignField: '_id', as: 'sample'}},
- {$match: statusQuery(filters, 'sample.status')}, // filter out wrong status once samples were added
- {$addFields: {['sample.' + measurementName]: '$measurement.values'}}, // more restructuring
- {$replaceRoot: {newRoot: {$mergeObjects: [{$arrayElemAt: ['$sample', 0]}, {}]}}}
- );
- }
- else { // sorting with samples as starting collection
- collection = SampleModel;
- queryPtr[0].$match.$and.push(statusQuery(filters, 'status'));
+ if (filters.sort[0].indexOf('measurements.') >= 0) { // sorting with measurements as starting collection
+ collection = MeasurementModel;
+ const [,measurementName, measurementParam] = filters.sort[0].split('.');
+ const measurementTemplates = await MeasurementTemplateModel.find({name: measurementName})
+ .lean().exec().catch(err => {next(err);});
+ if (measurementTemplates instanceof Error) return;
+ if (!measurementTemplates) {
+ return res.status(400).json({status: 'Invalid body format', details: filters.sort[0] + ' not found'});
+ }
+ let sortStartValue = null;
+ if (filters['from-id']) { // from-id specified, fetch values for sorting
+ const fromSample = await MeasurementModel.findOne({sample_id: mongoose.Types.ObjectId(filters['from-id'])})
+ .lean().exec().catch(err => {next(err);});
+ if (fromSample instanceof Error) return;
+ if (!fromSample) {
+ return res.status(400).json({status: 'Invalid body format', details: 'from-id not found'});
+ }
+ sortStartValue = fromSample.values[measurementParam];
+ }
+ // find measurements to sort
+ queryPtr[0].$match.$and.push({measurement_template: {$in: measurementTemplates.map(e => e._id)}});
+ if (filters.filters.find(e => e.field === filters.sort[0])) { // sorted measurement should also be filtered
+ queryPtr[0].$match.$and.push(...filterQueries(filters.filters.filter(e => e.field === filters.sort[0])
+ .map(e => {e.field = 'values.' + e.field.split('.')[2]; return e; })));
+ }
+ queryPtr.push(
+ ...sortQuery(filters, ['values.' + measurementParam, 'sample_id'], sortStartValue), // sort measurements
+ {$replaceRoot: {newRoot: {measurement: '$$ROOT'}}}, // fetch samples and restructure them to fit sample structure
+ {$lookup: {from: 'samples', localField: 'measurement.sample_id', foreignField: '_id', as: 'sample'}},
+ {$match: statusQuery(filters, 'sample.status')}, // filter out wrong status once samples were added
+ {$addFields: {['sample.' + measurementName]: '$measurement.values'}}, // more restructuring
+ {$replaceRoot: {newRoot: {$mergeObjects: [{$arrayElemAt: ['$sample', 0]}, {}]}}}
+ );
+ }
+ else { // sorting with samples as starting collection
+ collection = SampleModel;
+ queryPtr[0].$match.$and.push(statusQuery(filters, 'status'));
- // sorting for sample keys
- if (SampleValidate.sampleKeys.indexOf(filters.sort[0]) >= 0 || /condition\./.test(filters.sort[0])) {
- let sortStartValue = null;
- if (filters['from-id']) { // from-id specified
- const fromSample = await SampleModel.findById(filters['from-id']).lean().exec().catch(err => {
- next(err);
- });
- if (fromSample instanceof Error) return;
- if (!fromSample) {
- return res.status(400).json({status: 'Invalid body format', details: 'from-id not found'});
- }
- sortStartValue = fromSample[filters.sort[0]];
- }
- queryPtr.push(...sortQuery(filters, [filters.sort[0], '_id'], sortStartValue));
- }
- else { // add sort key to list to add field later
- sortFilterKeys.push(filters.sort[0]);
- }
- }
+ // sorting for sample keys
+ if (SampleValidate.sampleKeys.indexOf(filters.sort[0]) >= 0 || /condition\./.test(filters.sort[0])) {
+ let sortStartValue = null;
+ if (filters['from-id']) { // from-id specified
+ const fromSample = await SampleModel.findById(filters['from-id']).lean().exec().catch(err => {
+ next(err);
+ });
+ if (fromSample instanceof Error) return;
+ if (!fromSample) {
+ return res.status(400).json({status: 'Invalid body format', details: 'from-id not found'});
+ }
+ sortStartValue = fromSample[filters.sort[0]];
+ }
+ queryPtr.push(...sortQuery(filters, [filters.sort[0], '_id'], sortStartValue));
+ }
+ else { // add sort key to list to add field later
+ sortFilterKeys.push(filters.sort[0]);
+ }
+ }
- addFilterQueries(queryPtr, filters.filters.filter(
- e => (SampleValidate.sampleKeys.indexOf(e.field) >= 0) || /condition\./.test(e.field))
- ); // sample filters
+ addFilterQueries(queryPtr, filters.filters.filter(
+ e => (SampleValidate.sampleKeys.indexOf(e.field) >= 0) || /condition\./.test(e.field))
+ ); // sample filters
- let materialQuery = []; // put material query together separate first to reuse for first-id
- let materialAdded = false;
- if (sortFilterKeys.find(e => /material\./.test(e))) { // add material fields
- materialAdded = true;
- materialQuery.push( // add material properties
- {$lookup: {from: 'materials', localField: 'material_id', foreignField: '_id', as: 'material'}},
- {$addFields: {material: {$arrayElemAt: ['$material', 0]}}}
- );
- const baseMFilters = sortFilterKeys.filter(e => /material\./.test(e))
- .filter(e => ['material.supplier', 'material.group', 'material.number'].indexOf(e) < 0);
- // base material filters
- addFilterQueries(materialQuery, filters.filters.filter(e => baseMFilters.indexOf(e.field) >= 0));
- if (sortFilterKeys.find(e => e === 'material.supplier')) { // add supplier if needed
- materialQuery.push(
- {$lookup: {
- from: 'material_suppliers', localField: 'material.supplier_id', foreignField: '_id', as: 'material.supplier'}
- },
- {$addFields: {'material.supplier': {$arrayElemAt: ['$material.supplier.name', 0]}}}
- );
- }
- if (sortFilterKeys.find(e => e === 'material.group')) { // add group if needed
- materialQuery.push(
- {$lookup: {
- from: 'material_groups', localField: 'material.group_id', foreignField: '_id', as: 'material.group' }
- },
- {$addFields: {'material.group': { $arrayElemAt: ['$material.group.name', 0]}}}
- );
- }
- const specialMFilters = sortFilterKeys.filter(e => /material\./.test(e))
- .filter(e => ['material.supplier', 'material.group', 'material.number'].indexOf(e) >= 0);
- // base material filters
- addFilterQueries(materialQuery, filters.filters.filter(e => specialMFilters.indexOf(e.field) >= 0));
- queryPtr.push(...materialQuery);
- if (/material\./.test(filters.sort[0])) { // sort by material key
- let sortStartValue = null;
- if (filters['from-id']) { // from-id specified
- const fromSample = await SampleModel.aggregate(
- [{$match: {_id: mongoose.Types.ObjectId(filters['from-id'])}}, ...materialQuery]
- ).exec().catch(err => {next(err);});
- if (fromSample instanceof Error) return;
- if (!fromSample) {
- return res.status(400).json({status: 'Invalid body format', details: 'from-id not found'});
- }
- const filterKey = filters.sort[0].split('.');
- if (filterKey.length === 2) {
- sortStartValue = fromSample[0][filterKey[0]][filterKey[1]];
- }
- else {
- sortStartValue = fromSample[0][filterKey[0]];
- }
- }
- queryPtr.push(...sortQuery(filters, [filters.sort[0], '_id'], sortStartValue));
- }
- }
+ let materialQuery = []; // put material query together separate first to reuse for first-id
+ let materialAdded = false;
+ if (sortFilterKeys.find(e => /material\./.test(e))) { // add material fields
+ materialAdded = true;
+ materialQuery.push( // add material properties
+ {$lookup: {from: 'materials', localField: 'material_id', foreignField: '_id', as: 'material'}},
+ {$addFields: {material: {$arrayElemAt: ['$material', 0]}}}
+ );
+ const baseMFilters = sortFilterKeys.filter(e => /material\./.test(e))
+ .filter(e => ['material.supplier', 'material.group', 'material.number'].indexOf(e) < 0);
+ // base material filters
+ addFilterQueries(materialQuery, filters.filters.filter(e => baseMFilters.indexOf(e.field) >= 0));
+ if (sortFilterKeys.find(e => e === 'material.supplier')) { // add supplier if needed
+ materialQuery.push(
+ {$lookup: {
+ from: 'material_suppliers', localField: 'material.supplier_id', foreignField: '_id', as: 'material.supplier'}
+ },
+ {$addFields: {'material.supplier': {$arrayElemAt: ['$material.supplier.name', 0]}}}
+ );
+ }
+ if (sortFilterKeys.find(e => e === 'material.group')) { // add group if needed
+ materialQuery.push(
+ {$lookup: {
+ from: 'material_groups', localField: 'material.group_id', foreignField: '_id', as: 'material.group' }
+ },
+ {$addFields: {'material.group': { $arrayElemAt: ['$material.group.name', 0]}}}
+ );
+ }
+ const specialMFilters = sortFilterKeys.filter(e => /material\./.test(e))
+ .filter(e => ['material.supplier', 'material.group', 'material.number'].indexOf(e) >= 0);
+ // base material filters
+ addFilterQueries(materialQuery, filters.filters.filter(e => specialMFilters.indexOf(e.field) >= 0));
+ queryPtr.push(...materialQuery);
+ if (/material\./.test(filters.sort[0])) { // sort by material key
+ let sortStartValue = null;
+ if (filters['from-id']) { // from-id specified
+ const fromSample = await SampleModel.aggregate(
+ [{$match: {_id: mongoose.Types.ObjectId(filters['from-id'])}}, ...materialQuery]
+ ).exec().catch(err => {next(err);});
+ if (fromSample instanceof Error) return;
+ if (!fromSample) {
+ return res.status(400).json({status: 'Invalid body format', details: 'from-id not found'});
+ }
+ const filterKey = filters.sort[0].split('.');
+ if (filterKey.length === 2) {
+ sortStartValue = fromSample[0][filterKey[0]][filterKey[1]];
+ }
+ else {
+ sortStartValue = fromSample[0][filterKey[0]];
+ }
+ }
+ queryPtr.push(...sortQuery(filters, [filters.sort[0], '_id'], sortStartValue));
+ }
+ }
- if (sortFilterKeys.find(e => e === 'measurements')) { // filter for samples without measurements
- queryPtr.push({$lookup: {
- from: 'measurements', let: {sId: '$_id'},
- pipeline: [{$match:{$expr:{$and:[{$eq:['$sample_id','$$sId']}]}}}, {$project: {_id: true}}],
- as: 'measurementCount'
- }},
- {$match: {measurementCount: {$size: 0}}}
- );
- }
- const measurementFilterFields = _.uniq(sortFilterKeys.filter(e => /measurements\./.test(e))
- .map(e => e.split('.')[1])); // filter measurement names and remove duplicates from parameters
- if (sortFilterKeys.find(e => /measurements\./.test(e))) { // add measurement fields
- const measurementTemplates = await MeasurementTemplateModel.find({name: {$in: measurementFilterFields}})
- .lean().exec().catch(err => {next(err);});
- if (measurementTemplates instanceof Error) return;
- if (measurementTemplates.length < measurementFilterFields.length) {
- return res.status(400).json({status: 'Invalid body format', details: 'Measurement key not found'});
- }
- const pipeline: any[] = [{$match: {$expr: {$and: [
- {$eq: ['$sample_id', '$$sId']},
- {$in: ['$measurement_template', measurementTemplates.map(e => mongoose.Types.ObjectId(e._id))]}
- ]}}}
- ];
- if (measurementFilterFields.indexOf(globals.spectrum.spectrum) >= 0) { // filter out dpts
- pipeline.push(
- {$project: {['values.' + globals.spectrum.dpt]: false}},
- {$addFields: {'values._id': '$_id'}}
- );
- }
- queryPtr.push({$lookup: {
- from: 'measurements', let: {sId: '$_id'},
- pipeline: pipeline,
- as: 'measurements'
- }});
- const groupedMeasurementTemplates = measurementTemplates.reduce((s, e) => {
- if (s.hasOwnProperty(e.name)) {
- s[e.name].push(e);
- }
- else {
- s[e.name] = [e];
- }
- return s;
- }, {});
- Object.values(groupedMeasurementTemplates).forEach(templates => {
- addMeasurements(queryPtr, templates);
- });
- addFilterQueries(queryPtr, filters.filters
- .filter(e => sortFilterKeys.filter(e => /measurements\./.test(e)).indexOf(e.field) >= 0)
- .map(e => {e.field = e.field.replace('measurements.', ''); return e; })
- ); // measurement filters
- }
+ if (sortFilterKeys.find(e => e === 'measurements')) { // filter for samples without measurements
+ queryPtr.push({$lookup: {
+ from: 'measurements', let: {sId: '$_id'},
+ pipeline: [{$match:{$expr:{$and:[{$eq:['$sample_id','$$sId']}]}}}, {$project: {_id: true}}],
+ as: 'measurementCount'
+ }},
+ {$match: {measurementCount: {$size: 0}}}
+ );
+ }
+ const measurementFilterFields = _.uniq(sortFilterKeys.filter(e => /measurements\./.test(e))
+ .map(e => e.split('.')[1])); // filter measurement names and remove duplicates from parameters
+ if (sortFilterKeys.find(e => /measurements\./.test(e))) { // add measurement fields
+ const measurementTemplates = await MeasurementTemplateModel.find({name: {$in: measurementFilterFields}})
+ .lean().exec().catch(err => {next(err);});
+ if (measurementTemplates instanceof Error) return;
+ if (measurementTemplates.length < measurementFilterFields.length) {
+ return res.status(400).json({status: 'Invalid body format', details: 'Measurement key not found'});
+ }
+ const pipeline: any[] = [{$match: {$expr: {$and: [
+ {$eq: ['$sample_id', '$$sId']},
+ {$in: ['$measurement_template', measurementTemplates.map(e => mongoose.Types.ObjectId(e._id))]}
+ ]}}}
+ ];
+ if (measurementFilterFields.indexOf(globals.spectrum.spectrum) >= 0) { // filter out dpts
+ pipeline.push(
+ {$project: {['values.' + globals.spectrum.dpt]: false}},
+ {$addFields: {'values._id': '$_id'}}
+ );
+ }
+ queryPtr.push({$lookup: {
+ from: 'measurements', let: {sId: '$_id'},
+ pipeline: pipeline,
+ as: 'measurements'
+ }});
+ const groupedMeasurementTemplates = measurementTemplates.reduce((s, e) => {
+ if (s.hasOwnProperty(e.name)) {
+ s[e.name].push(e);
+ }
+ else {
+ s[e.name] = [e];
+ }
+ return s;
+ }, {});
+ Object.values(groupedMeasurementTemplates).forEach(templates => {
+ addMeasurements(queryPtr, templates);
+ });
+ addFilterQueries(queryPtr, filters.filters
+ .filter(e => sortFilterKeys.filter(e => /measurements\./.test(e)).indexOf(e.field) >= 0)
+ .map(e => {e.field = e.field.replace('measurements.', ''); return e; })
+ ); // measurement filters
+ }
- if (sortFilterKeys.find(e => e === 'notes.comment')) {
- addNotes(queryPtr);
- addFilterQueries(queryPtr, filters.filters.filter(e => e.field === 'notes.comment'));
- }
+ if (sortFilterKeys.find(e => e === 'notes.comment')) {
+ addNotes(queryPtr);
+ addFilterQueries(queryPtr, filters.filters.filter(e => e.field === 'notes.comment'));
+ }
- // count total number of items before $skip and $limit, only works when from-id is not specified and spectra are not
- // included
- if (!filters.fields.find(e =>
- e.indexOf(globals.spectrum.spectrum + '.' + globals.spectrum.dpt) >= 0) && !filters['from-id']
- ) {
- queryPtr.push({$facet: {count: [{$count: 'count'}], samples: []}});
- queryPtr = queryPtr[queryPtr.length - 1].$facet.samples; // add rest of aggregation pipeline into $facet
- }
+ // count total number of items before $skip and $limit, only works when from-id is not specified and spectra are not
+ // included
+ if (!filters.fields.find(e =>
+ e.indexOf(globals.spectrum.spectrum + '.' + globals.spectrum.dpt) >= 0) && !filters['from-id']
+ ) {
+ queryPtr.push({$facet: {count: [{$count: 'count'}], samples: []}});
+ queryPtr = queryPtr[queryPtr.length - 1].$facet.samples; // add rest of aggregation pipeline into $facet
+ }
- // paging
- if (filters['to-page']) {
- // number to skip, if going back pages, one page has to be skipped less but on sample more
- queryPtr.push({$skip: Math.abs(filters['to-page'] + Number(filters['to-page'] < 0)) * filters['page-size'] +
- Number(filters['to-page'] < 0)})
- }
- if (filters['page-size']) {
- queryPtr.push({$limit: filters['page-size']});
- }
+ // paging
+ if (filters['to-page']) {
+ // number to skip, if going back pages, one page has to be skipped less but on sample more
+ queryPtr.push({$skip: Math.abs(filters['to-page'] + Number(filters['to-page'] < 0)) * filters['page-size'] +
+ Number(filters['to-page'] < 0)})
+ }
+ if (filters['page-size']) {
+ queryPtr.push({$limit: filters['page-size']});
+ }
- const fieldsToAdd = filters.fields.filter(e => // fields to add
- sortFilterKeys.indexOf(e) < 0 // field was not in filter
- && e !== filters.sort[0] // field was not in sort
- );
+ const fieldsToAdd = filters.fields.filter(e => // fields to add
+ sortFilterKeys.indexOf(e) < 0 // field was not in filter
+ && e !== filters.sort[0] // field was not in sort
+ );
- if (fieldsToAdd.find(e => /^notes(\..+|$)/m.test(e))) { // add notes
- addNotes(queryPtr);
- }
+ if (fieldsToAdd.find(e => /^notes(\..+|$)/m.test(e))) { // add notes
+ addNotes(queryPtr);
+ }
- if (fieldsToAdd.find(e => /material\./.test(e)) && !materialAdded) { // add material, was not added already
- queryPtr.push(
- {$lookup: {from: 'materials', localField: 'material_id', foreignField: '_id', as: 'material'}},
- {$addFields: {material: { $arrayElemAt: ['$material', 0]}}}
- );
- }
- if (fieldsToAdd.indexOf('material.supplier') >= 0) { // add supplier if needed
- queryPtr.push(
- {$lookup: {
- from: 'material_suppliers', localField: 'material.supplier_id', foreignField: '_id', as: 'material.supplier'
- }},
- {$addFields: {'material.supplier': {$arrayElemAt: ['$material.supplier.name', 0]}}}
- );
- }
- if (fieldsToAdd.indexOf('material.group') >= 0) { // add group if needed
- queryPtr.push(
- {$lookup: {
- from: 'material_groups', localField: 'material.group_id', foreignField: '_id', as: 'material.group'
- }},
- {$addFields: {'material.group': { $arrayElemAt: ['$material.group.name', 0]}}}
- );
- }
+ if (fieldsToAdd.find(e => /material\./.test(e)) && !materialAdded) { // add material, was not added already
+ queryPtr.push(
+ {$lookup: {from: 'materials', localField: 'material_id', foreignField: '_id', as: 'material'}},
+ {$addFields: {material: { $arrayElemAt: ['$material', 0]}}}
+ );
+ }
+ if (fieldsToAdd.indexOf('material.supplier') >= 0) { // add supplier if needed
+ queryPtr.push(
+ {$lookup: {
+ from: 'material_suppliers', localField: 'material.supplier_id', foreignField: '_id', as: 'material.supplier'
+ }},
+ {$addFields: {'material.supplier': {$arrayElemAt: ['$material.supplier.name', 0]}}}
+ );
+ }
+ if (fieldsToAdd.indexOf('material.group') >= 0) { // add group if needed
+ queryPtr.push(
+ {$lookup: {
+ from: 'material_groups', localField: 'material.group_id', foreignField: '_id', as: 'material.group'
+ }},
+ {$addFields: {'material.group': { $arrayElemAt: ['$material.group.name', 0]}}}
+ );
+ }
- let measurementFieldsFields: string[] = _.uniq(
- fieldsToAdd.filter(e => /measurements\./.test(e)).map(e => e.split('.')[1])
- ); // filter measurement names and remove duplicates from parameters
- if (fieldsToAdd.find(e => /measurements\./.test(e))) { // add measurement fields
- const measurementTemplates = await MeasurementTemplateModel.find({name: {$in: measurementFieldsFields}})
- .lean().exec().catch(err => {next(err);});
- if (measurementTemplates instanceof Error) return;
- if (measurementTemplates.length < measurementFieldsFields.length) {
- return res.status(400).json({status: 'Invalid body format', details: 'Measurement key not found'});
- }
- // use different lookup methods with and without dpt for the best performance
- if (fieldsToAdd.find(e => new RegExp('measurements\\.' + globals.spectrum.spectrum).test(e))) { // with dpt
- // spectrum was already used for filters
- if (sortFilterKeys.find(e => new RegExp('measurements\\.' + globals.spectrum.spectrum).test(e))) {
- queryPtr.push(
- {$lookup: {from: 'measurements', localField: 'spectrum._id', foreignField: '_id', as: 'measurements'}}
- );
- }
- else {
- queryPtr.push(
- {$lookup: {from: 'measurements', localField: '_id', foreignField: 'sample_id', as: 'measurements'}}
- );
- }
- }
- else {
- queryPtr.push({$lookup: {
- from: 'measurements', let: {sId: '$_id'},
- pipeline: [{$match: {$expr: {$and: [
- {$eq: ['$sample_id', '$$sId']},
- {$in: ['$measurement_template', measurementTemplates.map(e => mongoose.Types.ObjectId(e._id))]}
- ]}}},
- {$project: _.merge(
- filters.fields.filter(e => /measurements\./.test(e))
- .map(e => 'values.' + e.split('.')[2]).reduce((s, e) => {s[e] = true; return s; }, {}),
- {measurement_template: true, status: true, sample_id: true}
- )}
- ],
- as: 'measurements'
- }});
- }
- const groupedMeasurementTemplates = measurementTemplates.reduce((s, e) => {
- if (s.hasOwnProperty(e.name)) {
- s[e.name].push(e);
- }
- else {
- s[e.name] = [e];
- }
- return s;
- }, {});
- Object.values(groupedMeasurementTemplates).forEach(templates => {
- addMeasurements(queryPtr, templates);
- });
- queryPtr.push({$project: {measurements: 0}});
- }
+ let measurementFieldsFields: string[] = _.uniq(
+ fieldsToAdd.filter(e => /measurements\./.test(e)).map(e => e.split('.')[1])
+ ); // filter measurement names and remove duplicates from parameters
+ if (fieldsToAdd.find(e => /measurements\./.test(e))) { // add measurement fields
+ const measurementTemplates = await MeasurementTemplateModel.find({name: {$in: measurementFieldsFields}})
+ .lean().exec().catch(err => {next(err);});
+ if (measurementTemplates instanceof Error) return;
+ if (measurementTemplates.length < measurementFieldsFields.length) {
+ return res.status(400).json({status: 'Invalid body format', details: 'Measurement key not found'});
+ }
+ // use different lookup methods with and without dpt for the best performance
+ if (fieldsToAdd.find(e => new RegExp('measurements\\.' + globals.spectrum.spectrum).test(e))) { // with dpt
+ // spectrum was already used for filters
+ if (sortFilterKeys.find(e => new RegExp('measurements\\.' + globals.spectrum.spectrum).test(e))) {
+ queryPtr.push(
+ {$lookup: {from: 'measurements', localField: 'spectrum._id', foreignField: '_id', as: 'measurements'}}
+ );
+ }
+ else {
+ queryPtr.push(
+ {$lookup: {from: 'measurements', localField: '_id', foreignField: 'sample_id', as: 'measurements'}}
+ );
+ }
+ }
+ else {
+ queryPtr.push({$lookup: {
+ from: 'measurements', let: {sId: '$_id'},
+ pipeline: [{$match: {$expr: {$and: [
+ {$eq: ['$sample_id', '$$sId']},
+ {$in: ['$measurement_template', measurementTemplates.map(e => mongoose.Types.ObjectId(e._id))]}
+ ]}}},
+ {$project: _.merge(
+ filters.fields.filter(e => /measurements\./.test(e))
+ .map(e => 'values.' + e.split('.')[2]).reduce((s, e) => {s[e] = true; return s; }, {}),
+ {measurement_template: true, status: true, sample_id: true}
+ )}
+ ],
+ as: 'measurements'
+ }});
+ }
+ const groupedMeasurementTemplates = measurementTemplates.reduce((s, e) => {
+ if (s.hasOwnProperty(e.name)) {
+ s[e.name].push(e);
+ }
+ else {
+ s[e.name] = [e];
+ }
+ return s;
+ }, {});
+ Object.values(groupedMeasurementTemplates).forEach(templates => {
+ addMeasurements(queryPtr, templates);
+ });
+ queryPtr.push({$project: {measurements: 0}});
+ }
- const projection = filters.fields.map(e => e.replace('measurements.', ''))
- .reduce((s, e) => {s[e] = true; return s; }, {});
- if (filters.fields.indexOf('_id') < 0 && filters.fields.indexOf('added') < 0) { // disable _id explicitly
- projection._id = false;
- }
- queryPtr.push({$project: projection});
- // use streaming when including spectrum files
- if (!fieldsToAdd.find(e => e.indexOf(globals.spectrum.spectrum + '.' + globals.spectrum.dpt) >= 0)) {
- collection.aggregate(query).allowDiskUse(true).exec((err, data) => {
- if (err) return next(err);
- if (data[0] && data[0].count) {
- res.header('x-total-items', data[0].count.length > 0 ? data[0].count[0].count : 0);
- res.header('Access-Control-Expose-Headers', 'x-total-items');
- data = data[0].samples;
- }
- if (filters.fields.indexOf('added') >= 0) { // add added date
- data.map(e => {
- e.added = e._id.getTimestamp();
- if (filters.fields.indexOf('_id') < 0) {
- delete e._id;
- }
- return e
- });
- }
- if (filters['to-page'] < 0) {
- data.reverse();
- }
- const measurementFields = _.uniq(
- [filters.sort[0].split('.')[1],
- ...measurementFilterFields, ...measurementFieldsFields]
- );
- if (filters.output === 'csv') { // output as csv
- csv(_.compact(data.map(e => SampleValidate.output(e, 'refs', measurementFields))), (err, data) => {
- if (err) return next(err);
- res.set('Content-Type', 'text/csv');
- res.set('Content-Disposition', 'attachment; filename="samples.csv"');
- res.send(data);
- });
- }
- else if (filters.output === 'flatten') {
- res.json(_.compact(data.map(e => flatten(SampleValidate.output(e, 'refs', measurementFields), true))));
- }
- else { // validate all and filter null values from validation errors
- res.json(_.compact(data.map(e => SampleValidate.output(e, 'refs', measurementFields))));
- }
- });
- }
- else {
- res.writeHead(200, {'Content-Type': 'application/json; charset=utf-8'});
- res.write('[');
- let count = 0;
- const stream = collection.aggregate(query).allowDiskUse(true).cursor().exec();
- stream.on('data', data => {
- if (filters.fields.indexOf('added') >= 0) { // add added date
- data.added = data._id.getTimestamp();
- if (filters.fields.indexOf('_id') < 0) {
- delete data._id;
- }
- }
- if (filters.output === 'flatten') {
- data = flatten(data, true);
- }
- res.write((count === 0 ? '' : ',\n') + JSON.stringify(data)); count ++;
- });
- stream.on('error', err => {
- console.error(err);
- });
- stream.on('close', () => {
- res.write(']');
- res.end();
- });
- }
+ const projection = filters.fields.map(e => e.replace('measurements.', ''))
+ .reduce((s, e) => {s[e] = true; return s; }, {});
+ if (filters.fields.indexOf('_id') < 0 && filters.fields.indexOf('added') < 0) { // disable _id explicitly
+ projection._id = false;
+ }
+ queryPtr.push({$project: projection});
+ // use streaming when including spectrum files
+ if (!fieldsToAdd.find(e => e.indexOf(globals.spectrum.spectrum + '.' + globals.spectrum.dpt) >= 0)) {
+ collection.aggregate(query).allowDiskUse(true).exec((err, data) => {
+ if (err) return next(err);
+ if (data[0] && data[0].count) {
+ res.header('x-total-items', data[0].count.length > 0 ? data[0].count[0].count : 0);
+ res.header('Access-Control-Expose-Headers', 'x-total-items');
+ data = data[0].samples;
+ }
+ if (filters.fields.indexOf('added') >= 0) { // add added date
+ data.map(e => {
+ e.added = e._id.getTimestamp();
+ if (filters.fields.indexOf('_id') < 0) {
+ delete e._id;
+ }
+ return e
+ });
+ }
+ if (filters['to-page'] < 0) {
+ data.reverse();
+ }
+ const measurementFields = _.uniq(
+ [filters.sort[0].split('.')[1],
+ ...measurementFilterFields, ...measurementFieldsFields]
+ );
+ if (filters.output === 'csv') { // output as csv
+ csv(_.compact(data.map(e => SampleValidate.output(e, 'refs', measurementFields))), (err, data) => {
+ if (err) return next(err);
+ res.set('Content-Type', 'text/csv');
+ res.set('Content-Disposition', 'attachment; filename="samples.csv"');
+ res.send(data);
+ });
+ }
+ else if (filters.output === 'flatten') {
+ res.json(_.compact(data.map(e => flatten(SampleValidate.output(e, 'refs', measurementFields), true))));
+ }
+ else { // validate all and filter null values from validation errors
+ res.json(_.compact(data.map(e => SampleValidate.output(e, 'refs', measurementFields))));
+ }
+ });
+ }
+ else {
+ res.writeHead(200, {'Content-Type': 'application/json; charset=utf-8'});
+ res.write('[');
+ let count = 0;
+ const stream = collection.aggregate(query).allowDiskUse(true).cursor().exec();
+ stream.on('data', data => {
+ if (filters.fields.indexOf('added') >= 0) { // add added date
+ data.added = data._id.getTimestamp();
+ if (filters.fields.indexOf('_id') < 0) {
+ delete data._id;
+ }
+ }
+ if (filters.output === 'flatten') {
+ data = flatten(data, true);
+ }
+ res.write((count === 0 ? '' : ',\n') + JSON.stringify(data)); count ++;
+ });
+ stream.on('error', err => {
+ console.error(err);
+ });
+ stream.on('close', () => {
+ res.write(']');
+ res.end();
+ });
+ }
});
router.get(`/samples/: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;
- SampleModel.find({status: req.params.state}).lean().exec((err, data) => {
- if (err) return next(err);
- // validate all and filter null values from validation errors
- res.json(_.compact(data.map(e => SampleValidate.output(e))));
- });
+ SampleModel.find({status: req.params.state}).lean().exec((err, data) => {
+ if (err) return next(err);
+ // validate all and filter null values from validation errors
+ res.json(_.compact(data.map(e => SampleValidate.output(e))));
+ });
});
router.get('/samples/count', (req, res, next) => {
- if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
+ if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
- SampleModel.estimatedDocumentCount((err, data) => {
- if (err) return next(err);
- res.json({count: data});
- });
+ SampleModel.estimatedDocumentCount((err, data) => {
+ if (err) return next(err);
+ res.json({count: data});
+ });
});
router.get('/sample/' + 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;
- SampleModel.findById(req.params.id).populate('material_id').populate('user_id', 'name').populate('note_id')
- .exec(async (err, sampleData: any) => {
- if (err) return next(err);
- await sampleReturn(sampleData, req, res, next);
- });
+ SampleModel.findById(req.params.id).populate('material_id').populate('user_id', 'name').populate('note_id')
+ .exec(async (err, sampleData: any) => {
+ if (err) return next(err);
+ await sampleReturn(sampleData, req, res, next);
+ });
});
router.put('/sample/' + IdValidate.parameter(), (req, res, next) => {
- if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
+ if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
- const {error, value: sample} = SampleValidate.input(req.body, 'change');
- if (error) return res400(error, res);
+ const {error, value: sample} = SampleValidate.input(req.body, 'change');
+ if (error) return res400(error, res);
- SampleModel.findById(req.params.id).lean().exec(async (err, sampleData: any) => { // check if id exists
- if (err) return next(err);
- if (!sampleData) {
- return res.status(404).json({status: 'Not found'});
- }
- if (sampleData.status === globals.status.del) {
- return res.status(403).json({status: 'Forbidden'});
- }
+ SampleModel.findById(req.params.id).lean().exec(async (err, sampleData: any) => { // check if id exists
+ if (err) return next(err);
+ if (!sampleData) {
+ return res.status(404).json({status: 'Not found'});
+ }
+ if (sampleData.status === globals.status.del) {
+ return res.status(403).json({status: 'Forbidden'});
+ }
- // only dev and admin are allowed to edit other user's data
- if (sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['dev', 'admin'], 'basic')) return;
- if (sample.hasOwnProperty('material_id')) {
- if (!await materialCheck(sample, res, next)) return;
- }
- // do not execute check if condition is and was empty
- if (sample.hasOwnProperty('condition') && !(_.isEmpty(sample.condition) && _.isEmpty(sampleData.condition))) {
- sample.condition = await conditionCheck(sample.condition, 'change', res, next,
- !(sampleData.condition.condition_template &&
- sampleData.condition.condition_template.toString() === sample.condition.condition_template));
- if (!sample.condition) return;
- }
+ // only dev and admin are allowed to edit other user's data
+ if (sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['dev', 'admin'], 'basic')) return;
+ if (sample.hasOwnProperty('material_id')) {
+ if (!await materialCheck(sample, res, next)) return;
+ }
+ // do not execute check if condition is and was empty
+ if (sample.hasOwnProperty('condition') && !(_.isEmpty(sample.condition) && _.isEmpty(sampleData.condition))) {
+ sample.condition = await conditionCheck(sample.condition, 'change', res, next,
+ !(sampleData.condition.condition_template &&
+ sampleData.condition.condition_template.toString() === sample.condition.condition_template));
+ if (!sample.condition) return;
+ }
- if (sample.hasOwnProperty('notes')) {
- let newNotes = true;
- if (sampleData.note_id !== null) { // old notes data exists
- const data = await NoteModel.findById(sampleData.note_id).lean().exec().catch(err => {next(err);}) as any;
- if (data instanceof Error) return;
- // check if notes were changed
- newNotes = !_.isEqual(_.pick(IdValidate.stringify(data), _.keys(sample.notes)), sample.notes);
- if (newNotes) {
- if (data.hasOwnProperty('custom_fields')) { // update note_fields
- customFieldsChange(Object.keys(data.custom_fields), -1, req);
- }
- await NoteModel.findByIdAndDelete(sampleData.note_id).log(req).lean().exec(err => { // delete old notes
- if (err) return console.error(err);
- });
- }
- }
+ if (sample.hasOwnProperty('notes')) {
+ let newNotes = true;
+ if (sampleData.note_id !== null) { // old notes data exists
+ const data = await NoteModel.findById(sampleData.note_id).lean().exec().catch(err => {next(err);}) as any;
+ if (data instanceof Error) return;
+ // check if notes were changed
+ newNotes = !_.isEqual(_.pick(IdValidate.stringify(data), _.keys(sample.notes)), sample.notes);
+ if (newNotes) {
+ if (data.hasOwnProperty('custom_fields')) { // update note_fields
+ customFieldsChange(Object.keys(data.custom_fields), -1, req);
+ }
+ await NoteModel.findByIdAndDelete(sampleData.note_id).log(req).lean().exec(err => { // delete old notes
+ if (err) return console.error(err);
+ });
+ }
+ }
- if (_.keys(sample.notes).length > 0 && newNotes) { // save new notes
- if (!await sampleRefCheck(sample, res, next)) return;
- // new custom_fields
- if (sample.notes.hasOwnProperty('custom_fields') && Object.keys(sample.notes.custom_fields).length > 0) {
- customFieldsChange(Object.keys(sample.notes.custom_fields), 1, req);
- }
- let data = await new NoteModel(sample.notes).save().catch(err => { return next(err)}); // save new notes
- db.log(req, 'notes', {_id: data._id}, data.toObject());
- delete sample.notes;
- sample.note_id = data._id;
- }
- }
+ if (_.keys(sample.notes).length > 0 && newNotes) { // save new notes
+ if (!await sampleRefCheck(sample, res, next)) return;
+ // new custom_fields
+ if (sample.notes.hasOwnProperty('custom_fields') && Object.keys(sample.notes.custom_fields).length > 0) {
+ customFieldsChange(Object.keys(sample.notes.custom_fields), 1, req);
+ }
+ let data = await new NoteModel(sample.notes).save().catch(err => { return next(err)}); // save new notes
+ db.log(req, 'notes', {_id: data._id}, data.toObject());
+ delete sample.notes;
+ sample.note_id = data._id;
+ }
+ }
- // check for changes
- if (!_.isEqual(_.pick(IdValidate.stringify(sampleData), _.keys(sample)), _.omit(sample, ['notes']))) {
- sample.status = globals.status.new;
- }
+ // check for changes
+ if (!_.isEqual(_.pick(IdValidate.stringify(sampleData), _.keys(sample)), _.omit(sample, ['notes']))) {
+ sample.status = globals.status.new;
+ }
- await SampleModel.findByIdAndUpdate(req.params.id, sample, {new: true}).log(req).lean().exec((err, data: any) => {
- if (err) return next(err);
- res.json(SampleValidate.output(data));
- });
+ await SampleModel.findByIdAndUpdate(req.params.id, sample, {new: true}).log(req).lean().exec((err, data: any) => {
+ if (err) return next(err);
+ res.json(SampleValidate.output(data));
+ });
- });
+ });
});
router.delete('/sample/' + IdValidate.parameter(), (req, res, next) => {
- if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
+ if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
- SampleModel.findById(req.params.id).lean().exec(async (err, sampleData: any) => { // check if id exists
- if (err) return next(err);
- if (!sampleData) {
- return res.status(404).json({status: 'Not found'});
- }
+ SampleModel.findById(req.params.id).lean().exec(async (err, sampleData: any) => { // check if id exists
+ if (err) return next(err);
+ if (!sampleData) {
+ return res.status(404).json({status: 'Not found'});
+ }
- // only dev and admin are allowed to edit other user's data
- if (sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['dev', 'admin'], 'basic')) return;
+ // only dev and admin are allowed to edit other user's data
+ if (sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['dev', 'admin'], 'basic')) return;
- // set sample status
- await SampleModel.findByIdAndUpdate(req.params.id, {status:'deleted'}).log(req).lean().exec(err => {
- if (err) return next(err);
+ // set sample status
+ await SampleModel.findByIdAndUpdate(req.params.id, {status:'deleted'}).log(req).lean().exec(err => {
+ if (err) return next(err);
- // set status of associated measurements also to deleted
- MeasurementModel.updateMany({sample_id: mongoose.Types.ObjectId(req.params.id)}, {status: globals.status.del})
- .log(req).lean().exec(err => {
- if (err) return next(err);
+ // set status of associated measurements also to deleted
+ MeasurementModel.updateMany({sample_id: mongoose.Types.ObjectId(req.params.id)}, {status: globals.status.del})
+ .log(req).lean().exec(err => {
+ if (err) return next(err);
- if (sampleData.note_id !== null) { // handle notes
- NoteModel.findById(sampleData.note_id).lean().exec((err, data: any) => { // find notes to update note_fields
- if (err) return next(err);
- if (data.hasOwnProperty('custom_fields')) { // update note_fields
- customFieldsChange(Object.keys(data.custom_fields), -1, req);
- }
- res.json({status: 'OK'});
- });
- }
- else {
- res.json({status: 'OK'});
- }
- });
- });
- });
+ if (sampleData.note_id !== null) { // handle notes
+ NoteModel.findById(sampleData.note_id).lean().exec((err, data: any) => { // find notes to update note_fields
+ if (err) return next(err);
+ if (data.hasOwnProperty('custom_fields')) { // update note_fields
+ customFieldsChange(Object.keys(data.custom_fields), -1, req);
+ }
+ res.json({status: 'OK'});
+ });
+ }
+ else {
+ res.json({status: 'OK'});
+ }
+ });
+ });
+ });
});
router.get('/sample/number/:number', (req, res, next) => {
- if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
+ if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
- SampleModel.findOne({number: req.params.number}).populate('material_id').populate('user_id', 'name')
- .populate('note_id').exec(async (err, sampleData: any) => {
- if (err) return next(err);
- await sampleReturn(sampleData, req, res, next);
- });
+ SampleModel.findOne({number: req.params.number}).populate('material_id').populate('user_id', 'name')
+ .populate('note_id').exec(async (err, sampleData: any) => {
+ if (err) return next(err);
+ await sampleReturn(sampleData, req, res, next);
+ });
});
router.put('/sample/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('/sample/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('/sample/new', async (req, res, next) => {
- if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
+ if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return;
- if (!req.body.hasOwnProperty('condition')) { // add empty condition if not specified
- req.body.condition = {};
- }
+ if (!req.body.hasOwnProperty('condition')) { // add empty condition if not specified
+ req.body.condition = {};
+ }
- const {error, value: sample} =
- SampleValidate.input(req.body, 'new' + (req.authDetails.level === 'admin' ? '-admin' : ''));
- if (error) return res400(error, res);
+ const {error, value: sample} =
+ SampleValidate.input(req.body, 'new' + (req.authDetails.level === 'admin' ? '-admin' : ''));
+ if (error) return res400(error, res);
- if (!await materialCheck(sample, res, next)) return;
- if (!await sampleRefCheck(sample, res, next)) return;
+ if (!await materialCheck(sample, res, next)) return;
+ if (!await sampleRefCheck(sample, res, next)) return;
- // new custom_fields
- if (sample.notes.hasOwnProperty('custom_fields') && Object.keys(sample.notes.custom_fields).length > 0) {
- customFieldsChange(Object.keys(sample.notes.custom_fields), 1, req);
- }
+ // new custom_fields
+ if (sample.notes.hasOwnProperty('custom_fields') && Object.keys(sample.notes.custom_fields).length > 0) {
+ customFieldsChange(Object.keys(sample.notes.custom_fields), 1, req);
+ }
- if (!_.isEmpty(sample.condition)) { // do not execute check if condition is empty
- sample.condition = await conditionCheck(sample.condition, 'change', res, next);
- if (!sample.condition) return;
- }
+ if (!_.isEmpty(sample.condition)) { // do not execute check if condition is empty
+ sample.condition = await conditionCheck(sample.condition, 'change', res, next);
+ if (!sample.condition) return;
+ }
- sample.status = globals.status.new; // set status to new
- if (sample.hasOwnProperty('number')) {
- if (!await numberCheck(sample, res, next)) return;
- }
- else {
- sample.number = await numberGenerate(sample, req, res, next);
- }
- if (!sample.number) return;
+ sample.status = globals.status.new; // set status to new
+ if (sample.hasOwnProperty('number')) {
+ if (!await numberCheck(sample, res, next)) return;
+ }
+ else {
+ sample.number = await numberGenerate(sample, req, res, next);
+ }
+ if (!sample.number) return;
- await new NoteModel(sample.notes).save((err, data) => { // save notes
- if (err) return next(err);
- db.log(req, 'notes', {_id: data._id}, data.toObject());
- delete sample.notes;
- sample.note_id = data._id;
- sample.user_id = req.authDetails.id;
+ await new NoteModel(sample.notes).save((err, data) => { // save notes
+ if (err) return next(err);
+ db.log(req, 'notes', {_id: data._id}, data.toObject());
+ delete sample.notes;
+ sample.note_id = data._id;
+ sample.user_id = req.authDetails.id;
- new SampleModel(sample).save((err, data) => {
- if (err) return next(err);
- db.log(req, 'samples', {_id: data._id}, data.toObject());
- res.json(SampleValidate.output(data.toObject()));
- });
- });
+ new SampleModel(sample).save((err, data) => {
+ if (err) return next(err);
+ db.log(req, 'samples', {_id: data._id}, data.toObject());
+ res.json(SampleValidate.output(data.toObject()));
+ });
+ });
});
router.get('/sample/notes/fields', (req, res, next) => {
- if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
+ if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
- NoteFieldModel.find({}).lean().exec((err, data) => {
- if (err) return next(err);
- // validate all and filter null values from validation errors
- res.json(_.compact(data.map(e => NoteFieldValidate.output(e))));
- })
+ NoteFieldModel.find({}).lean().exec((err, data) => {
+ if (err) return next(err);
+ // validate all and filter null values from validation errors
+ res.json(_.compact(data.map(e => NoteFieldValidate.output(e))));
+ })
});
@@ -678,267 +678,267 @@ const numberBuffer: {[location: string]: number} = {};
// generate number in format Location32, returns false on error
async function numberGenerate (sample, req, res, next) {
- const sampleData = await SampleModel
- .aggregate([
- {$match: {number: new RegExp('^' + req.authDetails.location + '[0-9]+$', 'm')}},
- {$addFields: {sortNumber: {$let: {
- vars: {tmp: {$concat: ['000000000000000000000000000000',
- {$arrayElemAt: [{$split:
- [{$arrayElemAt: [{$split: ['$number', req.authDetails.location]}, 1]}, '_']}, 0]}]}},
- in: {$substrCP: ['$$tmp', {$subtract: [{$strLenCP: '$$tmp'}, 30]}, {$strLenCP: '$$tmp'}]}
- }}}},
- {$sort: {sortNumber: -1}},
- {$limit: 1}
- ])
- .exec()
- .catch(err => next(err));
- if (sampleData instanceof Error) return false;
- let number = (sampleData[0] ? Number(sampleData[0].number.replace(/[^0-9]+/g, '')) : 0);
- if (numberBuffer[req.authDetails.location] && numberBuffer[req.authDetails.location] >= number) {
- number = numberBuffer[req.authDetails.location];
- }
- number ++;
- numberBuffer[req.authDetails.location] = number;
- return req.authDetails.location + number;
+ const sampleData = await SampleModel
+ .aggregate([
+ {$match: {number: new RegExp('^' + req.authDetails.location + '[0-9]+$', 'm')}},
+ {$addFields: {sortNumber: {$let: {
+ vars: {tmp: {$concat: ['000000000000000000000000000000',
+ {$arrayElemAt: [{$split:
+ [{$arrayElemAt: [{$split: ['$number', req.authDetails.location]}, 1]}, '_']}, 0]}]}},
+ in: {$substrCP: ['$$tmp', {$subtract: [{$strLenCP: '$$tmp'}, 30]}, {$strLenCP: '$$tmp'}]}
+ }}}},
+ {$sort: {sortNumber: -1}},
+ {$limit: 1}
+ ])
+ .exec()
+ .catch(err => next(err));
+ if (sampleData instanceof Error) return false;
+ let number = (sampleData[0] ? Number(sampleData[0].number.replace(/[^0-9]+/g, '')) : 0);
+ if (numberBuffer[req.authDetails.location] && numberBuffer[req.authDetails.location] >= number) {
+ number = numberBuffer[req.authDetails.location];
+ }
+ number ++;
+ numberBuffer[req.authDetails.location] = number;
+ return req.authDetails.location + number;
}
async function numberCheck(sample, res, next) {
- const sampleData = await SampleModel.findOne({number: sample.number})
- .lean().exec().catch(err => {next(err); return false;});
- if (sampleData) { // found entry with sample number
- res.status(400).json({status: 'Sample number already taken'});
- return false
- }
- return true;
+ const sampleData = await SampleModel.findOne({number: sample.number})
+ .lean().exec().catch(err => {next(err); return false;});
+ if (sampleData) { // found entry with sample number
+ res.status(400).json({status: 'Sample number already taken'});
+ return false
+ }
+ return true;
}
// validate material_id and color, returns false if invalid
async function materialCheck (sample, res, next) {
- const materialData = await MaterialModel.findById(sample.material_id).lean().exec().catch(err => next(err)) as any;
- if (materialData instanceof Error) return false;
- if (!materialData) { // could not find material_id
- res.status(400).json({status: 'Material not available'});
- return false;
- }
- return true;
+ const materialData = await MaterialModel.findById(sample.material_id).lean().exec().catch(err => next(err)) as any;
+ if (materialData instanceof Error) return false;
+ if (!materialData) { // could not find material_id
+ res.status(400).json({status: 'Material not available'});
+ return false;
+ }
+ return true;
}
// validate treatment template, returns false if invalid, otherwise template data
async function conditionCheck (condition, param, res, next, checkVersion = true) {
- if (!condition.condition_template || !IdValidate.valid(condition.condition_template)) { // template id not found
- res.status(400).json({status: 'Condition template not available'});
- return false;
- }
- const conditionData = await ConditionTemplateModel.findById(condition.condition_template)
- .lean().exec().catch(err => next(err)) as any;
- if (conditionData instanceof Error) return false;
- if (!conditionData) { // template not found
- res.status(400).json({status: 'Condition template not available'});
- return false;
- }
+ if (!condition.condition_template || !IdValidate.valid(condition.condition_template)) { // template id not found
+ res.status(400).json({status: 'Condition template not available'});
+ return false;
+ }
+ const conditionData = await ConditionTemplateModel.findById(condition.condition_template)
+ .lean().exec().catch(err => next(err)) as any;
+ if (conditionData instanceof Error) return false;
+ if (!conditionData) { // template not found
+ res.status(400).json({status: 'Condition template not available'});
+ return false;
+ }
- if (checkVersion) {
- // get all template versions and check if given is latest
- const conditionVersions = await ConditionTemplateModel.find({first_id: conditionData.first_id})
- .sort({version: -1}).lean().exec().catch(err => next(err)) as any;
- if (conditionVersions instanceof Error) return false;
- if (condition.condition_template !== conditionVersions[0]._id.toString()) { // template not latest
- res.status(400).json({status: 'Old template version not allowed'});
- return false;
- }
- }
+ if (checkVersion) {
+ // get all template versions and check if given is latest
+ const conditionVersions = await ConditionTemplateModel.find({first_id: conditionData.first_id})
+ .sort({version: -1}).lean().exec().catch(err => next(err)) as any;
+ if (conditionVersions instanceof Error) return false;
+ if (condition.condition_template !== conditionVersions[0]._id.toString()) { // template not latest
+ res.status(400).json({status: 'Old template version not allowed'});
+ return false;
+ }
+ }
- // validate parameters
- const {error, value} =
- ParametersValidate.input(_.omit(condition, 'condition_template'), conditionData.parameters, param);
- if (error) {res400(error, res); return false;}
- value.condition_template = condition.condition_template;
- return value;
+ // validate parameters
+ const {error, value} =
+ ParametersValidate.input(_.omit(condition, 'condition_template'), conditionData.parameters, param);
+ if (error) {res400(error, res); return false;}
+ value.condition_template = condition.condition_template;
+ return value;
}
function sampleRefCheck (sample, res, next) { // validate sample_references, resolves false for invalid reference
- return new Promise(resolve => {
- // there are sample_references
- if (sample.notes.hasOwnProperty('sample_references') && sample.notes.sample_references.length > 0) {
- let referencesCount = sample.notes.sample_references.length; // count to keep track of running async operations
+ return new Promise(resolve => {
+ // there are sample_references
+ if (sample.notes.hasOwnProperty('sample_references') && sample.notes.sample_references.length > 0) {
+ let referencesCount = sample.notes.sample_references.length; // count to keep track of running async operations
- sample.notes.sample_references.forEach(reference => {
- SampleModel.findById(reference.sample_id).lean().exec((err, data) => {
- if (err) {next(err); resolve(false)}
- if (!data) {
- res.status(400).json({status: 'Sample reference not available'});
- return resolve(false);
- }
- referencesCount --;
- if (referencesCount <= 0) { // all async requests done
- resolve(true);
- }
- });
- });
- }
- else {
- resolve(true);
- }
- });
+ sample.notes.sample_references.forEach(reference => {
+ SampleModel.findById(reference.sample_id).lean().exec((err, data) => {
+ if (err) {next(err); resolve(false)}
+ if (!data) {
+ res.status(400).json({status: 'Sample reference not available'});
+ return resolve(false);
+ }
+ referencesCount --;
+ if (referencesCount <= 0) { // all async requests done
+ resolve(true);
+ }
+ });
+ });
+ }
+ else {
+ resolve(true);
+ }
+ });
}
function customFieldsChange (fields, amount, req) { // update custom_fields and respective quantities
- fields.forEach(field => {
- NoteFieldModel.findOneAndUpdate({name: field}, {$inc: {qty: amount}} as any, {new: true})
- .log(req).lean().exec((err, data: any) => { // check if field exists
- if (err) return console.error(err);
- if (!data) { // new field
- new NoteFieldModel({name: field, qty: 1}).save((err, data) => {
- if (err) return console.error(err);
- db.log(req, 'note_fields', {_id: data._id}, data.toObject());
- })
- }
- else if (data.qty <= 0) { // delete document if field is not used anymore
- NoteFieldModel.findOneAndDelete({name: field}).log(req).lean().exec(err => {
- if (err) return console.error(err);
- });
- }
- });
- });
+ fields.forEach(field => {
+ NoteFieldModel.findOneAndUpdate({name: field}, {$inc: {qty: amount}} as any, {new: true})
+ .log(req).lean().exec((err, data: any) => { // check if field exists
+ if (err) return console.error(err);
+ if (!data) { // new field
+ new NoteFieldModel({name: field, qty: 1}).save((err, data) => {
+ if (err) return console.error(err);
+ db.log(req, 'note_fields', {_id: data._id}, data.toObject());
+ })
+ }
+ else if (data.qty <= 0) { // delete document if field is not used anymore
+ NoteFieldModel.findOneAndDelete({name: field}).log(req).lean().exec(err => {
+ if (err) return console.error(err);
+ });
+ }
+ });
+ });
}
function sortQuery(filters, sortKeys, sortStartValue) { // sortKeys = ['primary key', 'secondary key']
- if (filters['from-id']) { // from-id specified
- const ssv = sortStartValue !== undefined; // if value is not given, match for existence
- if ((filters['to-page'] === 0 && filters.sort[1] === 1) || (filters.sort[1] * filters['to-page'] > 0)) { // asc
- return [
- {$match: {$or: [
- {[sortKeys[0]]: ssv ? {$gt: sortStartValue} : {$exists: true}},
- {$and: [
- {[sortKeys[0]]: ssv ? sortStartValue : {$exists: false}},
- {[sortKeys[1]]: {$gte: new mongoose.Types.ObjectId(filters['from-id'])}}
- ]}
- ]}},
- {$sort: {[sortKeys[0]]: 1, _id: 1}}
- ];
- } else {
- return [
- {$match: {$or: [
- {[sortKeys[0]]: ssv ? {$lt: sortStartValue} : {$exists: false}},
- {$and: [
- {[sortKeys[0]]: ssv ? sortStartValue : {$exists: true}},
- {[sortKeys[1]]: {$lte: new mongoose.Types.ObjectId(filters['from-id'])}}
- ]}
- ]}},
- {$sort: {[sortKeys[0]]: -1, _id: -1}}
- ];
- }
- } else { // sort from beginning
- return [{$sort: {[sortKeys[0]]: filters.sort[1], [sortKeys[1]]: filters.sort[1]}}]; // set _id as secondary sort
- }
+ if (filters['from-id']) { // from-id specified
+ const ssv = sortStartValue !== undefined; // if value is not given, match for existence
+ if ((filters['to-page'] === 0 && filters.sort[1] === 1) || (filters.sort[1] * filters['to-page'] > 0)) { // asc
+ return [
+ {$match: {$or: [
+ {[sortKeys[0]]: ssv ? {$gt: sortStartValue} : {$exists: true}},
+ {$and: [
+ {[sortKeys[0]]: ssv ? sortStartValue : {$exists: false}},
+ {[sortKeys[1]]: {$gte: new mongoose.Types.ObjectId(filters['from-id'])}}
+ ]}
+ ]}},
+ {$sort: {[sortKeys[0]]: 1, _id: 1}}
+ ];
+ } else {
+ return [
+ {$match: {$or: [
+ {[sortKeys[0]]: ssv ? {$lt: sortStartValue} : {$exists: false}},
+ {$and: [
+ {[sortKeys[0]]: ssv ? sortStartValue : {$exists: true}},
+ {[sortKeys[1]]: {$lte: new mongoose.Types.ObjectId(filters['from-id'])}}
+ ]}
+ ]}},
+ {$sort: {[sortKeys[0]]: -1, _id: -1}}
+ ];
+ }
+ } else { // sort from beginning
+ return [{$sort: {[sortKeys[0]]: filters.sort[1], [sortKeys[1]]: filters.sort[1]}}]; // set _id as secondary sort
+ }
}
function statusQuery(filters, field) {
- return {$or: filters.status.map(e => ({[field]: e}))};
+ return {$or: filters.status.map(e => ({[field]: e}))};
}
function addFilterQueries (queryPtr, filters) { // returns array of match queries from given filters
- if (filters.length) {
- queryPtr.push({$match: {$and: filterQueries(filters)}});
- }
+ if (filters.length) {
+ queryPtr.push({$match: {$and: filterQueries(filters)}});
+ }
}
function filterQueries (filters) {
- return filters.map(e => {
- if (e.mode === 'or') { // allow or queries (needed for $ne added)
- return {['$' + e.mode]: e.values};
- }
- else if (e.mode === 'stringin') {
- return {[e.field]: {['$in']: [new RegExp(e.values[0])]}};
- }
- else {
- // add filter criteria as {field: {$mode: value}}, only use first value when mode is not in/nin
- return {[e.field]: {['$' + e.mode]: (e.mode.indexOf('in') >= 0 ? e.values : e.values[0])}};
- }
- });
+ return filters.map(e => {
+ if (e.mode === 'or') { // allow or queries (needed for $ne added)
+ return {['$' + e.mode]: e.values};
+ }
+ else if (e.mode === 'stringin') {
+ return {[e.field]: {['$in']: [new RegExp(e.values[0])]}};
+ }
+ else {
+ // add filter criteria as {field: {$mode: value}}, only use first value when mode is not in/nin
+ return {[e.field]: {['$' + e.mode]: (e.mode.indexOf('in') >= 0 ? e.values : e.values[0])}};
+ }
+ });
}
// add measurements as property [template.name], if one result, array is reduced to direct values. All given templates
// must have the same name
function addMeasurements(queryPtr, templates) {
- queryPtr.push(
- {$addFields: {[templates[0].name]: {$let: {vars: {
- arr: {$filter: {
- input: '$measurements', cond: {$and: [
- {$in: ['$$this.measurement_template', templates.map(e => mongoose.Types.ObjectId(e._id))]},
- {$ne: ['$$this.status', globals.status.del]}
- ]},
- }}},
- in: {$cond: [{$lte: [{$size: '$$arr'}, 1]}, {$arrayElemAt: ['$$arr', 0]}, '$$arr']}
- }}}},
- {$addFields: {[templates[0].name]: {$cond: [
- '$' + templates[0].name + '.values',
- '$' + templates[0].name + '.values',
- templates[0].parameters.reduce((s, e) => {s[e.name] = null; return s;}, {})
- ]}}},
- {$unwind: '$' + templates[0].name}
- );
+ queryPtr.push(
+ {$addFields: {[templates[0].name]: {$let: {vars: {
+ arr: {$filter: {
+ input: '$measurements', cond: {$and: [
+ {$in: ['$$this.measurement_template', templates.map(e => mongoose.Types.ObjectId(e._id))]},
+ {$ne: ['$$this.status', globals.status.del]}
+ ]},
+ }}},
+ in: {$cond: [{$lte: [{$size: '$$arr'}, 1]}, {$arrayElemAt: ['$$arr', 0]}, '$$arr']}
+ }}}},
+ {$addFields: {[templates[0].name]: {$cond: [
+ '$' + templates[0].name + '.values',
+ '$' + templates[0].name + '.values',
+ templates[0].parameters.reduce((s, e) => {s[e.name] = null; return s;}, {})
+ ]}}},
+ {$unwind: '$' + templates[0].name}
+ );
}
function addNotes(queryPtr) { // add note fields with default, if no notes are found
- queryPtr.push(
- {$lookup: {from: 'notes', localField: 'note_id', foreignField: '_id', as: 'notes'}},
- {$addFields: {notes: {$cond: [
- {'$arrayElemAt': ['$notes', 0]},
- {'$arrayElemAt': ['$notes', 0]},
- {comment: null, sample_references: []}
- ]}}}
- );
+ queryPtr.push(
+ {$lookup: {from: 'notes', localField: 'note_id', foreignField: '_id', as: 'notes'}},
+ {$addFields: {notes: {$cond: [
+ {'$arrayElemAt': ['$notes', 0]},
+ {'$arrayElemAt': ['$notes', 0]},
+ {comment: null, sample_references: []}
+ ]}}}
+ );
}
function dateToOId (date) { // convert date to ObjectId
- return mongoose.Types.ObjectId(Math.floor(date / 1000).toString(16) + '0000000000000000');
+ return mongoose.Types.ObjectId(Math.floor(date / 1000).toString(16) + '0000000000000000');
}
async function sampleReturn (sampleData, req, res, next) {
- if (sampleData) {
- await sampleData.populate('material_id.group_id').populate('material_id.supplier_id')
- .execPopulate().catch(err => next(err));
- if (sampleData instanceof Error) return;
- sampleData = sampleData.toObject();
+ if (sampleData) {
+ await sampleData.populate('material_id.group_id').populate('material_id.supplier_id')
+ .execPopulate().catch(err => next(err));
+ if (sampleData instanceof Error) return;
+ sampleData = sampleData.toObject();
- // deleted samples only available for dev/admin
- if (sampleData.status === globals.status.del && !req.auth(res, ['dev', 'admin'], 'all')) return;
- sampleData.material = sampleData.material_id; // map data to right keys
- sampleData.material.group = sampleData.material.group_id.name;
- sampleData.material.supplier = sampleData.material.supplier_id.name;
- sampleData.user = sampleData.user_id.name;
- sampleData.notes = sampleData.note_id ? sampleData.note_id : {};
- MeasurementModel.find({sample_id: sampleData._id, status: {$ne: 'deleted'}})
- .lean().exec((err, data) => {
- sampleData.measurements = data;
- if (['dev', 'admin'].indexOf(req.authDetails.level) < 0) { // strip dpt values if not dev or admin
- sampleData.measurements.forEach(measurement => {
- if (measurement.values[globals.spectrum.dpt]) {
- delete measurement.values[globals.spectrum.dpt];
- }
- });
- }
- res.json(SampleValidate.output(sampleData, 'details'));
- });
- }
- else {
- res.status(404).json({status: 'Not found'});
- }
+ // deleted samples only available for dev/admin
+ if (sampleData.status === globals.status.del && !req.auth(res, ['dev', 'admin'], 'all')) return;
+ sampleData.material = sampleData.material_id; // map data to right keys
+ sampleData.material.group = sampleData.material.group_id.name;
+ sampleData.material.supplier = sampleData.material.supplier_id.name;
+ sampleData.user = sampleData.user_id.name;
+ sampleData.notes = sampleData.note_id ? sampleData.note_id : {};
+ MeasurementModel.find({sample_id: sampleData._id, status: {$ne: 'deleted'}})
+ .lean().exec((err, data) => {
+ sampleData.measurements = data;
+ if (['dev', 'admin'].indexOf(req.authDetails.level) < 0) { // strip dpt values if not dev or admin
+ sampleData.measurements.forEach(measurement => {
+ if (measurement.values[globals.spectrum.dpt]) {
+ delete measurement.values[globals.spectrum.dpt];
+ }
+ });
+ }
+ res.json(SampleValidate.output(sampleData, 'details'));
+ });
+ }
+ else {
+ res.status(404).json({status: 'Not found'});
+ }
}
function setStatus (status, req, res, next) {
- SampleModel.findByIdAndUpdate(req.params.id, {status}).log(req).lean().exec((err, data) => {
- if (err) return next(err);
+ SampleModel.findByIdAndUpdate(req.params.id, {status}).log(req).lean().exec((err, data) => {
+ if (err) return next(err);
- if (!data) {
- return res.status(404).json({status: 'Not found'});
- }
- MeasurementModel.updateMany({sample_id: mongoose.Types.ObjectId(req.params.id)}, {status})
- .log(req).lean().exec(err => {
- if (err) return next(err);
+ if (!data) {
+ return res.status(404).json({status: 'Not found'});
+ }
+ MeasurementModel.updateMany({sample_id: mongoose.Types.ObjectId(req.params.id)}, {status})
+ .log(req).lean().exec(err => {
+ if (err) return next(err);
- res.json({status: 'OK'});
- });
- });
-}
\ No newline at end of file
+ res.json({status: 'OK'});
+ });
+ });
+}
diff --git a/src/routes/template.spec.ts b/src/routes/template.spec.ts
index d7e4b79..6b38e75 100644
--- a/src/routes/template.spec.ts
+++ b/src/routes/template.spec.ts
@@ -9,653 +9,653 @@ import mongoose from 'mongoose';
describe('/template', () => {
- let server;
- before(done => TestHelper.before(done));
- beforeEach(done => server = TestHelper.beforeEach(server, done));
- afterEach(done => TestHelper.afterEach(server, done));
- after(done => TestHelper.after(done));
+ let server;
+ before(done => TestHelper.before(done));
+ beforeEach(done => server = TestHelper.beforeEach(server, done));
+ afterEach(done => TestHelper.afterEach(server, done));
+ after(done => TestHelper.after(done));
- describe('/template/condition', () => {
- describe('GET /template/conditions', () => {
- it('returns all condition templates', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/template/conditions',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.condition_templates.length);
- should(res.body).matchEach(condition => {
- should(condition).have.only.keys('_id', 'name', 'version', 'first_id', 'parameters');
- should(condition).have.property('_id').be.type('string');
- should(condition).have.property('name').be.type('string');
- should(condition).have.property('version').be.type('number');
- should(condition).have.property('first_id').be.type('string');
- should(condition.parameters).matchEach(number => {
- should(number).have.only.keys('name', 'range');
- should(number).have.property('name').be.type('string');
- should(number).have.property('range').be.type('object');
- });
- });
- done();
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/template/conditions',
- auth: {key: 'janedoe'},
- httpStatus: 401
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/template/conditions',
- httpStatus: 401
- });
- });
- });
+ describe('/template/condition', () => {
+ describe('GET /template/conditions', () => {
+ it('returns all condition templates', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/template/conditions',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.condition_templates.length);
+ should(res.body).matchEach(condition => {
+ should(condition).have.only.keys('_id', 'name', 'version', 'first_id', 'parameters');
+ should(condition).have.property('_id').be.type('string');
+ should(condition).have.property('name').be.type('string');
+ should(condition).have.property('version').be.type('number');
+ should(condition).have.property('first_id').be.type('string');
+ should(condition.parameters).matchEach(number => {
+ should(number).have.only.keys('name', 'range');
+ should(number).have.property('name').be.type('string');
+ should(number).have.property('range').be.type('object');
+ });
+ });
+ done();
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/template/conditions',
+ auth: {key: 'janedoe'},
+ httpStatus: 401
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/template/conditions',
+ httpStatus: 401
+ });
+ });
+ });
- describe('GET /template/condition/{id}', () => {
- it('returns the right condition template', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, first_id: '200000000000000000000001', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]}
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/template/condition/200000000000000000000001',
- auth: {key: 'janedoe'},
- httpStatus: 401
- });
- });
- it('rejects an unknown id', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/template/condition/000000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 404
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/template/condition/200000000000000000000001',
- httpStatus: 401
- });
- });
- });
+ describe('GET /template/condition/{id}', () => {
+ it('returns the right condition template', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, first_id: '200000000000000000000001', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]}
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/template/condition/200000000000000000000001',
+ auth: {key: 'janedoe'},
+ httpStatus: 401
+ });
+ });
+ it('rejects an unknown id', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/template/condition/000000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 404
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/template/condition/200000000000000000000001',
+ httpStatus: 401
+ });
+ });
+ });
- describe('PUT /template/condition/{id}', () => {
- it('returns the right condition template', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {},
- res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, first_id: '200000000000000000000001', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]}
- });
- });
- it('keeps unchanged properties', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {name: 'heat treatment', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]},
- res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, first_id: '200000000000000000000001', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]}
- });
- });
- it('keeps only one unchanged property', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {name: 'heat treatment'},
- res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, first_id: '200000000000000000000001', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]}
- });
- });
- it('changes the given properties', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
- }).end((err, res) => {
- if (err) return done(err);
- TemplateConditionModel.findById(res.body._id).lean().exec((err, data:any) => {
- if (err) return done(err);
- should(data).have.only.keys('_id', 'first_id', 'name', 'version', 'parameters', '__v');
- should(data.first_id.toString()).be.eql('200000000000000000000001');
- should(data).have.property('name', 'heat aging');
- should(data).have.property('version', 2);
- should(data.first_id.toString()).be.eql('200000000000000000000001');
- should(data).have.property('parameters').have.lengthOf(1);
- should(data.parameters[0]).have.property('name', 'time');
- should(data.parameters[0]).have.property('range');
- should(data.parameters[0].range).have.property('min', 1);
- done();
- });
- });
- });
- it('renames all occurrences instead of creating a new version when only the parameter name is changed', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200, req: {name: 'heat treatment', parameters: [{name: 'treatmentMaterial', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({_id: '200000000000000000000001', name: 'heat treatment', version: 1, first_id: '200000000000000000000001', parameters: [{name: 'treatmentMaterial', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]});
- SampleModel.find({'condition.condition_template': mongoose.Types.ObjectId('200000000000000000000001')}).lean().exec((err, data:any) => {
- if (err) return done(err);
- should(data).matchEach(sample => {
- should(sample.condition).have.only.keys('treatmentMaterial', 'weeks', 'condition_template');
- });
- done();
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]},
- log: {
- collection: 'condition_templates',
- dataAdd: {
- first_id: '200000000000000000000001',
- version: 2
- }
- }
- });
- });
- it('does not increase the version on name change', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {name: 'heat aging'}
- }).end((err, res) => {
- if (err) return done(err);
- TemplateConditionModel.findById(res.body._id).lean().exec((err, data:any) => {
- if (err) return done(err);
- should(data).have.only.keys('_id', 'first_id', 'name', 'version', 'parameters', '__v');
- should(data.first_id.toString()).be.eql('200000000000000000000001');
- should(data).have.property('name', 'heat aging');
- should(data).have.property('version', 1);
- should(data).have.property('parameters').have.lengthOf(2);
- should(data.parameters[0]).have.property('name', 'material');
- should(data.parameters[1]).have.property('name', 'weeks');
- done();
- });
- });
- });
- it('does not increase the version on name change when property ranges stayed the same', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {name: 'heat aging', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'duration', range: {min: 1, max: 10, required: true}}]}
- }).end((err, res) => {
- if (err) return done(err);
- TemplateConditionModel.findById(res.body._id).lean().exec((err, data:any) => {
- if (err) return done(err);
- should(data).have.only.keys('_id', 'first_id', 'name', 'version', 'parameters', '__v');
- should(data.first_id.toString()).be.eql('200000000000000000000001');
- should(data).have.property('name', 'heat aging');
- should(data).have.property('version', 1);
- should(data).have.property('parameters').have.lengthOf(2);
- should(data.parameters[0]).have.property('name', 'material');
- should(data.parameters[1]).have.property('name', 'duration');
- done();
- });
- });
- });
- it('supports values ranges', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {parameters: [{name: 'time', range: {values: [1, 2, 5]}}]}
- }).end((err, res) => {
- if (err) return done(err);
- should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, first_id: '200000000000000000000001', parameters: [{name: 'time', range: {values: [1, 2, 5]}}]});
- done();
- });
- });
- it('supports min max ranges', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {parameters: [{name: 'time', range: {min: 1, max: 11}}]}
- }).end((err, res) => {
- if (err) return done(err);
- should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, first_id: '200000000000000000000001', parameters: [{name: 'time', range: {min: 1, max: 11}}]});
- done();
- });
- });
- it('supports array type ranges', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {parameters: [{name: 'time', range: {type: 'array'}}]}
- }).end((err, res) => {
- if (err) return done(err);
- should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, first_id: '200000000000000000000001', parameters: [{name: 'time', range: {type: 'array'}}]});
- done();
- });
- });
- it('supports required ranges', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {parameters: [{name: 'time', range: {required: true}}]}
- }).end((err, res) => {
- if (err) return done(err);
- should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, first_id: '200000000000000000000001', parameters: [{name: 'time', range: {required: true}}]});
- done();
- });
- });
- it('supports empty ranges', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {parameters: [{name: 'time', range: {}}]}
- }).end((err, res) => {
- if (err) return done(err);
- should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, first_id: '200000000000000000000001', parameters: [{name: 'time', range: {}}]});
- done();
- });
- });
- it('rejects `condition_template` as parameter name', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {parameters: [{name: 'condition_template', range: {}}]},
- res: {status: 'Invalid body format', details: '"parameters[0].name" contains an invalid value'}
- });
- });
- it('rejects not specified parameters', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {name: 'heat treatment', parameters: [{name: 'material', range: {xx: 5}}]},
- res: {status: 'Invalid body format', details: '"parameters[0].range.xx" is not allowed'}
- });
- });
- it('rejects an invalid id', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/2000000000h0000000000001',
- auth: {basic: 'admin'},
- httpStatus: 404,
- req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
- });
- });
- it('rejects an unknown id', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/000000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 404,
- req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {key: 'admin'},
- httpStatus: 401,
- req: {}
- });
- });
- it('rejects requests from a write user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/condition/200000000000000000000001',
- httpStatus: 401,
- req: {}
- });
- });
- });
+ describe('PUT /template/condition/{id}', () => {
+ it('returns the right condition template', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {},
+ res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, first_id: '200000000000000000000001', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]}
+ });
+ });
+ it('keeps unchanged properties', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {name: 'heat treatment', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]},
+ res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, first_id: '200000000000000000000001', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]}
+ });
+ });
+ it('keeps only one unchanged property', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {name: 'heat treatment'},
+ res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, first_id: '200000000000000000000001', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]}
+ });
+ });
+ it('changes the given properties', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
+ }).end((err, res) => {
+ if (err) return done(err);
+ TemplateConditionModel.findById(res.body._id).lean().exec((err, data:any) => {
+ if (err) return done(err);
+ should(data).have.only.keys('_id', 'first_id', 'name', 'version', 'parameters', '__v');
+ should(data.first_id.toString()).be.eql('200000000000000000000001');
+ should(data).have.property('name', 'heat aging');
+ should(data).have.property('version', 2);
+ should(data.first_id.toString()).be.eql('200000000000000000000001');
+ should(data).have.property('parameters').have.lengthOf(1);
+ should(data.parameters[0]).have.property('name', 'time');
+ should(data.parameters[0]).have.property('range');
+ should(data.parameters[0].range).have.property('min', 1);
+ done();
+ });
+ });
+ });
+ it('renames all occurrences instead of creating a new version when only the parameter name is changed', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200, req: {name: 'heat treatment', parameters: [{name: 'treatmentMaterial', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({_id: '200000000000000000000001', name: 'heat treatment', version: 1, first_id: '200000000000000000000001', parameters: [{name: 'treatmentMaterial', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]});
+ SampleModel.find({'condition.condition_template': mongoose.Types.ObjectId('200000000000000000000001')}).lean().exec((err, data:any) => {
+ if (err) return done(err);
+ should(data).matchEach(sample => {
+ should(sample.condition).have.only.keys('treatmentMaterial', 'weeks', 'condition_template');
+ });
+ done();
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]},
+ log: {
+ collection: 'condition_templates',
+ dataAdd: {
+ first_id: '200000000000000000000001',
+ version: 2
+ }
+ }
+ });
+ });
+ it('does not increase the version on name change', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {name: 'heat aging'}
+ }).end((err, res) => {
+ if (err) return done(err);
+ TemplateConditionModel.findById(res.body._id).lean().exec((err, data:any) => {
+ if (err) return done(err);
+ should(data).have.only.keys('_id', 'first_id', 'name', 'version', 'parameters', '__v');
+ should(data.first_id.toString()).be.eql('200000000000000000000001');
+ should(data).have.property('name', 'heat aging');
+ should(data).have.property('version', 1);
+ should(data).have.property('parameters').have.lengthOf(2);
+ should(data.parameters[0]).have.property('name', 'material');
+ should(data.parameters[1]).have.property('name', 'weeks');
+ done();
+ });
+ });
+ });
+ it('does not increase the version on name change when property ranges stayed the same', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {name: 'heat aging', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'duration', range: {min: 1, max: 10, required: true}}]}
+ }).end((err, res) => {
+ if (err) return done(err);
+ TemplateConditionModel.findById(res.body._id).lean().exec((err, data:any) => {
+ if (err) return done(err);
+ should(data).have.only.keys('_id', 'first_id', 'name', 'version', 'parameters', '__v');
+ should(data.first_id.toString()).be.eql('200000000000000000000001');
+ should(data).have.property('name', 'heat aging');
+ should(data).have.property('version', 1);
+ should(data).have.property('parameters').have.lengthOf(2);
+ should(data.parameters[0]).have.property('name', 'material');
+ should(data.parameters[1]).have.property('name', 'duration');
+ done();
+ });
+ });
+ });
+ it('supports values ranges', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {parameters: [{name: 'time', range: {values: [1, 2, 5]}}]}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, first_id: '200000000000000000000001', parameters: [{name: 'time', range: {values: [1, 2, 5]}}]});
+ done();
+ });
+ });
+ it('supports min max ranges', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {parameters: [{name: 'time', range: {min: 1, max: 11}}]}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, first_id: '200000000000000000000001', parameters: [{name: 'time', range: {min: 1, max: 11}}]});
+ done();
+ });
+ });
+ it('supports array type ranges', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {parameters: [{name: 'time', range: {type: 'array'}}]}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, first_id: '200000000000000000000001', parameters: [{name: 'time', range: {type: 'array'}}]});
+ done();
+ });
+ });
+ it('supports required ranges', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {parameters: [{name: 'time', range: {required: true}}]}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, first_id: '200000000000000000000001', parameters: [{name: 'time', range: {required: true}}]});
+ done();
+ });
+ });
+ it('supports empty ranges', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {parameters: [{name: 'time', range: {}}]}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, first_id: '200000000000000000000001', parameters: [{name: 'time', range: {}}]});
+ done();
+ });
+ });
+ it('rejects `condition_template` as parameter name', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {parameters: [{name: 'condition_template', range: {}}]},
+ res: {status: 'Invalid body format', details: '"parameters[0].name" contains an invalid value'}
+ });
+ });
+ it('rejects not specified parameters', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {name: 'heat treatment', parameters: [{name: 'material', range: {xx: 5}}]},
+ res: {status: 'Invalid body format', details: '"parameters[0].range.xx" is not allowed'}
+ });
+ });
+ it('rejects an invalid id', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/2000000000h0000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 404,
+ req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
+ });
+ });
+ it('rejects an unknown id', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/000000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 404,
+ req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {key: 'admin'},
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ it('rejects requests from a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/condition/200000000000000000000001',
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ });
- describe('POST /template/condition/new', () => {
- it('returns the right condition template', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/template/condition/new',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {name: 'heat treatment3', parameters: [{name: 'material', range: {values: ['copper']}}]}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).have.only.keys('_id', 'name', 'version', 'first_id', 'parameters');
- should(res.body).have.property('name', 'heat treatment3');
- should(res.body).have.property('version', 1);
- should(res.body._id).be.eql(res.body.first_id);
- should(res.body).have.property('parameters').have.lengthOf(1);
- should(res.body.parameters[0]).have.property('name', 'material');
- should(res.body.parameters[0]).have.property('range');
- should(res.body.parameters[0].range).have.property('values');
- should(res.body.parameters[0].range.values[0]).be.eql('copper');
- done();
- });
- });
- it('stores the template', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/template/condition/new',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
- }).end((err, res) => {
- if (err) return done(err);
- TemplateConditionModel.findById(res.body._id).lean().exec((err, data:any) => {
- if (err) return done(err);
- should(data).have.only.keys('_id', 'first_id', 'name', 'version', 'parameters', '__v');
- should(data.first_id.toString()).be.eql(data._id.toString());
- should(data).have.property('name', 'heat aging');
- should(data).have.property('version', 1);
- should(res.body._id).be.eql(res.body.first_id);
- should(data).have.property('parameters').have.lengthOf(1);
- should(data.parameters[0]).have.property('name', 'time');
- should(data.parameters[0]).have.property('range');
- should(data.parameters[0].range).have.property('min', 1);
- done();
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/template/condition/new',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]},
- log: {
- collection: 'condition_templates',
- dataAdd: {version: 1},
- dataIgn: ['first_id']
- }
- });
- });
- it('rejects a missing name', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/template/condition/new',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {parameters: [{name: 'time', range: {min: 1}}]},
- res: {status: 'Invalid body format', details: '"name" is required'}
- });
- });
- it('rejects `condition_template` as parameter name', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/template/condition/new',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {name: 'heat aging', parameters: [{name: 'condition_template', range: {min: 1}}]},
- res: {status: 'Invalid body format', details: '"parameters[0].name" contains an invalid value'}
- });
- });
- it('rejects a number prefix', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/template/condition/new',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {name: 'heat aging', number_prefix: 'C', parameters: [{name: 'time', range: {min: 1}}]},
- res: {status: 'Invalid body format', details: '"number_prefix" is not allowed'}
- });
- });
- it('rejects missing parameters', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/template/condition/new',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {name: 'heat aging'},
- res: {status: 'Invalid body format', details: '"parameters" is required'}
- });
- });
- it('rejects a missing parameter name', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/template/condition/new',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {name: 'heat aging', parameters: [{range: {min: 1}}]},
- res: {status: 'Invalid body format', details: '"parameters[0].name" is required'}
- });
- });
- it('rejects a missing parameter range', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/template/condition/new',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {name: 'heat aging', parameters: [{name: 'time'}]},
- res: {status: 'Invalid body format', details: '"parameters[0].range" is required'}
- });
- });
- it('rejects an invalid parameter range property', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/template/condition/new',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {name: 'heat aging', parameters: [{name: 'time', range: {xx: 1}}]},
- res: {status: 'Invalid body format', details: '"parameters[0].range.xx" is not allowed'}
- });
- });
- it('rejects wrong properties', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/template/condition/new',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {name: 'heat aging', parameters: [{name: 'time', range: {}}], xx: 33},
- res: {status: 'Invalid body format', details: '"xx" is not allowed'}
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/template/condition/new',
- auth: {key: 'admin'},
- httpStatus: 401,
- req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
- });
- });
- it('rejects requests from a write user', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/template/condition/new',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/template/condition/new',
- httpStatus: 401,
- req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
- });
- });
- });
- });
+ describe('POST /template/condition/new', () => {
+ it('returns the right condition template', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/template/condition/new',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {name: 'heat treatment3', parameters: [{name: 'material', range: {values: ['copper']}}]}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).have.only.keys('_id', 'name', 'version', 'first_id', 'parameters');
+ should(res.body).have.property('name', 'heat treatment3');
+ should(res.body).have.property('version', 1);
+ should(res.body._id).be.eql(res.body.first_id);
+ should(res.body).have.property('parameters').have.lengthOf(1);
+ should(res.body.parameters[0]).have.property('name', 'material');
+ should(res.body.parameters[0]).have.property('range');
+ should(res.body.parameters[0].range).have.property('values');
+ should(res.body.parameters[0].range.values[0]).be.eql('copper');
+ done();
+ });
+ });
+ it('stores the template', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/template/condition/new',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
+ }).end((err, res) => {
+ if (err) return done(err);
+ TemplateConditionModel.findById(res.body._id).lean().exec((err, data:any) => {
+ if (err) return done(err);
+ should(data).have.only.keys('_id', 'first_id', 'name', 'version', 'parameters', '__v');
+ should(data.first_id.toString()).be.eql(data._id.toString());
+ should(data).have.property('name', 'heat aging');
+ should(data).have.property('version', 1);
+ should(res.body._id).be.eql(res.body.first_id);
+ should(data).have.property('parameters').have.lengthOf(1);
+ should(data.parameters[0]).have.property('name', 'time');
+ should(data.parameters[0]).have.property('range');
+ should(data.parameters[0].range).have.property('min', 1);
+ done();
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/template/condition/new',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]},
+ log: {
+ collection: 'condition_templates',
+ dataAdd: {version: 1},
+ dataIgn: ['first_id']
+ }
+ });
+ });
+ it('rejects a missing name', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/template/condition/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {parameters: [{name: 'time', range: {min: 1}}]},
+ res: {status: 'Invalid body format', details: '"name" is required'}
+ });
+ });
+ it('rejects `condition_template` as parameter name', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/template/condition/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {name: 'heat aging', parameters: [{name: 'condition_template', range: {min: 1}}]},
+ res: {status: 'Invalid body format', details: '"parameters[0].name" contains an invalid value'}
+ });
+ });
+ it('rejects a number prefix', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/template/condition/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {name: 'heat aging', number_prefix: 'C', parameters: [{name: 'time', range: {min: 1}}]},
+ res: {status: 'Invalid body format', details: '"number_prefix" is not allowed'}
+ });
+ });
+ it('rejects missing parameters', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/template/condition/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {name: 'heat aging'},
+ res: {status: 'Invalid body format', details: '"parameters" is required'}
+ });
+ });
+ it('rejects a missing parameter name', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/template/condition/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {name: 'heat aging', parameters: [{range: {min: 1}}]},
+ res: {status: 'Invalid body format', details: '"parameters[0].name" is required'}
+ });
+ });
+ it('rejects a missing parameter range', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/template/condition/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {name: 'heat aging', parameters: [{name: 'time'}]},
+ res: {status: 'Invalid body format', details: '"parameters[0].range" is required'}
+ });
+ });
+ it('rejects an invalid parameter range property', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/template/condition/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {name: 'heat aging', parameters: [{name: 'time', range: {xx: 1}}]},
+ res: {status: 'Invalid body format', details: '"parameters[0].range.xx" is not allowed'}
+ });
+ });
+ it('rejects wrong properties', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/template/condition/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {name: 'heat aging', parameters: [{name: 'time', range: {}}], xx: 33},
+ res: {status: 'Invalid body format', details: '"xx" is not allowed'}
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/template/condition/new',
+ auth: {key: 'admin'},
+ httpStatus: 401,
+ req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
+ });
+ });
+ it('rejects requests from a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/template/condition/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/template/condition/new',
+ httpStatus: 401,
+ req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}
+ });
+ });
+ });
+ });
- describe('/template/measurement', () => {
- describe('GET /template/measurements', () => {
- it('returns all measurement templates', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/template/measurements',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.measurement_templates.length);
- should(res.body).matchEach(measurement => {
- should(measurement).have.only.keys('_id', 'name', 'version', 'first_id', 'parameters');
- should(measurement).have.property('_id').be.type('string');
- should(measurement).have.property('first_id').be.type('string');
- should(measurement).have.property('name').be.type('string');
- should(measurement).have.property('version').be.type('number');
- should(measurement.parameters).matchEach(number => {
- should(number).have.only.keys('name', 'range');
- should(number).have.property('name').be.type('string');
- should(number).have.property('range').be.type('object');
- });
- });
- done();
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/template/measurements',
- auth: {key: 'janedoe'},
- httpStatus: 401
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/template/measurements',
- httpStatus: 401
- });
- });
- });
- describe('PUT /template/measurement/{id}', () => {
- it('renames all occurrences instead of creating a new version when only the parameter name is changed', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/measurement/300000000000000000000001',
- auth: {basic: 'admin'},
- httpStatus: 200, req: {name: 'spectrum', parameters: [{name: 'spectrumValues', range: {type: 'array'}}, {name: 'device', range: {}}, {name: 'filename', range: {}}]}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({_id: '300000000000000000000001', name: 'spectrum', version: 1, first_id: '300000000000000000000001', parameters: [{name: 'spectrumValues', range: {type: 'array'}}, {name: 'device', range: {}}, {name: 'filename', range: {}}]});
- MeasurementModel.find({'measurement_template': mongoose.Types.ObjectId('300000000000000000000001')}).lean().exec((err, data:any) => {
- if (err) return done(err);
- should(data).matchEach(measurement => {
- should(measurement.values).have.only.keys('spectrumValues', 'device', 'filename');
- });
- done();
- });
- });
- });
- });
- // other methods should be covered by condition tests
- });
+ describe('/template/measurement', () => {
+ describe('GET /template/measurements', () => {
+ it('returns all measurement templates', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/template/measurements',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.measurement_templates.length);
+ should(res.body).matchEach(measurement => {
+ should(measurement).have.only.keys('_id', 'name', 'version', 'first_id', 'parameters');
+ should(measurement).have.property('_id').be.type('string');
+ should(measurement).have.property('first_id').be.type('string');
+ should(measurement).have.property('name').be.type('string');
+ should(measurement).have.property('version').be.type('number');
+ should(measurement.parameters).matchEach(number => {
+ should(number).have.only.keys('name', 'range');
+ should(number).have.property('name').be.type('string');
+ should(number).have.property('range').be.type('object');
+ });
+ });
+ done();
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/template/measurements',
+ auth: {key: 'janedoe'},
+ httpStatus: 401
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/template/measurements',
+ httpStatus: 401
+ });
+ });
+ });
+ describe('PUT /template/measurement/{id}', () => {
+ it('renames all occurrences instead of creating a new version when only the parameter name is changed', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/measurement/300000000000000000000001',
+ auth: {basic: 'admin'},
+ httpStatus: 200, req: {name: 'spectrum', parameters: [{name: 'spectrumValues', range: {type: 'array'}}, {name: 'device', range: {}}, {name: 'filename', range: {}}]}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({_id: '300000000000000000000001', name: 'spectrum', version: 1, first_id: '300000000000000000000001', parameters: [{name: 'spectrumValues', range: {type: 'array'}}, {name: 'device', range: {}}, {name: 'filename', range: {}}]});
+ MeasurementModel.find({'measurement_template': mongoose.Types.ObjectId('300000000000000000000001')}).lean().exec((err, data:any) => {
+ if (err) return done(err);
+ should(data).matchEach(measurement => {
+ should(measurement.values).have.only.keys('spectrumValues', 'device', 'filename');
+ });
+ done();
+ });
+ });
+ });
+ });
+ // other methods should be covered by condition tests
+ });
- describe('/template/material', () => {
- describe('GET /template/materials', () => {
- it('returns all material templates', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/template/materials',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.material_templates.length);
- should(res.body).matchEach(measurement => {
- should(measurement).have.only.keys('_id', 'name', 'version', 'first_id', 'parameters');
- should(measurement).have.property('_id').be.type('string');
- should(measurement).have.property('name').be.type('string');
- should(measurement).have.property('version').be.type('number');
- should(measurement.parameters).matchEach(number => {
- should(number).have.only.keys('name', 'range');
- should(number).have.property('name').be.type('string');
- should(number).have.property('range').be.type('object');
- });
- });
- done();
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/template/materials',
- auth: {key: 'janedoe'},
- httpStatus: 401
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/template/materials',
- httpStatus: 401
- });
- });
- });
- describe('PUT /template/material/{id}', () => {
- it('renames all occurrences instead of creating a new version when only the parameter name is changed', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/template/material/130000000000000000000003',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {name: 'plastic', parameters: [ {name: 'glassfiber', range: {min: 0, max: 100, required: true}}, {name: 'carbonfiber', range: {min: 0, max: 100, required: true}}, {name: 'mineral', range: {min: 0, max: 100, required: true}}]}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({_id: '130000000000000000000003', name: 'plastic', version: 2, first_id: '130000000000000000000001', parameters: [ {name: 'glassfiber', range: {min: 0, max: 100, required: true}}, {name: 'carbonfiber', range: {min: 0, max: 100, required: true}}, {name: 'mineral', range: {min: 0, max: 100, required: true}}]});
- MaterialModel.find({'properties': mongoose.Types.ObjectId('130000000000000000000003')}).lean().exec((err, data:any) => {
- if (err) return done(err);
- should(data).matchEach(material => {
- should(material.parameters).have.only.keys('glassfiber', 'carbonfiber', 'mineral', 'material_template');
- });
- done();
- });
- });
- });
- });
- // other methods should be covered by condition tests
- });
-});
\ No newline at end of file
+ describe('/template/material', () => {
+ describe('GET /template/materials', () => {
+ it('returns all material templates', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/template/materials',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.material_templates.length);
+ should(res.body).matchEach(measurement => {
+ should(measurement).have.only.keys('_id', 'name', 'version', 'first_id', 'parameters');
+ should(measurement).have.property('_id').be.type('string');
+ should(measurement).have.property('name').be.type('string');
+ should(measurement).have.property('version').be.type('number');
+ should(measurement.parameters).matchEach(number => {
+ should(number).have.only.keys('name', 'range');
+ should(number).have.property('name').be.type('string');
+ should(number).have.property('range').be.type('object');
+ });
+ });
+ done();
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/template/materials',
+ auth: {key: 'janedoe'},
+ httpStatus: 401
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/template/materials',
+ httpStatus: 401
+ });
+ });
+ });
+ describe('PUT /template/material/{id}', () => {
+ it('renames all occurrences instead of creating a new version when only the parameter name is changed', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/template/material/130000000000000000000003',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {name: 'plastic', parameters: [ {name: 'glassfiber', range: {min: 0, max: 100, required: true}}, {name: 'carbonfiber', range: {min: 0, max: 100, required: true}}, {name: 'mineral', range: {min: 0, max: 100, required: true}}]}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({_id: '130000000000000000000003', name: 'plastic', version: 2, first_id: '130000000000000000000001', parameters: [ {name: 'glassfiber', range: {min: 0, max: 100, required: true}}, {name: 'carbonfiber', range: {min: 0, max: 100, required: true}}, {name: 'mineral', range: {min: 0, max: 100, required: true}}]});
+ MaterialModel.find({'properties': mongoose.Types.ObjectId('130000000000000000000003')}).lean().exec((err, data:any) => {
+ if (err) return done(err);
+ should(data).matchEach(material => {
+ should(material.parameters).have.only.keys('glassfiber', 'carbonfiber', 'mineral', 'material_template');
+ });
+ done();
+ });
+ });
+ });
+ });
+ // other methods should be covered by condition tests
+ });
+});
diff --git a/src/routes/template.ts b/src/routes/template.ts
index 8eeffdd..d8966f9 100644
--- a/src/routes/template.ts
+++ b/src/routes/template.ts
@@ -18,141 +18,141 @@ import db from '../db';
const router = express.Router();
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
- model(req).find({}).lean().exec((err, data) => {
- if (err) next (err);
- // validate all and filter null values from validation errors
- res.json(_.compact(data.map(e => TemplateValidate.output(e))));
- });
+ req.params.collection = req.params.collection.replace(/s$/g, ''); // remove trailing s
+ model(req).find({}).lean().exec((err, data) => {
+ if (err) next (err);
+ // validate all and filter null values from validation errors
+ res.json(_.compact(data.map(e => TemplateValidate.output(e))));
+ });
});
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) => {
- if (err) next (err);
- if (data) {
- res.json(TemplateValidate.output(data));
- }
- else {
- res.status(404).json({status: 'Not found'});
- }
- });
+ model(req).findById(req.params.id).lean().exec((err, data) => {
+ if (err) next (err);
+ if (data) {
+ res.json(TemplateValidate.output(data));
+ }
+ else {
+ res.status(404).json({status: 'Not found'});
+ }
+ });
});
router.put('/template/:collection(measurement|condition|material)/' + IdValidate.parameter(),
- async (req, res, next) => {
- if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
+ async (req, res, next) => {
+ if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
- const {error, value: template} = TemplateValidate.input(req.body, 'change');
- if (error) return res400(error, res);
+ const {error, value: template} = TemplateValidate.input(req.body, 'change');
+ if (error) return res400(error, res);
- // find given template
- const templateRef = await model(req).findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
- if (templateRef instanceof Error) return;
- if (!templateRef) {
- return res.status(404).json({status: 'Not found'});
- }
- // find latest version
- const templateData = await model(req).findOne({first_id: templateRef.first_id}).sort({version: -1})
- .lean().exec().catch(err => {next(err);}) as any;
- if (templateData instanceof Error) return;
- if (!templateData) {
- return res.status(404).json({status: 'Not found'});
- }
+ // find given template
+ const templateRef = await model(req).findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
+ if (templateRef instanceof Error) return;
+ if (!templateRef) {
+ return res.status(404).json({status: 'Not found'});
+ }
+ // find latest version
+ const templateData = await model(req).findOne({first_id: templateRef.first_id}).sort({version: -1})
+ .lean().exec().catch(err => {next(err);}) as any;
+ if (templateData instanceof Error) return;
+ if (!templateData) {
+ return res.status(404).json({status: 'Not found'});
+ }
- if (!_.isEqual(_.pick(templateData, _.keys(template)), template)) { // data 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})
- .log(req).lean().exec((err, data) => {
- if (err) next (err);
- res.json(TemplateValidate.output(data));
- });
- }
- else if (template.parameters.filter((e, i) => _.isEqual(e.range, templateData.parameters[i].range)).length
- === templateData.parameters.length) { // only names changed
- const changedParameterNames = template.parameters.map((e, i) => ( // list of new names
- {name: e.name, index: i, oldName: templateData.parameters[i].name}
- )).filter(e => e.name !== e.oldName);
+ if (!_.isEqual(_.pick(templateData, _.keys(template)), template)) { // data 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})
+ .log(req).lean().exec((err, data) => {
+ if (err) next (err);
+ res.json(TemplateValidate.output(data));
+ });
+ }
+ else if (template.parameters.filter((e, i) => _.isEqual(e.range, templateData.parameters[i].range)).length
+ === templateData.parameters.length) { // only names changed
+ const changedParameterNames = template.parameters.map((e, i) => ( // list of new names
+ {name: e.name, index: i, oldName: templateData.parameters[i].name}
+ )).filter(e => e.name !== e.oldName);
- // custom mappings for different collections
- let targetModel; // model of the collection where the template is used
- let pathPrefix; // path to the parameters in use
- let templatePath; // complete path of the template property
- switch (req.params.collection) {
- case 'condition':
- targetModel = SampleModel;
- pathPrefix = 'condition.';
- templatePath = 'condition.condition_template';
- break;
- case 'measurement':
- targetModel = MeasurementModel;
- pathPrefix = 'values.';
- templatePath = 'measurement_template';
- break;
- case 'material':
- targetModel = MaterialModel;
- pathPrefix = 'properties.';
- templatePath = 'properties.material_template';
- break;
- }
+ // custom mappings for different collections
+ let targetModel; // model of the collection where the template is used
+ let pathPrefix; // path to the parameters in use
+ let templatePath; // complete path of the template property
+ switch (req.params.collection) {
+ case 'condition':
+ targetModel = SampleModel;
+ pathPrefix = 'condition.';
+ templatePath = 'condition.condition_template';
+ break;
+ case 'measurement':
+ targetModel = MeasurementModel;
+ pathPrefix = 'values.';
+ templatePath = 'measurement_template';
+ break;
+ case 'material':
+ targetModel = MaterialModel;
+ pathPrefix = 'properties.';
+ templatePath = 'properties.material_template';
+ break;
+ }
- targetModel.updateMany({[templatePath]: mongoose.Types.ObjectId(templateData._id)},
- {$rename:
- changedParameterNames.reduce((s, e) => {s[pathPrefix + e.oldName] = pathPrefix + e.name; return s;}, {})
- }) .log(req).lean().exec(err => {
- if (err) return next(err);
- model(req).findByIdAndUpdate(req.params.id,
- {$set:
- changedParameterNames.reduce(
- (s, e) => {s[`parameters.${e.index}.name`] = e.name; return s;}, {name: template.name}
- ),
- },{new: true}).log(req).lean().exec((err, data) => {
- if (err) next (err);
- res.json(TemplateValidate.output(data));
- });
- });
- }
- else {
- template.version = templateData.version + 1; // increase version
- // save new template, fill with old properties
- await new (model(req))(_.assign({}, _.omit(templateData, ['_id', '__v']), template)).save((err, data) => {
- if (err) next (err);
- db.log(req, req.params.collection + '_templates', {_id: data._id}, data.toObject());
- res.json(TemplateValidate.output(data.toObject()));
- });
- }
- }
- else {
- res.json(TemplateValidate.output(templateData));
- }
-});
+ targetModel.updateMany({[templatePath]: mongoose.Types.ObjectId(templateData._id)},
+ {$rename:
+ changedParameterNames.reduce((s, e) => {s[pathPrefix + e.oldName] = pathPrefix + e.name; return s;}, {})
+ }) .log(req).lean().exec(err => {
+ if (err) return next(err);
+ model(req).findByIdAndUpdate(req.params.id,
+ {$set:
+ changedParameterNames.reduce(
+ (s, e) => {s[`parameters.${e.index}.name`] = e.name; return s;}, {name: template.name}
+ ),
+ },{new: true}).log(req).lean().exec((err, data) => {
+ if (err) next (err);
+ res.json(TemplateValidate.output(data));
+ });
+ });
+ }
+ else {
+ template.version = templateData.version + 1; // increase version
+ // save new template, fill with old properties
+ await new (model(req))(_.assign({}, _.omit(templateData, ['_id', '__v']), template)).save((err, data) => {
+ if (err) next (err);
+ db.log(req, req.params.collection + '_templates', {_id: data._id}, data.toObject());
+ res.json(TemplateValidate.output(data.toObject()));
+ });
+ }
+ }
+ else {
+ res.json(TemplateValidate.output(templateData));
+ }
+ });
-router.post('/template/:collection(measurement|condition|material)/new', async (req, res, next) => {
- if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
+ router.post('/template/:collection(measurement|condition|material)/new', async (req, res, next) => {
+ if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
- const {error, value: template} = TemplateValidate.input(req.body, 'new');
- if (error) return res400(error, res);
+ const {error, value: template} = TemplateValidate.input(req.body, 'new');
+ if (error) return res400(error, res);
- template._id = mongoose.Types.ObjectId(); // set reference to itself for first version of template
- template.first_id = template._id;
- template.version = 1; // set template version
- await new (model(req))(template).save((err, data) => {
- if (err) next (err);
- db.log(req, req.params.collection + '_templates', {_id: data._id}, data.toObject());
- res.json(TemplateValidate.output(data.toObject()));
- });
-});
+ template._id = mongoose.Types.ObjectId(); // set reference to itself for first version of template
+ template.first_id = template._id;
+ template.version = 1; // set template version
+ await new (model(req))(template).save((err, data) => {
+ if (err) next (err);
+ db.log(req, req.params.collection + '_templates', {_id: data._id}, data.toObject());
+ res.json(TemplateValidate.output(data.toObject()));
+ });
+ });
-module.exports = router;
+ module.exports = router;
-function model (req) { // return right template model
- switch (req.params.collection) {
- case 'condition': return ConditionTemplateModel
- case 'measurement': return MeasurementTemplateModel
- case 'material': return MaterialTemplateModel
- }
-}
\ No newline at end of file
+ function model (req) { // return right template model
+ switch (req.params.collection) {
+ case 'condition': return ConditionTemplateModel
+ case 'measurement': return MeasurementTemplateModel
+ case 'material': return MaterialTemplateModel
+ }
+ }
diff --git a/src/routes/user.spec.ts b/src/routes/user.spec.ts
index c63806b..309c23f 100644
--- a/src/routes/user.spec.ts
+++ b/src/routes/user.spec.ts
@@ -5,792 +5,792 @@ import TestHelper from "../test/helper";
describe('/user', () => {
- let server;
- before(done => TestHelper.before(done));
- beforeEach(done => server = TestHelper.beforeEach(server, done));
- afterEach(done => TestHelper.afterEach(server, done));
- after(done => TestHelper.after(done));
+ let server;
+ before(done => TestHelper.before(done));
+ beforeEach(done => server = TestHelper.beforeEach(server, done));
+ afterEach(done => TestHelper.afterEach(server, done));
+ after(done => TestHelper.after(done));
- describe('GET /users', () => {
- it('returns all users', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/users',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done(err);
- const json = require('../test/db.json');
- should(res.body).have.lengthOf(json.collections.users.length);
- should(res.body).matchEach(user => {
- should(user).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models', 'status');
- should(user).have.property('_id').be.type('string');
- should(user).have.property('email').be.type('string');
- should(user).have.property('name').be.type('string');
- should(user).have.property('level').be.type('string');
- should(user).have.property('location').be.type('string');
- should(user.devices).matchEach(device => {
- should(device).be.type('string');
- });
- should(user.models).matchEach(model => {
- should(model).be.type('string');
- });
- should(user).have.property('status').be.type('string');
- });
- done();
- });
- });
- it('rejects requests from non-admins', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/users',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('rejects requests from an admin API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/users',
- auth: {key: 'admin'},
- httpStatus: 401
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/users',
- httpStatus: 401
- });
- });
- });
+ describe('GET /users', () => {
+ it('returns all users', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/users',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done(err);
+ const json = require('../test/db.json');
+ should(res.body).have.lengthOf(json.collections.users.length);
+ should(res.body).matchEach(user => {
+ should(user).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models', 'status');
+ should(user).have.property('_id').be.type('string');
+ should(user).have.property('email').be.type('string');
+ should(user).have.property('name').be.type('string');
+ should(user).have.property('level').be.type('string');
+ should(user).have.property('location').be.type('string');
+ should(user.devices).matchEach(device => {
+ should(device).be.type('string');
+ });
+ should(user.models).matchEach(model => {
+ should(model).be.type('string');
+ });
+ should(user).have.property('status').be.type('string');
+ });
+ done();
+ });
+ });
+ it('rejects requests from non-admins', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/users',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('rejects requests from an admin API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/users',
+ auth: {key: 'admin'},
+ httpStatus: 401
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/users',
+ httpStatus: 401
+ });
+ });
+ });
- describe('GET /user/{name}', () => {
- it('returns own user details', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/user',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models');
- should(res.body).have.property('_id').be.type('string');
- should(res.body).have.property('email', 'jane.doe@bosch.com');
- should(res.body).have.property('name', 'janedoe');
- should(res.body).have.property('level', 'write');
- should(res.body).have.property('location', 'Rng');
- should(res.body).have.property('devices', ['Alpha I']);
- should(res.body).have.property('models', []);
- done();
- });
- });
- it('returns other user details for admin', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/user/janedoe',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models');
- should(res.body).have.property('_id').be.type('string');
- should(res.body).have.property('email', 'jane.doe@bosch.com');
- should(res.body).have.property('name', 'janedoe');
- should(res.body).have.property('level', 'write');
- should(res.body).have.property('location', 'Rng');
- should(res.body).have.property('devices', ['Alpha I']);
- should(res.body).have.property('models', []);
- done();
- });
- });
- it('rejects requests from non-admins for another user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/user/admin',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('rejects requests from a user API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/user',
- auth: {key: 'janedoe'},
- httpStatus: 401
- });
- });
- it('rejects requests from an admin API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/user/janedoe',
- auth: {key: 'janedoe'},
- httpStatus: 401
- });
- });
- it('returns 404 for an unknown user', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/user/unknown',
- auth: {basic: 'admin'},
- httpStatus: 404
- });
- });
- it('rejects requests from an admin API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/user/janedoe',
- httpStatus: 401
- });
- });
- });
+ describe('GET /user/{name}', () => {
+ it('returns own user details', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/user',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models');
+ should(res.body).have.property('_id').be.type('string');
+ should(res.body).have.property('email', 'jane.doe@bosch.com');
+ should(res.body).have.property('name', 'janedoe');
+ should(res.body).have.property('level', 'write');
+ should(res.body).have.property('location', 'Rng');
+ should(res.body).have.property('devices', ['Alpha I']);
+ should(res.body).have.property('models', []);
+ done();
+ });
+ });
+ it('returns other user details for admin', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/user/janedoe',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models');
+ should(res.body).have.property('_id').be.type('string');
+ should(res.body).have.property('email', 'jane.doe@bosch.com');
+ should(res.body).have.property('name', 'janedoe');
+ should(res.body).have.property('level', 'write');
+ should(res.body).have.property('location', 'Rng');
+ should(res.body).have.property('devices', ['Alpha I']);
+ should(res.body).have.property('models', []);
+ done();
+ });
+ });
+ it('rejects requests from non-admins for another user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/user/admin',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('rejects requests from a user API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/user',
+ auth: {key: 'janedoe'},
+ httpStatus: 401
+ });
+ });
+ it('rejects requests from an admin API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/user/janedoe',
+ auth: {key: 'janedoe'},
+ httpStatus: 401
+ });
+ });
+ it('returns 404 for an unknown user', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/user/unknown',
+ auth: {basic: 'admin'},
+ httpStatus: 404
+ });
+ });
+ it('rejects requests from an admin API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/user/janedoe',
+ httpStatus: 401
+ });
+ });
+ });
- describe('PUT /user/{name}', () => {
- it('returns own user details', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- req: {}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models');
- should(res.body).have.property('_id').be.type('string');
- should(res.body).have.property('email', 'jane.doe@bosch.com');
- should(res.body).have.property('name', 'janedoe');
- should(res.body).have.property('level', 'write');
- should(res.body).have.property('location', 'Rng');
- should(res.body).have.property('devices', ['Alpha I']);
- should(res.body).have.property('models', []);
- done();
- });
- });
- it('returns other user details for admin', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user/janedoe',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models');
- should(res.body).have.property('_id').be.type('string');
- should(res.body).have.property('email', 'jane.doe@bosch.com');
- should(res.body).have.property('name', 'janedoe');
- should(res.body).have.property('level', 'write');
- should(res.body).have.property('location', 'Rng');
- should(res.body).have.property('devices', ['Alpha I']);
- should(res.body).have.property('models', []);
- done();
- });
- });
- it('changes user details as given', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {name: 'adminnew', email: 'adminnew@bosch.com', pass: 'Abc123##', location: 'Abt', devices: ['test'], models: ['120000000000000000000002']}
- }).end(err => {
- if (err) return done (err);
- UserModel.find({name: 'adminnew'}).lean().exec( (err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]).have.only.keys('_id', 'name', 'pass', 'email', 'level', 'location', 'devices', 'models', 'key', 'status', '__v');
- should(data[0]).have.property('_id');
- should(data[0]).have.property('name', 'adminnew');
- should(data[0]).have.property('email', 'adminnew@bosch.com');
- should(data[0]).have.property('pass').not.eql('Abc123##');
- should(data[0]).have.property('level', 'admin');
- should(data[0]).have.property('location', 'Abt');
- should(data[0]).have.property('devices', ['test']);
- should(data[0].models[0].toString()).be.eql('120000000000000000000002');
- should(data[0]).have.property('status', 'new');
- done();
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {name: 'adminnew', email: 'adminnew@bosch.com', pass: 'Abc123##', location: 'Abt', devices: ['test']},
- log: {
- collection: 'users',
- dataIgn: ['pass']
- }
- });
- });
- it('lets the admin change a user level', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user/janedoe',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {level: 'read'}
- }).end(err => {
- if (err) return done (err);
- UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]).have.property('level', 'read');
- done();
- });
- });
- });
- it('does not change the level', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user',
- auth: {basic: 'janedoe'},
- httpStatus: 400, default: false,
- req: {level: 'read'}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'Invalid body format', details: '"level" is not allowed'});
- UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]).have.property('level', 'write');
- done();
- });
- });
- });
- it('lets the admin change accessible models', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user/janedoe',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {models: ['120000000000000000000001']}
- }).end(err => {
- if (err) return done (err);
- UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0].models[0].toString()).be.eql('120000000000000000000001');
- done();
- });
- });
- });
- it('does not change the models', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user',
- auth: {basic: 'janedoe'},
- httpStatus: 400, default: false,
- req: {models: ['120000000000000000000001']}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'Invalid body format', details: '"models" is not allowed'});
- UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]).have.property('models', []);
- done();
- });
- });
- });
- it('rejects a username already in use', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user',
- auth: {basic: 'admin'},
- httpStatus: 400, default: false,
- req: {name: 'janedoe'}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'Username already taken'});
- UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- done();
- });
- });
- });
- it('rejects a username which is in the special names', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/new',
- auth: {basic: 'admin'},
- httpStatus: 400, default: false,
- req: {email: 'j.doe@bosch.com', name: 'passreset', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: []},
- res: {status: 'Username already taken'}
- });
- });
- it('rejects invalid user details', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', location: 44, devices: ['Alpha II']},
- res: {status: 'Invalid body format', details: '"location" must be a string'}
- });
- });
- it('rejects an invalid email address', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {email: 'john.doe'},
- res: {status: 'Invalid body format', details: '"email" must be a valid email'}
- });
- });
- it('rejects an invalid password', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {pass: 'pass'},
- res: {status: 'Invalid body format', details: '"pass" length must be at least 8 characters long'}
- });
- });
- it('rejects requests from non-admins for another user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user/admin',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {}
- });
- });
- it('rejects requests from a user API key', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user',
- auth: {key: 'janedoe'},
- httpStatus: 401,
- req: {}
- });
- });
- it('rejects requests from an admin API key', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user/janedoe',
- auth: {key: 'admin'},
- httpStatus: 401,
- req: {}
- });
- });
- it('returns 404 for an unknown user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user/unknown',
- auth: {basic: 'admin'},
- httpStatus: 404,
- req: {}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user/janedoe',
- httpStatus: 401,
- req: {}
- });
- });
- });
+ describe('PUT /user/{name}', () => {
+ it('returns own user details', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ req: {}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models');
+ should(res.body).have.property('_id').be.type('string');
+ should(res.body).have.property('email', 'jane.doe@bosch.com');
+ should(res.body).have.property('name', 'janedoe');
+ should(res.body).have.property('level', 'write');
+ should(res.body).have.property('location', 'Rng');
+ should(res.body).have.property('devices', ['Alpha I']);
+ should(res.body).have.property('models', []);
+ done();
+ });
+ });
+ it('returns other user details for admin', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user/janedoe',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models');
+ should(res.body).have.property('_id').be.type('string');
+ should(res.body).have.property('email', 'jane.doe@bosch.com');
+ should(res.body).have.property('name', 'janedoe');
+ should(res.body).have.property('level', 'write');
+ should(res.body).have.property('location', 'Rng');
+ should(res.body).have.property('devices', ['Alpha I']);
+ should(res.body).have.property('models', []);
+ done();
+ });
+ });
+ it('changes user details as given', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {name: 'adminnew', email: 'adminnew@bosch.com', pass: 'Abc123##', location: 'Abt', devices: ['test'], models: ['120000000000000000000002']}
+ }).end(err => {
+ if (err) return done (err);
+ UserModel.find({name: 'adminnew'}).lean().exec( (err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.only.keys('_id', 'name', 'pass', 'email', 'level', 'location', 'devices', 'models', 'key', 'status', '__v');
+ should(data[0]).have.property('_id');
+ should(data[0]).have.property('name', 'adminnew');
+ should(data[0]).have.property('email', 'adminnew@bosch.com');
+ should(data[0]).have.property('pass').not.eql('Abc123##');
+ should(data[0]).have.property('level', 'admin');
+ should(data[0]).have.property('location', 'Abt');
+ should(data[0]).have.property('devices', ['test']);
+ should(data[0].models[0].toString()).be.eql('120000000000000000000002');
+ should(data[0]).have.property('status', 'new');
+ done();
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {name: 'adminnew', email: 'adminnew@bosch.com', pass: 'Abc123##', location: 'Abt', devices: ['test']},
+ log: {
+ collection: 'users',
+ dataIgn: ['pass']
+ }
+ });
+ });
+ it('lets the admin change a user level', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user/janedoe',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {level: 'read'}
+ }).end(err => {
+ if (err) return done (err);
+ UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.property('level', 'read');
+ done();
+ });
+ });
+ });
+ it('does not change the level', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400, default: false,
+ req: {level: 'read'}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'Invalid body format', details: '"level" is not allowed'});
+ UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.property('level', 'write');
+ done();
+ });
+ });
+ });
+ it('lets the admin change accessible models', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user/janedoe',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {models: ['120000000000000000000001']}
+ }).end(err => {
+ if (err) return done (err);
+ UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0].models[0].toString()).be.eql('120000000000000000000001');
+ done();
+ });
+ });
+ });
+ it('does not change the models', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user',
+ auth: {basic: 'janedoe'},
+ httpStatus: 400, default: false,
+ req: {models: ['120000000000000000000001']}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'Invalid body format', details: '"models" is not allowed'});
+ UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.property('models', []);
+ done();
+ });
+ });
+ });
+ it('rejects a username already in use', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user',
+ auth: {basic: 'admin'},
+ httpStatus: 400, default: false,
+ req: {name: 'janedoe'}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'Username already taken'});
+ UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ done();
+ });
+ });
+ });
+ it('rejects a username which is in the special names', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400, default: false,
+ req: {email: 'j.doe@bosch.com', name: 'passreset', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: []},
+ res: {status: 'Username already taken'}
+ });
+ });
+ it('rejects invalid user details', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', location: 44, devices: ['Alpha II']},
+ res: {status: 'Invalid body format', details: '"location" must be a string'}
+ });
+ });
+ it('rejects an invalid email address', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {email: 'john.doe'},
+ res: {status: 'Invalid body format', details: '"email" must be a valid email'}
+ });
+ });
+ it('rejects an invalid password', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {pass: 'pass'},
+ res: {status: 'Invalid body format', details: '"pass" length must be at least 8 characters long'}
+ });
+ });
+ it('rejects requests from non-admins for another user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user/admin',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {}
+ });
+ });
+ it('rejects requests from a user API key', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user',
+ auth: {key: 'janedoe'},
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ it('rejects requests from an admin API key', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user/janedoe',
+ auth: {key: 'admin'},
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ it('returns 404 for an unknown user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user/unknown',
+ auth: {basic: 'admin'},
+ httpStatus: 404,
+ req: {}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user/janedoe',
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ });
- describe('DELETE /user/{name}', () => {
- it('sets own user details to deleted', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/user',
- auth: {basic: 'janedoe'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]).have.property('status', 'deleted');
- done();
- });
- });
- });
- it('sets other user details to deleted for admin', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/user/janedoe',
- auth: {basic: 'admin'},
- httpStatus: 200
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]).have.property('status', 'deleted');
- done();
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/user',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- log: {
- collection: 'users',
- dataAdd: {status: 'deleted'}
- }
- });
- });
- it('rejects requests from non-admins for another user', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/user/admin',
- auth: {basic: 'janedoe'},
- httpStatus: 403
- });
- });
- it('rejects requests from a user API key', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/user',
- auth: {key: 'janedoe'},
- httpStatus: 401
- });
- });
- it('rejects requests from an admin API key', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/user/janedoe',
- auth: {key: 'admin'},
- httpStatus: 401
- });
- });
- it('returns 404 for an unknown user', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/user/unknown',
- auth: {basic: 'admin'},
- httpStatus: 404
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'delete',
- url: '/user/janedoe',
- httpStatus: 401
- });
- });
- });
+ describe('DELETE /user/{name}', () => {
+ it('sets own user details to deleted', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/user',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.property('status', 'deleted');
+ done();
+ });
+ });
+ });
+ it('sets other user details to deleted for admin', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/user/janedoe',
+ auth: {basic: 'admin'},
+ httpStatus: 200
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.property('status', 'deleted');
+ done();
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/user',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ log: {
+ collection: 'users',
+ dataAdd: {status: 'deleted'}
+ }
+ });
+ });
+ it('rejects requests from non-admins for another user', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/user/admin',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403
+ });
+ });
+ it('rejects requests from a user API key', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/user',
+ auth: {key: 'janedoe'},
+ httpStatus: 401
+ });
+ });
+ it('rejects requests from an admin API key', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/user/janedoe',
+ auth: {key: 'admin'},
+ httpStatus: 401
+ });
+ });
+ it('returns 404 for an unknown user', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/user/unknown',
+ auth: {basic: 'admin'},
+ httpStatus: 404
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'delete',
+ url: '/user/janedoe',
+ httpStatus: 401
+ });
+ });
+ });
- describe('PUT /user/restore/{name}', () => {
- it('sets the status', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user/restore/customerold',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'OK'});
- UserModel.findOne({name: 'customerold'}).lean().exec((err, data: any) => {
- if (err) return done(err);
- should(data).have.property('status','new');
- done();
- });
- });
- });
- it('rejects an API key', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user/restore/customerold',
- auth: {key: 'admin'},
- httpStatus: 401,
- req: {}
- });
- });
- it('rejects a write user', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user/restore/customerold',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {}
- });
- });
- it('returns 404 for an unknown sample', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user/restore/xxx',
- auth: {basic: 'admin'},
- httpStatus: 404,
- req: {}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'put',
- url: '/user/restore/customerold',
- httpStatus: 401,
- req: {}
- });
- });
- });
+ describe('PUT /user/restore/{name}', () => {
+ it('sets the status', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user/restore/customerold',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'OK'});
+ UserModel.findOne({name: 'customerold'}).lean().exec((err, data: any) => {
+ if (err) return done(err);
+ should(data).have.property('status','new');
+ done();
+ });
+ });
+ });
+ it('rejects an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user/restore/customerold',
+ auth: {key: 'admin'},
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ it('rejects a write user', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user/restore/customerold',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {}
+ });
+ });
+ it('returns 404 for an unknown sample', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user/restore/xxx',
+ auth: {basic: 'admin'},
+ httpStatus: 404,
+ req: {}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'put',
+ url: '/user/restore/customerold',
+ httpStatus: 401,
+ req: {}
+ });
+ });
+ });
- describe('GET /user/key', () => {
- it('returns the right API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/user/key',
- auth: {basic: 'janedoe'},
- httpStatus: 200,
- res: {key: TestHelper.auth.janedoe.key}
- });
- });
- it('rejects requests from an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/user/key',
- auth: {key: 'janedoe'},
- httpStatus: 401
- });
- });
- it('rejects requests from an API key', done => {
- TestHelper.request(server, done, {
- method: 'get',
- url: '/user/key',
- httpStatus: 401
- });
- });
- });
+ describe('GET /user/key', () => {
+ it('returns the right API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/user/key',
+ auth: {basic: 'janedoe'},
+ httpStatus: 200,
+ res: {key: TestHelper.auth.janedoe.key}
+ });
+ });
+ it('rejects requests from an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/user/key',
+ auth: {key: 'janedoe'},
+ httpStatus: 401
+ });
+ });
+ it('rejects requests from an API key', done => {
+ TestHelper.request(server, done, {
+ method: 'get',
+ url: '/user/key',
+ httpStatus: 401
+ });
+ });
+ });
- describe('POST /user/new', () => {
- it('returns the added user data', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/new',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: ['120000000000000000000003']}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models');
- should(res.body).have.property('_id').be.type('string');
- should(res.body).have.property('email', 'john.doe@bosch.com');
- should(res.body).have.property('name', 'johndoe');
- should(res.body).have.property('level', 'read');
- should(res.body).have.property('location', 'Rng');
- should(res.body).have.property('devices', ['Alpha II']);
- should(res.body).have.property('models', ['120000000000000000000003']);
- done();
- });
- });
- it('stores the data', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/new',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: ['120000000000000000000002']}
- }).end(err => {
- if (err) return done (err);
- UserModel.find({name: 'johndoe'}).lean().exec( (err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- should(data[0]).have.only.keys('_id', 'name', 'pass', 'email', 'level', 'location', 'devices', 'models', 'key', 'status', '__v');
- should(data[0]).have.property('_id');
- should(data[0]).have.property('name', 'johndoe');
- should(data[0]).have.property('email', 'john.doe@bosch.com');
- should(data[0]).have.property('pass').not.eql('Abc123!#');
- should(data[0]).have.property('level', 'read');
- should(data[0]).have.property('location', 'Rng');
- should(data[0]).have.property('devices', ['Alpha II']);
- should(data[0].models.toString()).be.eql('120000000000000000000002');
- should(data[0]).have.property('status', 'new');
- done();
- });
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/new',
- auth: {basic: 'admin'},
- httpStatus: 200,
- req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: ['120000000000000000000002']},
- log: {
- collection: 'users',
- dataIgn: ['pass', 'key'],
- dataAdd: { status: 'new'}
- }
- });
- });
- it('rejects a username already in use', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/new',
- auth: {basic: 'admin'},
- httpStatus: 400, default: false,
- req: {email: 'j.doe@bosch.com', name: 'janedoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: []}
- }).end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql({status: 'Username already taken'});
- UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
- if (err) return done(err);
- should(data).have.lengthOf(1);
- done();
- });
- });
- });
- it('rejects a username which is in the special names', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/new',
- auth: {basic: 'admin'},
- httpStatus: 400, default: false,
- req: {email: 'j.doe@bosch.com', name: 'passreset', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: []},
- res: {status: 'Username already taken'}
- });
- });
- it('rejects invalid user details', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/new',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 44, devices: ['Alpha II']},
- res: {status: 'Invalid body format', details: '"location" must be a string'}
- });
- });
- it('rejects an invalid user level', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/new',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'xxx', location: 'Rng', devices: ['Alpha II']},
- res: {status: 'Invalid body format', details: '"level" must be one of [predict, read, write, dev, admin]'}
- });
- });
- it('rejects an invalid email address', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/new',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {email: 'john.doe', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II']},
- res: {status: 'Invalid body format', details: '"email" must be a valid email'}
- });
- });
- it('rejects an invalid password', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/new',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'pass', level: 'read', location: 'Rng', devices: ['Alpha II']},
- res: {status: 'Invalid body format', details: '"pass" length must be at least 8 characters long'}
- });
- });
- it('rejects an invalid model', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/new',
- auth: {basic: 'admin'},
- httpStatus: 400,
- req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: ['120000000000000000000001', '000000000000000000000001']},
- res: {status: 'Invalid model id'}
- });
- });
- it('rejects requests from non-admins', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/new',
- auth: {basic: 'janedoe'},
- httpStatus: 403,
- req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II']}
- });
- });
- it('rejects requests from an admin API key', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/new',
- auth: {key: 'admin'},
- httpStatus: 401,
- req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II']}
- });
- });
- it('rejects unauthorized requests', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/new',
- httpStatus: 401,
- req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II']}
- });
- });
- });
+ describe('POST /user/new', () => {
+ it('returns the added user data', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/new',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: ['120000000000000000000003']}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models');
+ should(res.body).have.property('_id').be.type('string');
+ should(res.body).have.property('email', 'john.doe@bosch.com');
+ should(res.body).have.property('name', 'johndoe');
+ should(res.body).have.property('level', 'read');
+ should(res.body).have.property('location', 'Rng');
+ should(res.body).have.property('devices', ['Alpha II']);
+ should(res.body).have.property('models', ['120000000000000000000003']);
+ done();
+ });
+ });
+ it('stores the data', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/new',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: ['120000000000000000000002']}
+ }).end(err => {
+ if (err) return done (err);
+ UserModel.find({name: 'johndoe'}).lean().exec( (err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ should(data[0]).have.only.keys('_id', 'name', 'pass', 'email', 'level', 'location', 'devices', 'models', 'key', 'status', '__v');
+ should(data[0]).have.property('_id');
+ should(data[0]).have.property('name', 'johndoe');
+ should(data[0]).have.property('email', 'john.doe@bosch.com');
+ should(data[0]).have.property('pass').not.eql('Abc123!#');
+ should(data[0]).have.property('level', 'read');
+ should(data[0]).have.property('location', 'Rng');
+ should(data[0]).have.property('devices', ['Alpha II']);
+ should(data[0].models.toString()).be.eql('120000000000000000000002');
+ should(data[0]).have.property('status', 'new');
+ done();
+ });
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/new',
+ auth: {basic: 'admin'},
+ httpStatus: 200,
+ req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: ['120000000000000000000002']},
+ log: {
+ collection: 'users',
+ dataIgn: ['pass', 'key'],
+ dataAdd: { status: 'new'}
+ }
+ });
+ });
+ it('rejects a username already in use', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400, default: false,
+ req: {email: 'j.doe@bosch.com', name: 'janedoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: []}
+ }).end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql({status: 'Username already taken'});
+ UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
+ if (err) return done(err);
+ should(data).have.lengthOf(1);
+ done();
+ });
+ });
+ });
+ it('rejects a username which is in the special names', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400, default: false,
+ req: {email: 'j.doe@bosch.com', name: 'passreset', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: []},
+ res: {status: 'Username already taken'}
+ });
+ });
+ it('rejects invalid user details', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 44, devices: ['Alpha II']},
+ res: {status: 'Invalid body format', details: '"location" must be a string'}
+ });
+ });
+ it('rejects an invalid user level', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'xxx', location: 'Rng', devices: ['Alpha II']},
+ res: {status: 'Invalid body format', details: '"level" must be one of [predict, read, write, dev, admin]'}
+ });
+ });
+ it('rejects an invalid email address', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {email: 'john.doe', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II']},
+ res: {status: 'Invalid body format', details: '"email" must be a valid email'}
+ });
+ });
+ it('rejects an invalid password', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'pass', level: 'read', location: 'Rng', devices: ['Alpha II']},
+ res: {status: 'Invalid body format', details: '"pass" length must be at least 8 characters long'}
+ });
+ });
+ it('rejects an invalid model', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/new',
+ auth: {basic: 'admin'},
+ httpStatus: 400,
+ req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: ['120000000000000000000001', '000000000000000000000001']},
+ res: {status: 'Invalid model id'}
+ });
+ });
+ it('rejects requests from non-admins', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/new',
+ auth: {basic: 'janedoe'},
+ httpStatus: 403,
+ req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II']}
+ });
+ });
+ it('rejects requests from an admin API key', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/new',
+ auth: {key: 'admin'},
+ httpStatus: 401,
+ req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II']}
+ });
+ });
+ it('rejects unauthorized requests', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/new',
+ httpStatus: 401,
+ req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II']}
+ });
+ });
+ });
- describe('POST /user/passreset', () => {
- it('returns the ok response', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/passreset',
- httpStatus: 200,
- req: {email: 'jane.doe@bosch.com', name: 'janedoe'},
- res: {status: 'OK'}
- });
- });
- it('creates a changelog', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/passreset',
- httpStatus: 200,
- req: {email: 'jane.doe@bosch.com', name: 'janedoe'},
- log: {
- collection: 'users',
- dataIgn: ['email', 'name', 'pass']
- }
- });
- });
- it('returns 404 for wrong username/email combo', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/passreset',
- httpStatus: 404,
- req: {email: 'jane.doe@bosch.com', name: 'admin'}
- });
- });
- it('returns 404 for unknown username', done => {
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/passreset',
- httpStatus: 404,
- req: {email: 'jane.doe@bosch.com', name: 'username'}
- });
- });
- it('changes the user password', done => {
- UserModel.find({name: 'janedoe'}).lean().exec( (err, data: any) => {
- if (err) return done(err);
- const oldpass = data[0].pass;
- TestHelper.request(server, done, {
- method: 'post',
- url: '/user/passreset',
- httpStatus: 200,
- req: {email: 'jane.doe@bosch.com', name: 'janedoe'}
- }).end((err, res) => {
- if (err) return done(err);
- should(res.body).be.eql({status: 'OK'});
- UserModel.find({name: 'janedoe'}).lean().exec( (err, data: any) => {
- if (err) return done(err);
- should(data[0].pass).not.eql(oldpass);
- done();
- });
- });
- });
- });
- });
-});
\ No newline at end of file
+ describe('POST /user/passreset', () => {
+ it('returns the ok response', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/passreset',
+ httpStatus: 200,
+ req: {email: 'jane.doe@bosch.com', name: 'janedoe'},
+ res: {status: 'OK'}
+ });
+ });
+ it('creates a changelog', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/passreset',
+ httpStatus: 200,
+ req: {email: 'jane.doe@bosch.com', name: 'janedoe'},
+ log: {
+ collection: 'users',
+ dataIgn: ['email', 'name', 'pass']
+ }
+ });
+ });
+ it('returns 404 for wrong username/email combo', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/passreset',
+ httpStatus: 404,
+ req: {email: 'jane.doe@bosch.com', name: 'admin'}
+ });
+ });
+ it('returns 404 for unknown username', done => {
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/passreset',
+ httpStatus: 404,
+ req: {email: 'jane.doe@bosch.com', name: 'username'}
+ });
+ });
+ it('changes the user password', done => {
+ UserModel.find({name: 'janedoe'}).lean().exec( (err, data: any) => {
+ if (err) return done(err);
+ const oldpass = data[0].pass;
+ TestHelper.request(server, done, {
+ method: 'post',
+ url: '/user/passreset',
+ httpStatus: 200,
+ req: {email: 'jane.doe@bosch.com', name: 'janedoe'}
+ }).end((err, res) => {
+ if (err) return done(err);
+ should(res.body).be.eql({status: 'OK'});
+ UserModel.find({name: 'janedoe'}).lean().exec( (err, data: any) => {
+ if (err) return done(err);
+ should(data[0].pass).not.eql(oldpass);
+ done();
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/src/routes/user.ts b/src/routes/user.ts
index 7f5e791..0238713 100644
--- a/src/routes/user.ts
+++ b/src/routes/user.ts
@@ -15,206 +15,206 @@ const router = express.Router();
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) => {
- // validate all and filter null values from validation errors
- res.json(_.compact(data.map(e => UserValidate.output(e, 'admin'))));
- });
+ UserModel.find({}).lean().exec( (err, data:any) => {
+ // validate all and filter null values from validation errors
+ 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.
// See https://forbeslindesay.github.io/express-route-tester/ for the generated regex
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);
- if (!username) return;
- UserModel.findOne({name: username}).lean().exec( (err, data:any) => {
- if (err) return next(err);
- if (data) {
- res.json(UserValidate.output(data)); // validate all and filter null values from validation errors
- }
- else {
- res.status(404).json({status: 'Not found'});
- }
- });
+ const username = getUsername(req, res);
+ if (!username) return;
+ UserModel.findOne({name: username}).lean().exec( (err, data:any) => {
+ if (err) return next(err);
+ if (data) {
+ res.json(UserValidate.output(data)); // validate all and filter null values from validation errors
+ }
+ else {
+ res.status(404).json({status: 'Not found'});
+ }
+ });
});
// 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) => {
- 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);
- if (!username) return;
+ const username = getUsername(req, res);
+ if (!username) return;
- const {error, value: user} = UserValidate.input(req.body, 'change' +
- (req.authDetails.level === 'admin'? 'admin' : ''));
- if (error) return res400(error, res);
+ const {error, value: user} = UserValidate.input(req.body, 'change' +
+ (req.authDetails.level === 'admin'? 'admin' : ''));
+ if (error) return res400(error, res);
- if (user.hasOwnProperty('pass')) {
- user.pass = bcrypt.hashSync(user.pass, 10);
- }
+ if (user.hasOwnProperty('pass')) {
+ user.pass = bcrypt.hashSync(user.pass, 10);
+ }
- // check that user does not already exist if new name was specified
- if (user.hasOwnProperty('name') && user.name !== username) {
- if (!await usernameCheck(user.name, res, next)) return;
- }
+ // check that user does not already exist if new name was specified
+ if (user.hasOwnProperty('name') && user.name !== username) {
+ if (!await usernameCheck(user.name, res, next)) return;
+ }
- if (user.hasOwnProperty('models')) {
- if (!await modelsCheck(user.models, res, next)) return;
- }
+ if (user.hasOwnProperty('models')) {
+ if (!await modelsCheck(user.models, res, next)) return;
+ }
- // get current mail address to compare to given address
- const oldUserData = await UserModel.findOne({name: username}).lean().exec().catch(err => next(err));
+ // get current mail address to compare to given address
+ 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) => {
- if (err) return next(err);
- if (data) {
- 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',
- 'Hi,
Your email address of your DeFinMa account was changed to ' + data.mail +
- '
If you actually did this, just delete this email.' +
- '
If you did not change your email, someone might be messing around with your account, ' +
- 'so talk to the sysadmin quickly!
Have a nice day.' +
- '
The DeFinMa team');
- }
- res.json(UserValidate.output(data));
- }
- else {
- res.status(404).json({status: 'Not found'});
- }
- });
+ await UserModel.findOneAndUpdate({name: username}, user, {new: true}).log(req).lean().exec( (err, data:any) => {
+ if (err) return next(err);
+ if (data) {
+ 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',
+ 'Hi,
Your email address of your DeFinMa account was changed to ' + data.mail +
+ '
If you actually did this, just delete this email.' +
+ '
If you did not change your email, someone might be messing around with your account, ' +
+ 'so talk to the sysadmin quickly!
Have a nice day.' +
+ '
The DeFinMa team');
+ }
+ res.json(UserValidate.output(data));
+ }
+ else {
+ res.status(404).json({status: 'Not found'});
+ }
+ });
});
// 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
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);
- if (!username) return;
+ const username = getUsername(req, res);
+ if (!username) return;
- UserModel.findOneAndUpdate({name: username}, {status: globals.status.del}).log(req).lean().exec( (err, data:any) => {
- if (err) return next(err);
- if (data) {
- res.json({status: 'OK'})
- }
- else {
- res.status(404).json({status: 'Not found'});
- }
- });
+ UserModel.findOneAndUpdate({name: username}, {status: globals.status.del}).log(req).lean().exec( (err, data:any) => {
+ if (err) return next(err);
+ if (data) {
+ res.json({status: 'OK'})
+ }
+ else {
+ res.status(404).json({status: 'Not found'});
+ }
+ });
});
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})
- .log(req).lean().exec((err, data) => {
- if (err) return next(err);
+ UserModel.findOneAndUpdate({name: req.params.username}, {status: globals.status.new})
+ .log(req).lean().exec((err, data) => {
+ if (err) return next(err);
- if (!data) {
- return res.status(404).json({status: 'Not found'});
- }
- res.json({status: 'OK'});
- });
+ if (!data) {
+ return res.status(404).json({status: 'Not found'});
+ }
+ res.json({status: 'OK'});
+ });
});
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) => {
- if (err) return next(err);
- res.json({key: data.key});
- });
+ UserModel.findOne({name: req.authDetails.username}).lean().exec( (err, data:any) => {
+ if (err) return next(err);
+ res.json({key: data.key});
+ });
});
router.post('/user/new', async (req, res, next) => {
- if (!req.auth(res, ['admin'], 'basic')) return;
+ if (!req.auth(res, ['admin'], 'basic')) return;
- // validate input
- const {error, value: user} = UserValidate.input(req.body, 'new');
- if (error) return res400(error, res);
+ // validate input
+ const {error, value: user} = UserValidate.input(req.body, 'new');
+ if (error) return res400(error, res);
- // check that user does not already exist
- if (!await usernameCheck(user.name, res, next)) return;
- if (!await modelsCheck(user.models, res, next)) return;
+ // check that user does not already exist
+ if (!await usernameCheck(user.name, res, next)) return;
+ if (!await modelsCheck(user.models, res, next)) return;
- user.key = mongoose.Types.ObjectId(); // use object id as unique API key
- user.status = globals.status.new;
- bcrypt.hash(user.pass, 10, (err, hash) => { // password hashing
- user.pass = hash;
- new UserModel(user).save((err, data) => { // store user
- if (err) return next(err);
- db.log(req, 'users', {_id: data._id}, data.toObject());
- res.json(UserValidate.output(data.toObject()));
- });
- });
+ user.key = mongoose.Types.ObjectId(); // use object id as unique API key
+ user.status = globals.status.new;
+ bcrypt.hash(user.pass, 10, (err, hash) => { // password hashing
+ user.pass = hash;
+ new UserModel(user).save((err, data) => { // store user
+ if (err) return next(err);
+ db.log(req, 'users', {_id: data._id}, data.toObject());
+ res.json(UserValidate.output(data.toObject()));
+ });
+ });
});
router.post('/user/passreset', (req, res, next) => {
- // check if user/email combo exists
- UserModel.find({name: req.body.name, email: req.body.email}).lean().exec( (err, data: any) => {
- if (err) return next(err);
- if (data.length === 1) { // it exists
- const newPass = Math.random().toString(36).substring(2); // generate temporary password
- bcrypt.hash(newPass, 10, (err, hash) => { // password hashing
- if (err) return next(err);
+ // check if user/email combo exists
+ UserModel.find({name: req.body.name, email: req.body.email}).lean().exec( (err, data: any) => {
+ if (err) return next(err);
+ if (data.length === 1) { // it exists
+ const newPass = Math.random().toString(36).substring(2); // generate temporary password
+ bcrypt.hash(newPass, 10, (err, hash) => { // password hashing
+ if (err) return next(err);
- UserModel.findByIdAndUpdate(data[0]._id, {pass: hash}).log(req).exec(err => { // write new password
- if (err) return next(err);
+ UserModel.findByIdAndUpdate(data[0]._id, {pass: hash}).log(req).exec(err => { // write new password
+ if (err) return next(err);
- // send email
- Mail.send(data[0].email, 'Your new password for the DeFinMa database',
- 'Hi,
You requested to reset your password.
Your new password is:
' + newPass + '' +
- '
If you did not request a password reset, talk to the sysadmin quickly!
Have a nice day.' +
- '
The DeFinMa team', err => {
- if (err) return next(err);
- res.json({status: 'OK'});
- });
- });
- });
- }
- else {
- res.status(404).json({status: 'Not found'});
- }
- });
+ // send email
+ Mail.send(data[0].email, 'Your new password for the DeFinMa database',
+ 'Hi,
You requested to reset your password.
Your new password is:
' + newPass + '' +
+ '
If you did not request a password reset, talk to the sysadmin quickly!
Have a nice day.' +
+ '
The DeFinMa team', err => {
+ if (err) return next(err);
+ res.json({status: 'OK'});
+ });
+ });
+ });
+ }
+ else {
+ res.status(404).json({status: 'Not found'});
+ }
+ });
});
module.exports = router;
function getUsername (req, res) { // returns username or false if action is not allowed
- req.params.username = req.params[0]; // because of path regex
- if (req.params.username !== undefined) { // different username than request user
- if (!req.auth(res, ['admin'], 'basic')) return false;
- return req.params.username;
- }
- else {
- return req.authDetails.username;
- }
+ req.params.username = req.params[0]; // because of path regex
+if (req.params.username !== undefined) { // different username than request user
+ if (!req.auth(res, ['admin'], 'basic')) return false;
+ return req.params.username;
+}
+else {
+ return req.authDetails.username;
+}
}
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;
- if (userData instanceof Error) return false;
- if (userData || UserValidate.isSpecialName(name)) {
- res.status(400).json({status: 'Username already taken'});
- return false;
- }
- return true;
+ const userData = await UserModel.findOne({name: name}).lean().exec().catch(err => next(err)) as any;
+if (userData instanceof Error) return false;
+if (userData || UserValidate.isSpecialName(name)) {
+ res.status(400).json({status: 'Username already taken'});
+ return false;
+}
+return true;
}
async function modelsCheck (models, res, next) { // check if model ids exist, returns false on error
- let result = true;
- for (let i in models) {
- const model = await ModelModel.findOne({'models._id': mongoose.Types.ObjectId(models[i])})
- .lean().exec().catch(err => next(err)) as any;
- if(!model) {
- res.status(400).json({status: 'Invalid model id'});
- result = false;
- break;
- }
- }
- return result;
-}
\ No newline at end of file
+ let result = true;
+for (let i in models) {
+ const model = await ModelModel.findOne({'models._id': mongoose.Types.ObjectId(models[i])})
+ .lean().exec().catch(err => next(err)) as any;
+ if(!model) {
+ res.status(400).json({status: 'Invalid model id'});
+ result = false;
+ break;
+ }
+}
+return result;
+}
diff --git a/src/routes/validate/help.ts b/src/routes/validate/help.ts
index 6dcf64a..cb97d81 100644
--- a/src/routes/validate/help.ts
+++ b/src/routes/validate/help.ts
@@ -2,33 +2,33 @@ import Joi from 'joi';
import globals from '../../globals';
export default class HelpValidate {
- private static help = {
- text: Joi.string()
- .allow('')
- .max(8192),
+ private static help = {
+ text: Joi.string()
+ .allow('')
+ .max(8192),
- level: Joi.string()
- .valid('none', ...Object.values(globals.levels))
- }
+ level: Joi.string()
+ .valid('none', ...Object.values(globals.levels))
+ }
- static input (data) {
- return Joi.object({
- text: this.help.text.required(),
- level: this.help.level.required()
- }).validate(data);
- }
+ static input (data) {
+ return Joi.object({
+ text: this.help.text.required(),
+ level: this.help.level.required()
+ }).validate(data);
+ }
- static output (data) {
- const {value, error} = Joi.object({
- text: this.help.text,
- level: this.help.level
- }).validate(data, {stripUnknown: true});
- return error !== undefined? null : value;
- }
+ static output (data) {
+ const {value, error} = Joi.object({
+ text: this.help.text,
+ level: this.help.level
+ }).validate(data, {stripUnknown: true});
+ return error !== undefined? null : value;
+ }
- static params(data) {
- return Joi.object({
- key: Joi.string().min(1).max(128)
- }).validate(data);
- }
-}
\ No newline at end of file
+ static params(data) {
+ return Joi.object({
+ key: Joi.string().min(1).max(128)
+ }).validate(data);
+ }
+}
diff --git a/src/routes/validate/id.ts b/src/routes/validate/id.ts
index e0d8362..9e85b58 100644
--- a/src/routes/validate/id.ts
+++ b/src/routes/validate/id.ts
@@ -1,33 +1,33 @@
import Joi from 'joi';
export default class IdValidate {
- private static id = Joi.string()
- .pattern(new RegExp('[0-9a-f]{24}'))
- .length(24)
- .messages({'string.pattern.base': 'Invalid object id'});
+ private static id = Joi.string()
+ .pattern(new RegExp('[0-9a-f]{24}'))
+ .length(24)
+ .messages({'string.pattern.base': 'Invalid object id'});
- static get () { // return joi validation
- return this.id;
- }
+ static get () { // return joi validation
+ return this.id;
+ }
- static valid (id) { // validate id
- return this.id.validate(id).error === undefined;
- }
+ static valid (id) { // validate id
+ return this.id.validate(id).error === undefined;
+ }
- static parameter () { // :id url parameter
- return ':id([0-9a-f]{24})';
- }
+ static parameter () { // :id url parameter
+ return ':id([0-9a-f]{24})';
+ }
- static stringify (data) { // convert all ObjectID objects to plain strings
- Object.keys(data).forEach(key => {
- // stringify id
- if (data[key] !== null && data[key].hasOwnProperty('_bsontype') && data[key]._bsontype === 'ObjectID') {
- data[key] = data[key].toString();
- }
- else if (typeof data[key] === 'object' && data[key] !== null) { // deeper into recursion
- data[key] = this.stringify(data[key]);
- }
- });
- return data;
- }
-}
\ No newline at end of file
+ static stringify (data) { // convert all ObjectID objects to plain strings
+ Object.keys(data).forEach(key => {
+ // stringify id
+ if (data[key] !== null && data[key].hasOwnProperty('_bsontype') && data[key]._bsontype === 'ObjectID') {
+ data[key] = data[key].toString();
+ }
+ else if (typeof data[key] === 'object' && data[key] !== null) { // deeper into recursion
+ data[key] = this.stringify(data[key]);
+ }
+ });
+ return data;
+ }
+}
diff --git a/src/routes/validate/material.ts b/src/routes/validate/material.ts
index cf5782d..7bbb698 100644
--- a/src/routes/validate/material.ts
+++ b/src/routes/validate/material.ts
@@ -4,99 +4,99 @@ import IdValidate from './id';
import globals from '../../globals';
export default class MaterialValidate { // validate input for material
- private static material = {
- name: Joi.string()
- .max(128),
+ private static material = {
+ name: Joi.string()
+ .max(128),
- supplier: Joi.string()
- .max(128),
+ supplier: Joi.string()
+ .max(128),
- group: Joi.string()
- .max(128),
+ group: Joi.string()
+ .max(128),
- properties: Joi.object(),
+ properties: Joi.object(),
- numbers: Joi.array()
- .items(
- Joi.string()
- .max(64)
- ),
+ numbers: Joi.array()
+ .items(
+ Joi.string()
+ .max(64)
+ ),
- status: Joi.string()
- .valid(...Object.values(globals.status))
- };
+ status: Joi.string()
+ .valid(...Object.values(globals.status))
+ };
- static input (data, param) { // validate input, set param to 'new' to make all attributes required
- if (param === 'new') {
- return Joi.object({
- name: this.material.name.required(),
- supplier: this.material.supplier.required(),
- group: this.material.group.required(),
- properties: this.material.properties.required(),
- numbers: this.material.numbers.required()
- }).validate(data);
- }
- else if (param === 'change') {
- return Joi.object({
- name: this.material.name,
- supplier: this.material.supplier,
- group: this.material.group,
- properties: this.material.properties,
- numbers: this.material.numbers
- }).validate(data);
- }
- else {
- return{error: 'No parameter specified!', value: {}};
- }
- }
+ static input (data, param) { // validate input, set param to 'new' to make all attributes required
+ if (param === 'new') {
+ return Joi.object({
+ name: this.material.name.required(),
+ supplier: this.material.supplier.required(),
+ group: this.material.group.required(),
+ properties: this.material.properties.required(),
+ numbers: this.material.numbers.required()
+ }).validate(data);
+ }
+ else if (param === 'change') {
+ return Joi.object({
+ name: this.material.name,
+ supplier: this.material.supplier,
+ group: this.material.group,
+ properties: this.material.properties,
+ numbers: this.material.numbers
+ }).validate(data);
+ }
+ else {
+ return{error: 'No parameter specified!', value: {}};
+ }
+ }
- static output (data, status = false) { // validate output and strip unwanted properties, returns null if not valid
- data = IdValidate.stringify(data);
- data.group = data.group_id.name;
- data.supplier = data.supplier_id.name;
- const validate: any = {
- _id: IdValidate.get(),
- name: this.material.name,
- supplier: this.material.supplier,
- group: this.material.group,
- properties: this.material.properties,
- numbers: this.material.numbers
- };
- if (status) {
- validate.status = this.material.status;
- }
- const {value, error} = Joi.object(validate).validate(data, {stripUnknown: true});
- return error !== undefined? null : value;
- }
+ static output (data, status = false) { // validate output and strip unwanted properties, returns null if not valid
+ data = IdValidate.stringify(data);
+ data.group = data.group_id.name;
+ data.supplier = data.supplier_id.name;
+ const validate: any = {
+ _id: IdValidate.get(),
+ name: this.material.name,
+ supplier: this.material.supplier,
+ group: this.material.group,
+ properties: this.material.properties,
+ numbers: this.material.numbers
+ };
+ if (status) {
+ validate.status = this.material.status;
+ }
+ const {value, error} = Joi.object(validate).validate(data, {stripUnknown: true});
+ return error !== undefined? null : value;
+ }
- 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});
- return error !== undefined? null : value;
- }
+ 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});
+ return error !== undefined? null : value;
+ }
- 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});
- return error !== undefined? null : value;
- }
+ 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});
+ return error !== undefined? null : value;
+ }
- static outputV() { // return output validator
- return Joi.object({
- _id: IdValidate.get(),
- name: this.material.name,
- supplier: this.material.supplier,
- group: this.material.group,
- properties: this.material.properties,
- numbers: this.material.numbers
- });
- }
+ static outputV() { // return output validator
+ return Joi.object({
+ _id: IdValidate.get(),
+ name: this.material.name,
+ supplier: this.material.supplier,
+ group: this.material.group,
+ properties: this.material.properties,
+ numbers: this.material.numbers
+ });
+ }
- static query (data, dev = false) {
- const acceptedStatuses = [globals.status.val, globals.status.new];
- if (dev) { // dev and admin can also access deleted samples
- acceptedStatuses.push(globals.status.del)
- }
- return Joi.object({
- status: Joi.array().items(Joi.string().valid(...acceptedStatuses)).default([globals.status.val])
- }).validate(data);
- }
-}
\ No newline at end of file
+ static query (data, dev = false) {
+ const acceptedStatuses = [globals.status.val, globals.status.new];
+ if (dev) { // dev and admin can also access deleted samples
+ acceptedStatuses.push(globals.status.del)
+ }
+ return Joi.object({
+ status: Joi.array().items(Joi.string().valid(...acceptedStatuses)).default([globals.status.val])
+ }).validate(data);
+ }
+}
diff --git a/src/routes/validate/measurement.ts b/src/routes/validate/measurement.ts
index 2c31000..f5a8f60 100644
--- a/src/routes/validate/measurement.ts
+++ b/src/routes/validate/measurement.ts
@@ -4,64 +4,64 @@ import IdValidate from './id';
import globals from '../../globals';
export default class MeasurementValidate {
- private static measurement = {
- values: Joi.object()
- .pattern(/.*/, Joi.alternatives()
- .try(
- Joi.string().max(128),
- Joi.number(),
- Joi.boolean(),
- Joi.array().items(Joi.array().items(Joi.number())), // for spectra
- Joi.array()
- )
- .allow(null)
- )
- };
+ private static measurement = {
+ values: Joi.object()
+ .pattern(/.*/, Joi.alternatives()
+.try(
+ Joi.string().max(128),
+ Joi.number(),
+ Joi.boolean(),
+ Joi.array().items(Joi.array().items(Joi.number())), // for spectra
+ Joi.array()
+)
+.allow(null)
+ )
+ };
- static input (data, param) { // validate input, set param to 'new' to make all attributes required
- if (param === 'new') {
- return Joi.object({
- sample_id: IdValidate.get().required(),
- values: this.measurement.values.required(),
- measurement_template: IdValidate.get().required()
- }).validate(data);
- }
- else if (param === 'change') {
- return Joi.object({
- values: this.measurement.values
- }).validate(data);
- }
- else {
- return{error: 'No parameter specified!', value: {}};
- }
- }
+ static input (data, param) { // validate input, set param to 'new' to make all attributes required
+ if (param === 'new') {
+ return Joi.object({
+ sample_id: IdValidate.get().required(),
+ values: this.measurement.values.required(),
+ measurement_template: IdValidate.get().required()
+ }).validate(data);
+ }
+ else if (param === 'change') {
+ return Joi.object({
+ values: this.measurement.values
+ }).validate(data);
+ }
+ else {
+ return{error: 'No parameter specified!', value: {}};
+ }
+ }
- // validate output and strip unwanted properties, returns null if not valid
- static output (data, req, status = false) {
- data = IdValidate.stringify(data);
- // spectral data not allowed for read/write users
- if (['dev', 'admin'].indexOf(req.authDetails.level) < 0 && data.values[globals.spectrum.dpt]) {
- delete data.values[globals.spectrum.dpt];
- }
- const validation: any = {
- _id: IdValidate.get(),
- sample_id: IdValidate.get(),
- values: this.measurement.values,
- measurement_template: IdValidate.get()
- };
- if (status) {
- validation.status = Joi.string().valid(...Object.values(globals.status));
- }
- const {value, error} = Joi.object(validation).validate(data, {stripUnknown: true});
- return error !== undefined? null : value;
- }
+ // validate output and strip unwanted properties, returns null if not valid
+ static output (data, req, status = false) {
+ data = IdValidate.stringify(data);
+ // spectral data not allowed for read/write users
+ if (['dev', 'admin'].indexOf(req.authDetails.level) < 0 && data.values[globals.spectrum.dpt]) {
+ delete data.values[globals.spectrum.dpt];
+ }
+ const validation: any = {
+ _id: IdValidate.get(),
+ sample_id: IdValidate.get(),
+ values: this.measurement.values,
+ measurement_template: IdValidate.get()
+ };
+ if (status) {
+ validation.status = Joi.string().valid(...Object.values(globals.status));
+ }
+ const {value, error} = Joi.object(validation).validate(data, {stripUnknown: true});
+ return error !== undefined? null : value;
+ }
- static outputV() { // return output validator
- return Joi.object({
- _id: IdValidate.get(),
- sample_id: IdValidate.get(),
- values: this.measurement.values,
- measurement_template: IdValidate.get()
- });
- }
-}
\ No newline at end of file
+ static outputV() { // return output validator
+ return Joi.object({
+ _id: IdValidate.get(),
+ sample_id: IdValidate.get(),
+ values: this.measurement.values,
+ measurement_template: IdValidate.get()
+ });
+ }
+}
diff --git a/src/routes/validate/model.ts b/src/routes/validate/model.ts
index 971cfd3..7700638 100644
--- a/src/routes/validate/model.ts
+++ b/src/routes/validate/model.ts
@@ -3,40 +3,40 @@ import IdValidate from './id';
export default class ModelValidate { // validate input for model
- private static model = {
- group: Joi.string()
- .disallow('file')
- .max(128),
+ private static model = {
+ group: Joi.string()
+ .disallow('file')
+ .max(128),
- model: Joi.object({
- name: Joi.string()
- .max(128)
- .required(),
+ model: Joi.object({
+ name: Joi.string()
+ .max(128)
+ .required(),
- url: Joi.string()
- .uri()
- .max(512)
- .required()
- })
- };
+ url: Joi.string()
+ .uri()
+ .max(512)
+ .required()
+ })
+ };
- static input (data) { // validate input
- return this.model.model.required().validate(data);
- }
+ static input (data) { // validate input
+ return this.model.model.required().validate(data);
+ }
- static output (data) { // validate output and strip unwanted properties, returns null if not valid
- data = IdValidate.stringify(data);
- const {value, error} = Joi.object({
- group: this.model.group,
- models: Joi.array().items(this.model.model.append({_id: IdValidate.get()}))
- }).validate(data, {stripUnknown: true});
- return error !== undefined? null : value;
- }
+ static output (data) { // validate output and strip unwanted properties, returns null if not valid
+ data = IdValidate.stringify(data);
+ const {value, error} = Joi.object({
+ group: this.model.group,
+ models: Joi.array().items(this.model.model.append({_id: IdValidate.get()}))
+ }).validate(data, {stripUnknown: true});
+ return error !== undefined? null : value;
+ }
- static fileOutput (data) {
- return {
- name: data.name,
- size: data.data.length
- }
- }
-}
\ No newline at end of file
+ static fileOutput (data) {
+ return {
+ name: data.name,
+ size: data.data.length
+ }
+ }
+}
diff --git a/src/routes/validate/note_field.ts b/src/routes/validate/note_field.ts
index 5185443..ead0a65 100644
--- a/src/routes/validate/note_field.ts
+++ b/src/routes/validate/note_field.ts
@@ -1,18 +1,18 @@
import Joi from 'joi';
export default class NoteFieldValidate {
- private static note_field = {
- name: Joi.string()
- .max(128),
+ private static note_field = {
+ name: Joi.string()
+ .max(128),
- qty: Joi.number()
- };
+ qty: Joi.number()
+ };
- static output (data) { // validate output and strip unwanted properties, returns null if not valid
- const {value, error} = Joi.object({
- name: this.note_field.name,
- qty: this.note_field.qty
- }).validate(data, {stripUnknown: true});
- return error !== undefined? null : value;
- }
-}
\ No newline at end of file
+ static output (data) { // validate output and strip unwanted properties, returns null if not valid
+ const {value, error} = Joi.object({
+ name: this.note_field.name,
+ qty: this.note_field.qty
+ }).validate(data, {stripUnknown: true});
+ return error !== undefined? null : value;
+ }
+}
diff --git a/src/routes/validate/parameters.ts b/src/routes/validate/parameters.ts
index a54804b..6c5bf56 100644
--- a/src/routes/validate/parameters.ts
+++ b/src/routes/validate/parameters.ts
@@ -1,42 +1,42 @@
import Joi from 'joi';
export default class ParametersValidate {
- // data to validate, parameters from template, param: 'new', 'change', 'null'(null values are allowed)
- static input (data, parameters, param) {
- let joiObject = {};
- parameters.forEach(parameter => {
- switch (parameter.range.type) {
- case 'number': joiObject[parameter.name] = Joi.number();
- break;
- case 'boolean': joiObject[parameter.name] = Joi.boolean();
- break;
- case 'array': joiObject[parameter.name] = Joi.array();
- break;
- case 'string': joiObject[parameter.name] = Joi.string().max(128);
- break; // min or max implicitly define the value to be a number
- default: if (parameter.range.hasOwnProperty('min') || parameter.range.hasOwnProperty('max')) {
- joiObject[parameter.name] = Joi.number();
- }
- else {
- joiObject[parameter.name] = Joi.string().max(128);
- }
- }
- if (parameter.range.hasOwnProperty('min')) {
- joiObject[parameter.name] = joiObject[parameter.name].min(parameter.range.min)
- }
- if (parameter.range.hasOwnProperty('max')) {
- joiObject[parameter.name] = joiObject[parameter.name].max(parameter.range.max)
- }
- if (parameter.range.hasOwnProperty('values')) {
- joiObject[parameter.name] = joiObject[parameter.name].valid(...parameter.range.values);
- }
- if (parameter.range.hasOwnProperty('required') && parameter.range.required) {
- joiObject[parameter.name] = joiObject[parameter.name].required();
- }
- if (param === 'null') {
- joiObject[parameter.name] = joiObject[parameter.name].allow(null)
- }
- });
- return Joi.object(joiObject).validate(data);
- }
-}
\ No newline at end of file
+ // data to validate, parameters from template, param: 'new', 'change', 'null'(null values are allowed)
+ static input (data, parameters, param) {
+ let joiObject = {};
+ parameters.forEach(parameter => {
+ switch (parameter.range.type) {
+ case 'number': joiObject[parameter.name] = Joi.number();
+ break;
+ case 'boolean': joiObject[parameter.name] = Joi.boolean();
+ break;
+ case 'array': joiObject[parameter.name] = Joi.array();
+ break;
+ case 'string': joiObject[parameter.name] = Joi.string().max(128);
+ break; // min or max implicitly define the value to be a number
+ default: if (parameter.range.hasOwnProperty('min') || parameter.range.hasOwnProperty('max')) {
+ joiObject[parameter.name] = Joi.number();
+ }
+ else {
+ joiObject[parameter.name] = Joi.string().max(128);
+ }
+ }
+ if (parameter.range.hasOwnProperty('min')) {
+ joiObject[parameter.name] = joiObject[parameter.name].min(parameter.range.min)
+ }
+ if (parameter.range.hasOwnProperty('max')) {
+ joiObject[parameter.name] = joiObject[parameter.name].max(parameter.range.max)
+ }
+ if (parameter.range.hasOwnProperty('values')) {
+ joiObject[parameter.name] = joiObject[parameter.name].valid(...parameter.range.values);
+ }
+ if (parameter.range.hasOwnProperty('required') && parameter.range.required) {
+ joiObject[parameter.name] = joiObject[parameter.name].required();
+ }
+ if (param === 'null') {
+ joiObject[parameter.name] = joiObject[parameter.name].allow(null)
+ }
+ });
+ return Joi.object(joiObject).validate(data);
+ }
+}
diff --git a/src/routes/validate/res400.ts b/src/routes/validate/res400.ts
index e4595c8..d5a2ba4 100644
--- a/src/routes/validate/res400.ts
+++ b/src/routes/validate/res400.ts
@@ -1,5 +1,5 @@
// respond with 400 and include error details from the joi validation
export default function res400 (error, res) {
- res.status(400).json({status: 'Invalid body format', details: error.details[0].message});
-}
\ No newline at end of file
+ res.status(400).json({status: 'Invalid body format', details: error.details[0].message});
+}
diff --git a/src/routes/validate/root.ts b/src/routes/validate/root.ts
index a6dfe33..144814e 100644
--- a/src/routes/validate/root.ts
+++ b/src/routes/validate/root.ts
@@ -2,50 +2,50 @@ import Joi from 'joi';
import IdValidate from './id';
export default class RootValidate { // validate input for root methods
- private static changelog = {
- timestamp: Joi.date()
- .iso()
- .min('1970-01-01T00:00:00.000Z'),
+ private static changelog = {
+ timestamp: Joi.date()
+ .iso()
+ .min('1970-01-01T00:00:00.000Z'),
- page: Joi.number()
- .integer()
- .min(0)
- .default(0),
+ page: Joi.number()
+ .integer()
+ .min(0)
+ .default(0),
- pagesize: Joi.number()
- .integer()
- .min(0)
- .default(25),
+ pagesize: Joi.number()
+ .integer()
+ .min(0)
+ .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) {
- return Joi.object({
- id: IdValidate.get(),
- page: this.changelog.page,
- pagesize: this.changelog.pagesize
- }).validate(data);
- }
+ static changelogParams (data) {
+ return Joi.object({
+ id: IdValidate.get(),
+ page: this.changelog.page,
+ pagesize: this.changelog.pagesize
+ }).validate(data);
+ }
- static changelogOutput (data) {
- data.date = data._id.getTimestamp();
- data.collection = data.collection_name;
- data = IdValidate.stringify(data);
- const {value, error} = Joi.object({
- _id: IdValidate.get(),
- date: this.changelog.timestamp,
- action: this.changelog.action,
- collection: this.changelog.collection,
- conditions: this.changelog.conditions,
- data: this.changelog.data,
- }).validate(data, {stripUnknown: true});
- return error !== undefined? null : value;
- }
-}
\ No newline at end of file
+ static changelogOutput (data) {
+ data.date = data._id.getTimestamp();
+ data.collection = data.collection_name;
+ data = IdValidate.stringify(data);
+ const {value, error} = Joi.object({
+ _id: IdValidate.get(),
+ date: this.changelog.timestamp,
+ action: this.changelog.action,
+ collection: this.changelog.collection,
+ conditions: this.changelog.conditions,
+ data: this.changelog.data,
+ }).validate(data, {stripUnknown: true});
+ return error !== undefined? null : value;
+ }
+}
diff --git a/src/routes/validate/sample.ts b/src/routes/validate/sample.ts
index efaedf1..6f6b0e2 100644
--- a/src/routes/validate/sample.ts
+++ b/src/routes/validate/sample.ts
@@ -8,273 +8,273 @@ import globals from '../../globals';
export default class SampleValidate {
- private static sample = {
- number: Joi.string()
- .max(128),
+ private static sample = {
+ number: Joi.string()
+ .max(128),
- color: Joi.string()
- .max(128)
- .allow(''),
+ color: Joi.string()
+ .max(128)
+ .allow(''),
- type: Joi.string()
- .valid('as-delivered/raw', 'processed'),
+ type: Joi.string()
+ .valid('as-delivered/raw', 'processed'),
- batch: Joi.string()
- .max(128)
- .allow(''),
+ batch: Joi.string()
+ .max(128)
+ .allow(''),
- condition: Joi.object(),
+ condition: Joi.object(),
- notes: Joi.object({
- comment: Joi.string()
- .max(512)
- .allow('')
- .allow(null),
+ notes: Joi.object({
+ comment: Joi.string()
+ .max(512)
+ .allow('')
+ .allow(null),
- sample_references: Joi.array()
- .items(Joi.object({
- sample_id: IdValidate.get(),
+ sample_references: Joi.array()
+ .items(Joi.object({
+ sample_id: IdValidate.get(),
- relation: Joi.string()
- .max(128)
- })),
+ relation: Joi.string()
+ .max(128)
+ })),
- custom_fields: Joi.object()
- .pattern(/.*/, Joi.alternatives()
- .try(
- Joi.string().max(128),
- Joi.number(),
- Joi.boolean(),
- Joi.date()
- )
- )
- }),
+ custom_fields: Joi.object()
+ .pattern(/.*/, Joi.alternatives()
+.try(
+ Joi.string().max(128),
+ Joi.number(),
+ Joi.boolean(),
+ Joi.date()
+)
+ )
+ }),
- added: Joi.date()
- .iso()
- .min('1970-01-01T00:00:00.000Z'),
+ added: Joi.date()
+ .iso()
+ .min('1970-01-01T00:00:00.000Z'),
- status: Joi.string()
- .valid(...Object.values(globals.status))
- };
+ status: Joi.string()
+ .valid(...Object.values(globals.status))
+ };
- static readonly sampleKeys = [ // keys which can be found in the sample directly
- '_id',
- 'color',
- 'number',
- 'type',
- 'batch',
- 'added',
- 'condition',
- 'material_id',
- 'note_id',
- 'user_id'
- ];
+ static readonly sampleKeys = [ // keys which can be found in the sample directly
+ '_id',
+ 'color',
+ 'number',
+ 'type',
+ 'batch',
+ 'added',
+ 'condition',
+ 'material_id',
+ 'note_id',
+ 'user_id'
+ ];
- private static sortKeys = [
- '_id',
- 'color',
- 'number',
- 'type',
- 'batch',
- 'added',
- 'status',
- 'notes.comment',
- 'material.name',
- 'material.supplier',
- 'material.group',
- 'material.properties.*',
- 'condition.*',
- `measurements.(?!${globals.spectrum.spectrum}.${globals.spectrum.dpt})*`
- ];
+ private static sortKeys = [
+ '_id',
+ 'color',
+ 'number',
+ 'type',
+ 'batch',
+ 'added',
+ 'status',
+ 'notes.comment',
+ 'material.name',
+ 'material.supplier',
+ 'material.group',
+ 'material.properties.*',
+ 'condition.*',
+ `measurements.(?!${globals.spectrum.spectrum}.${globals.spectrum.dpt})*`
+ ];
- private static fieldKeys = [
- ...SampleValidate.sortKeys,
- 'condition',
- 'notes',
- 'material_id',
- 'material',
- 'note_id',
- 'user_id',
- 'material._id',
- 'material.numbers',
- 'measurements',
- `measurements.${globals.spectrum.spectrum}.${globals.spectrum.dpt}`,
- ];
+ private static fieldKeys = [
+ ...SampleValidate.sortKeys,
+ 'condition',
+ 'notes',
+ 'material_id',
+ 'material',
+ 'note_id',
+ 'user_id',
+ 'material._id',
+ 'material.numbers',
+ 'measurements',
+ `measurements.${globals.spectrum.spectrum}.${globals.spectrum.dpt}`,
+ ];
- static input (data, param) { // validate input, set param to 'new' to make all attributes required
- if (param === 'new') {
- return Joi.object({
- color: this.sample.color.required(),
- type: this.sample.type.required(),
- batch: this.sample.batch.required(),
- condition: this.sample.condition.required(),
- material_id: IdValidate.get().required(),
- notes: this.sample.notes.required()
- }).validate(data);
- }
- else if (param === 'change') {
- return Joi.object({
- color: this.sample.color,
- type: this.sample.type,
- batch: this.sample.batch,
- condition: this.sample.condition,
- material_id: IdValidate.get(),
- notes: this.sample.notes,
- }).validate(data);
- }
- else if (param === 'new-admin') {
- return Joi.object({
- number: this.sample.number,
- color: this.sample.color.required(),
- type: this.sample.type.required(),
- batch: this.sample.batch.required(),
- condition: this.sample.condition.required(),
- material_id: IdValidate.get().required(),
- notes: this.sample.notes.required()
- }).validate(data);
- }
- else {
- return{error: 'No parameter specified!', value: {}};
- }
- }
+ static input (data, param) { // validate input, set param to 'new' to make all attributes required
+ if (param === 'new') {
+ return Joi.object({
+ color: this.sample.color.required(),
+ type: this.sample.type.required(),
+ batch: this.sample.batch.required(),
+ condition: this.sample.condition.required(),
+ material_id: IdValidate.get().required(),
+ notes: this.sample.notes.required()
+ }).validate(data);
+ }
+ else if (param === 'change') {
+ return Joi.object({
+ color: this.sample.color,
+ type: this.sample.type,
+ batch: this.sample.batch,
+ condition: this.sample.condition,
+ material_id: IdValidate.get(),
+ notes: this.sample.notes,
+ }).validate(data);
+ }
+ else if (param === 'new-admin') {
+ return Joi.object({
+ number: this.sample.number,
+ color: this.sample.color.required(),
+ type: this.sample.type.required(),
+ batch: this.sample.batch.required(),
+ condition: this.sample.condition.required(),
+ material_id: IdValidate.get().required(),
+ notes: this.sample.notes.required()
+ }).validate(data);
+ }
+ else {
+ return{error: 'No parameter specified!', value: {}};
+ }
+ }
- // validate output and strip unwanted properties, returns null if not valid
- static output (data, param = 'refs+added', additionalParams = []) {
- if (param === 'refs+added') {
- param = 'refs';
- data.added = data._id.getTimestamp();
- }
- data = IdValidate.stringify(data);
- let joiObject;
- if (param === 'refs') {
- joiObject = {
- _id: IdValidate.get(),
- number: this.sample.number,
- color: this.sample.color,
- type: this.sample.type,
- batch: this.sample.batch,
- condition: this.sample.condition,
- material_id: IdValidate.get(),
- material: MaterialValidate.outputV().append({number: Joi.string().max(128).allow('')}),
- note_id: IdValidate.get().allow(null),
- notes: this.sample.notes,
- user_id: IdValidate.get(),
- added: this.sample.added,
- status: this.sample.status
- };
- }
- else if(param === 'details') {
- joiObject = {
- _id: IdValidate.get(),
- number: this.sample.number,
- color: this.sample.color,
- type: this.sample.type,
- batch: this.sample.batch,
- condition: this.sample.condition,
- material: MaterialValidate.outputV(),
- measurements: Joi.array().items(MeasurementValidate.outputV()),
- notes: this.sample.notes,
- user: UserValidate.username(),
- status: this.sample.status
- }
- }
- else {
- return null;
- }
- additionalParams.forEach(param => {
- joiObject[param] = Joi.any();
- });
- const {value, error} = Joi.object(joiObject).validate(data, {stripUnknown: true});
- return error !== undefined? null : value;
- }
+ // validate output and strip unwanted properties, returns null if not valid
+ static output (data, param = 'refs+added', additionalParams = []) {
+ if (param === 'refs+added') {
+ param = 'refs';
+ data.added = data._id.getTimestamp();
+ }
+ data = IdValidate.stringify(data);
+ let joiObject;
+ if (param === 'refs') {
+ joiObject = {
+ _id: IdValidate.get(),
+ number: this.sample.number,
+ color: this.sample.color,
+ type: this.sample.type,
+ batch: this.sample.batch,
+ condition: this.sample.condition,
+ material_id: IdValidate.get(),
+ material: MaterialValidate.outputV().append({number: Joi.string().max(128).allow('')}),
+ note_id: IdValidate.get().allow(null),
+ notes: this.sample.notes,
+ user_id: IdValidate.get(),
+ added: this.sample.added,
+ status: this.sample.status
+ };
+ }
+ else if(param === 'details') {
+ joiObject = {
+ _id: IdValidate.get(),
+ number: this.sample.number,
+ color: this.sample.color,
+ type: this.sample.type,
+ batch: this.sample.batch,
+ condition: this.sample.condition,
+ material: MaterialValidate.outputV(),
+ measurements: Joi.array().items(MeasurementValidate.outputV()),
+ notes: this.sample.notes,
+ user: UserValidate.username(),
+ status: this.sample.status
+ }
+ }
+ else {
+ return null;
+ }
+ additionalParams.forEach(param => {
+ joiObject[param] = Joi.any();
+ });
+ const {value, error} = Joi.object(joiObject).validate(data, {stripUnknown: true});
+ return error !== undefined? null : value;
+ }
- static query (data, dev = false) {
- if (data.filters && data.filters.length) {
- const filterValidation = Joi.array().items(Joi.string()).validate(data.filters);
- if (filterValidation.error) return filterValidation;
- try {
- for (let i in data.filters) {
- try {
- data.filters[i] = decodeURIComponent(data.filters[i]);
- }
- catch (ignore) {}
- data.filters[i] = JSON.parse(data.filters[i]);
- data.filters[i].values = data.filters[i].values.map(e => { // validate filter values
- if (e === null) { // null values are always allowed
- return null;
- }
- let validator;
- let field = data.filters[i].field;
- if (/material\./.test(field)) { // select right validation model
- validator = MaterialValidate.outputV().append({
- number: Joi.string().max(128).allow(''),
- properties: Joi.alternatives().try(Joi.number(), Joi.string().max(128).allow(''))
- });
- field = field.replace('material.', '').split('.')[0];
- }
- else if (/measurements\./.test(field) || /condition\./.test(field)) {
- validator = Joi.object({
- value: Joi.alternatives()
- .try(
- Joi.number(),
- Joi.string().max(128).allow(''),
- Joi.boolean(),
- Joi.array()
- )
- .allow(null)
- });
- field = 'value';
- }
- else if (field === 'measurements') {
- validator = Joi.object({
- value: Joi.object({}).allow(null).disallow({})
- });
- field = 'value';
- }
- else if (field === 'notes.comment') {
- field = 'comment';
- validator = this.sample.notes
- }
- else {
- validator = Joi.object(this.sample);
- }
- const {value, error} = validator.validate({[field]: e});
- if (error) throw error; // reject invalid values
- return value[field];
- });
- }
- }
- catch (err) {
- return {error: {details: [{message: 'Invalid JSON string for filter parameter'}]}, value: null}
- }
- }
- const acceptedStatuses = [globals.status.val, globals.status.new];
- if (dev) { // dev and admin can also access deleted samples
- acceptedStatuses.push(globals.status.del)
- }
- return Joi.object({
- status: Joi.array().items(Joi.string().valid(...acceptedStatuses)).default([globals.status.val]),
- 'from-id': IdValidate.get(),
- 'to-page': Joi.number().integer(),
- 'page-size': Joi.number().integer().min(1),
- sort: Joi.string().pattern(
- new RegExp('^(' + this.sortKeys.join('|').replace(/\./g, '\\.').replace(/\*/g, '.+') + ')-(asc|desc)$', 'm')
- ).default('_id-asc'),
- output: Joi.string().valid('json', 'flatten', 'csv').default('json'),
- fields: Joi.array().items(Joi.string().pattern(
- new RegExp('^(' + this.fieldKeys.join('|').replace(/\./g, '\\.').replace(/\*/g, '.+') + ')$', 'm')
- )).default(['_id','number','type','batch','material_id','color','condition','note_id','user_id','added'])
- .messages({'string.pattern.base': 'Invalid field name'}),
- filters: Joi.array().items(Joi.object({
- mode: Joi.string().valid('eq', 'ne', 'lt', 'lte', 'gt', 'gte', 'in', 'nin', 'stringin'),
- field: Joi.string().pattern(
- new RegExp('^(' + this.fieldKeys.join('|').replace(/\./g, '\\.').replace(/\*/g, '.+') + ')$', 'm')
- ).messages({'string.pattern.base': 'Invalid filter field name'}),
- values: Joi.array().items(Joi.alternatives().try(
- Joi.string().max(128).allow(''), Joi.number(), Joi.boolean(), Joi.date().iso(), Joi.object(), null
- )).min(1)
- })).default([])
- }).with('to-page', 'page-size').validate(data);
- }
-}
\ No newline at end of file
+ static query (data, dev = false) {
+ if (data.filters && data.filters.length) {
+ const filterValidation = Joi.array().items(Joi.string()).validate(data.filters);
+ if (filterValidation.error) return filterValidation;
+ try {
+ for (let i in data.filters) {
+ try {
+ data.filters[i] = decodeURIComponent(data.filters[i]);
+ }
+ catch (ignore) {}
+ data.filters[i] = JSON.parse(data.filters[i]);
+ data.filters[i].values = data.filters[i].values.map(e => { // validate filter values
+ if (e === null) { // null values are always allowed
+ return null;
+ }
+ let validator;
+ let field = data.filters[i].field;
+ if (/material\./.test(field)) { // select right validation model
+ validator = MaterialValidate.outputV().append({
+ number: Joi.string().max(128).allow(''),
+ properties: Joi.alternatives().try(Joi.number(), Joi.string().max(128).allow(''))
+ });
+ field = field.replace('material.', '').split('.')[0];
+ }
+ else if (/measurements\./.test(field) || /condition\./.test(field)) {
+ validator = Joi.object({
+ value: Joi.alternatives()
+ .try(
+ Joi.number(),
+ Joi.string().max(128).allow(''),
+ Joi.boolean(),
+ Joi.array()
+ )
+ .allow(null)
+ });
+ field = 'value';
+ }
+ else if (field === 'measurements') {
+ validator = Joi.object({
+ value: Joi.object({}).allow(null).disallow({})
+ });
+ field = 'value';
+ }
+ else if (field === 'notes.comment') {
+ field = 'comment';
+ validator = this.sample.notes
+ }
+ else {
+ validator = Joi.object(this.sample);
+ }
+ const {value, error} = validator.validate({[field]: e});
+ if (error) throw error; // reject invalid values
+ return value[field];
+ });
+ }
+ }
+ catch (err) {
+ return {error: {details: [{message: 'Invalid JSON string for filter parameter'}]}, value: null}
+ }
+ }
+ const acceptedStatuses = [globals.status.val, globals.status.new];
+ if (dev) { // dev and admin can also access deleted samples
+ acceptedStatuses.push(globals.status.del)
+ }
+ return Joi.object({
+ status: Joi.array().items(Joi.string().valid(...acceptedStatuses)).default([globals.status.val]),
+ 'from-id': IdValidate.get(),
+ 'to-page': Joi.number().integer(),
+ 'page-size': Joi.number().integer().min(1),
+ sort: Joi.string().pattern(
+ new RegExp('^(' + this.sortKeys.join('|').replace(/\./g, '\\.').replace(/\*/g, '.+') + ')-(asc|desc)$', 'm')
+ ).default('_id-asc'),
+ output: Joi.string().valid('json', 'flatten', 'csv').default('json'),
+ fields: Joi.array().items(Joi.string().pattern(
+ new RegExp('^(' + this.fieldKeys.join('|').replace(/\./g, '\\.').replace(/\*/g, '.+') + ')$', 'm')
+ )).default(['_id','number','type','batch','material_id','color','condition','note_id','user_id','added'])
+ .messages({'string.pattern.base': 'Invalid field name'}),
+ filters: Joi.array().items(Joi.object({
+ mode: Joi.string().valid('eq', 'ne', 'lt', 'lte', 'gt', 'gte', 'in', 'nin', 'stringin'),
+ field: Joi.string().pattern(
+ new RegExp('^(' + this.fieldKeys.join('|').replace(/\./g, '\\.').replace(/\*/g, '.+') + ')$', 'm')
+ ).messages({'string.pattern.base': 'Invalid filter field name'}),
+ values: Joi.array().items(Joi.alternatives().try(
+ Joi.string().max(128).allow(''), Joi.number(), Joi.boolean(), Joi.date().iso(), Joi.object(), null
+ )).min(1)
+ })).default([])
+ }).with('to-page', 'page-size').validate(data);
+ }
+}
diff --git a/src/routes/validate/template.ts b/src/routes/validate/template.ts
index 9de7474..ab84661 100644
--- a/src/routes/validate/template.ts
+++ b/src/routes/validate/template.ts
@@ -2,70 +2,70 @@ import Joi from 'joi';
import IdValidate from './id';
export default class TemplateValidate {
- private static template = {
- name: Joi.string()
- .max(128),
+ private static template = {
+ name: Joi.string()
+ .max(128),
- version: Joi.number()
- .min(1),
+ version: Joi.number()
+ .min(1),
- parameters: Joi.array()
- .items(
- Joi.object({
- name: Joi.string()
- .max(128)
- .invalid('condition_template', 'material_template')
- .pattern(/^[^.]+$/)
- .required()
- .messages({'string.pattern.base': 'name must not contain a dot'}),
+ parameters: Joi.array()
+ .items(
+ Joi.object({
+ name: Joi.string()
+ .max(128)
+ .invalid('condition_template', 'material_template')
+ .pattern(/^[^.]+$/)
+ .required()
+ .messages({'string.pattern.base': 'name must not contain a dot'}),
- range: Joi.object({
- values: Joi.array()
- .min(1),
+ range: Joi.object({
+ values: Joi.array()
+ .min(1),
- min: Joi.number(),
+ min: Joi.number(),
- max: Joi.number(),
+ max: Joi.number(),
- type: Joi.string()
- .valid('string', 'number', 'boolean', 'array'),
+ type: Joi.string()
+ .valid('string', 'number', 'boolean', 'array'),
- required: Joi.boolean()
- })
- .oxor('values', 'min')
- .oxor('values', 'max')
- .required()
- })
- )
- };
+ required: Joi.boolean()
+ })
+ .oxor('values', 'min')
+ .oxor('values', 'max')
+ .required()
+ })
+ )
+ };
- static input (data, param) { // validate input, set param to 'new' to make all attributes required
- if (param === 'new') {
- return Joi.object({
- name: this.template.name.required(),
- parameters: this.template.parameters.required()
- }).validate(data);
- }
- else if (param === 'change') {
- return Joi.object({
- name: this.template.name,
- parameters: this.template.parameters
- }).validate(data);
- }
- else {
- return{error: 'No parameter specified!', value: {}};
- }
- }
+ static input (data, param) { // validate input, set param to 'new' to make all attributes required
+ if (param === 'new') {
+ return Joi.object({
+ name: this.template.name.required(),
+ parameters: this.template.parameters.required()
+ }).validate(data);
+ }
+ else if (param === 'change') {
+ return Joi.object({
+ name: this.template.name,
+ parameters: this.template.parameters
+ }).validate(data);
+ }
+ else {
+ return{error: 'No parameter specified!', value: {}};
+ }
+ }
- static output (data) { // validate output and strip unwanted properties, returns null if not valid
- data = IdValidate.stringify(data);
- const {value, error} = Joi.object({
- _id: IdValidate.get(),
- name: this.template.name,
- version: this.template.version,
- first_id: IdValidate.get(),
- parameters: this.template.parameters
- }).validate(data, {stripUnknown: true});
- return error !== undefined? null : value;
- }
-}
\ No newline at end of file
+ static output (data) { // validate output and strip unwanted properties, returns null if not valid
+ data = IdValidate.stringify(data);
+ const {value, error} = Joi.object({
+ _id: IdValidate.get(),
+ name: this.template.name,
+ version: this.template.version,
+ first_id: IdValidate.get(),
+ parameters: this.template.parameters
+ }).validate(data, {stripUnknown: true});
+ return error !== undefined? null : value;
+ }
+}
diff --git a/src/routes/validate/user.ts b/src/routes/validate/user.ts
index c9288d7..6ada465 100644
--- a/src/routes/validate/user.ts
+++ b/src/routes/validate/user.ts
@@ -4,104 +4,104 @@ import globals from '../../globals';
import IdValidate from './id';
export default class UserValidate { // validate input for user
- private static user = {
- name: Joi.string()
- .lowercase()
- .pattern(new RegExp('^[a-z0-9-_.]+$'))
- .max(128)
- .messages({'string.pattern.base': 'name must only contain a-z0-9_.'}),
+ private static user = {
+ name: Joi.string()
+ .lowercase()
+ .pattern(new RegExp('^[a-z0-9-_.]+$'))
+ .max(128)
+ .messages({'string.pattern.base': 'name must only contain a-z0-9_.'}),
- email: Joi.string()
- .email({minDomainSegments: 2})
- .lowercase()
- .max(128),
+ email: Joi.string()
+ .email({minDomainSegments: 2})
+ .lowercase()
+ .max(128),
- pass: Joi.string()
- .min(8)
- .max(128),
+ pass: Joi.string()
+ .min(8)
+ .max(128),
- level: Joi.string()
- .valid(...Object.values(globals.levels)),
+ level: Joi.string()
+ .valid(...Object.values(globals.levels)),
- location: Joi.string()
- .alphanum()
- .max(128),
+ location: Joi.string()
+ .alphanum()
+ .max(128),
- devices: Joi.array()
- .items(Joi.string()
- .allow('')
- .max(128)
- ),
+ devices: Joi.array()
+ .items(Joi.string()
+ .allow('')
+ .max(128)
+ ),
- models: Joi.array()
- .items(IdValidate.get()),
+ models: Joi.array()
+ .items(IdValidate.get()),
- status: Joi.string()
- .valid(...Object.values(globals.status))
- };
+ status: Joi.string()
+ .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
- if (param === 'new') {
- return Joi.object({
- name: this.user.name.required(),
- email: this.user.email.required(),
- pass: this.user.pass.required(),
- level: this.user.level.required(),
- location: this.user.location.required(),
- devices: this.user.devices.required(),
- models: this.user.models.required()
- }).validate(data);
- }
- else if (param === 'change') {
- return Joi.object({
- name: this.user.name,
- email: this.user.email,
- pass: this.user.pass,
- location: this.user.location,
- devices: this.user.devices
- }).validate(data);
- }
- else if (param === 'changeadmin') {
- return Joi.object({
- name: this.user.name,
- email: this.user.email,
- pass: this.user.pass,
- level: this.user.level,
- location: this.user.location,
- devices: this.user.devices,
- models: this.user.models
- }).validate(data);
- }
- else {
- return {error: 'No parameter specified!', value: {}};
- }
- }
+ static input (data, param) { // validate input, set param to 'new' to make all attributes required
+ if (param === 'new') {
+ return Joi.object({
+ name: this.user.name.required(),
+ email: this.user.email.required(),
+ pass: this.user.pass.required(),
+ level: this.user.level.required(),
+ location: this.user.location.required(),
+ devices: this.user.devices.required(),
+ models: this.user.models.required()
+ }).validate(data);
+ }
+ else if (param === 'change') {
+ return Joi.object({
+ name: this.user.name,
+ email: this.user.email,
+ pass: this.user.pass,
+ location: this.user.location,
+ devices: this.user.devices
+ }).validate(data);
+ }
+ else if (param === 'changeadmin') {
+ return Joi.object({
+ name: this.user.name,
+ email: this.user.email,
+ pass: this.user.pass,
+ level: this.user.level,
+ location: this.user.location,
+ devices: this.user.devices,
+ models: this.user.models
+ }).validate(data);
+ }
+ else {
+ return {error: 'No parameter specified!', value: {}};
+ }
+ }
- static output (data, param = '') { // validate output and strip unwanted properties, returns null if not valid
- data = IdValidate.stringify(data);
- const validate: {[key: string]: object} = {
- _id: IdValidate.get(),
- name: this.user.name,
- email: this.user.email,
- level: this.user.level,
- location: this.user.location,
- devices: this.user.devices,
- models: this.user.models
- }
- if (param === 'admin') {
- validate.status = this.user.status;
- }
- const {value, error} = Joi.object(validate).validate(data, {stripUnknown: true});
- return error !== undefined? null : value;
- }
+ static output (data, param = '') { // validate output and strip unwanted properties, returns null if not valid
+ data = IdValidate.stringify(data);
+ const validate: {[key: string]: object} = {
+ _id: IdValidate.get(),
+ name: this.user.name,
+ email: this.user.email,
+ level: this.user.level,
+ location: this.user.location,
+ devices: this.user.devices,
+ models: this.user.models
+ }
+ if (param === 'admin') {
+ validate.status = this.user.status;
+ }
+ const {value, error} = Joi.object(validate).validate(data, {stripUnknown: true});
+ return error !== undefined? null : value;
+ }
- static isSpecialName (name) { // true if name belongs to special names
- return this.specialUsernames.indexOf(name) > -1;
- }
+ static isSpecialName (name) { // true if name belongs to special names
+ return this.specialUsernames.indexOf(name) > -1;
+ }
- static username() {
- return this.user.name;
- }
+ static username() {
+ return this.user.name;
+ }
}
diff --git a/src/test/helper.ts b/src/test/helper.ts
index 70dd638..1ef25d0 100644
--- a/src/test/helper.ts
+++ b/src/test/helper.ts
@@ -7,141 +7,141 @@ import IdValidate from '../routes/validate/id';
export default class TestHelper {
- public static auth = { // test user credentials
- admin: {pass: 'Abc123!#', key: '000000000000000000001003', id: '000000000000000000000003'},
- janedoe: {pass: 'Xyz890*)', key: '000000000000000000001002', id: '000000000000000000000002'},
- user: {pass: 'Xyz890*)', key: '000000000000000000001001', id: '000000000000000000000001'},
- johnnydoe: {pass: 'Xyz890*)', key: '000000000000000000001004', id: '000000000000000000000004'},
- customer: {pass: 'Xyz890*)', key: '000000000000000000001005', id: '000000000000000000000005'}
- }
+ public static auth = { // test user credentials
+ admin: {pass: 'Abc123!#', key: '000000000000000000001003', id: '000000000000000000000003'},
+ janedoe: {pass: 'Xyz890*)', key: '000000000000000000001002', id: '000000000000000000000002'},
+ user: {pass: 'Xyz890*)', key: '000000000000000000001001', id: '000000000000000000000001'},
+ johnnydoe: {pass: 'Xyz890*)', key: '000000000000000000001004', id: '000000000000000000000004'},
+ customer: {pass: 'Xyz890*)', key: '000000000000000000001005', id: '000000000000000000000005'}
+ }
- public static res = { // default responses
- 400: {status: 'Bad request'},
- 401: {status: 'Unauthorized'},
- 403: {status: 'Forbidden'},
- 404: {status: 'Not found'},
- 500: {status: 'Internal server error'}
- }
+ public static res = { // default responses
+ 400: {status: 'Bad request'},
+ 401: {status: 'Unauthorized'},
+ 403: {status: 'Forbidden'},
+ 404: {status: 'Not found'},
+ 500: {status: 'Internal server error'}
+ }
- static before (done) {
- process.env.port = '2999';
- process.env.NODE_ENV = 'test';
- db.connect('test', done);
- }
+ static before (done) {
+ process.env.port = '2999';
+ process.env.NODE_ENV = 'test';
+ db.connect('test', done);
+ }
- static beforeEach (server, done) {
- // 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 => {
- delete require.cache[key]; // prevent loading from cache
- });
- server = require('../index');
- db.drop(err => { // reset database
- if (err) return done(err);
- db.loadJson(require('./db.json'), done);
- });
- return server
- }
+ static beforeEach (server, done) {
+ // 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 => {
+ delete require.cache[key]; // prevent loading from cache
+ });
+ server = require('../index');
+ db.drop(err => { // reset database
+ if (err) return done(err);
+ db.loadJson(require('./db.json'), done);
+ });
+ return server
+ }
- // 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)}
- static request (server, done, options) {
- let st = supertest(server);
- if (options.hasOwnProperty('auth') && options.auth.hasOwnProperty('key')) { // resolve API key
- options.url += '?key=' +
- (this.auth.hasOwnProperty(options.auth.key)? this.auth[options.auth.key].key : options.auth.key);
- }
- switch (options.method) { // http method
- case 'get':
- st = st.get(options.url)
- break;
- case 'post':
- st = st.post(options.url)
- break;
- case 'put':
- st = st.put(options.url)
- break;
- case 'delete':
- st = st.delete(options.url)
- break;
- }
- if (options.hasOwnProperty('reqType')) { // request body
- st = st.type(options.reqType);
- }
- if (options.hasOwnProperty('req')) { // request body
- st = st.send(options.req);
- }
- if (options.hasOwnProperty('reqContentType')) { // request body
- st = st.set('Content-Type', options.reqContentType);
- }
- if (options.hasOwnProperty('auth') && options.auth.hasOwnProperty('basic')) { // resolve basic auth
- if (this.auth.hasOwnProperty(options.auth.basic)) {
- st = st.auth(options.auth.basic, this.auth[options.auth.basic].pass)
- }
- else {
- st = st.auth(options.auth.basic.name, options.auth.basic.pass)
- }
- }
- if (options.hasOwnProperty('contentType')) {
- st = st.expect('Content-type', options.contentType).expect(options.httpStatus);
- }
- else {
- st = st.expect('Content-type', /json/).expect(options.httpStatus);
- }
- if (options.hasOwnProperty('res')) { // evaluate result
- return st.end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql(options.res);
- done();
- });
- }
- else if (this.res.hasOwnProperty(options.httpStatus) && options.default !== false) { // evaluate default results
- return st.end((err, res) => {
- if (err) return done (err);
- should(res.body).be.eql(this.res[options.httpStatus]);
- done();
- });
- }
- // check changelog, takes log: {collection, skip, data/(dataAdd, dataIgn)}
- else if (options.hasOwnProperty('log')) {
- return st.end(err => {
- if (err) return done (err);
- ChangelogModel.findOne({}).sort({_id: -1}).skip(options.log.skip? options.log.skip : 0)
- .lean().exec((err, data) => { // latest entry
- if (err) return done(err);
- 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('collection_name', options.log.collection);
- if (options.log.hasOwnProperty('data')) {
- should(data).have.property('data', options.log.data);
- }
- else {
- const ignore = ['_id', '__v'];
- if (options.log.hasOwnProperty('dataIgn')) {
- ignore.push(...options.log.dataIgn);
- }
- let tmp = options.req ? options.req : {};
- if (options.log.hasOwnProperty('dataAdd')) {
- _.assign(tmp, options.log.dataAdd)
- }
- should(IdValidate.stringify(_.omit(data.data, ignore))).be.eql(_.omit(tmp, ignore));
- }
- if (data.user_id) {
- should(data.user_id.toString()).be.eql(this.auth[options.auth.basic].id);
- }
- done();
- });
- });
- }
- else { // return object to do .end() manually
- return st;
- }
- }
+ // 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)}
+ static request (server, done, options) {
+ let st = supertest(server);
+ if (options.hasOwnProperty('auth') && options.auth.hasOwnProperty('key')) { // resolve API key
+ options.url += '?key=' +
+ (this.auth.hasOwnProperty(options.auth.key)? this.auth[options.auth.key].key : options.auth.key);
+ }
+ switch (options.method) { // http method
+ case 'get':
+ st = st.get(options.url)
+ break;
+ case 'post':
+ st = st.post(options.url)
+ break;
+ case 'put':
+ st = st.put(options.url)
+ break;
+ case 'delete':
+ st = st.delete(options.url)
+ break;
+ }
+ if (options.hasOwnProperty('reqType')) { // request body
+ st = st.type(options.reqType);
+ }
+ if (options.hasOwnProperty('req')) { // request body
+ st = st.send(options.req);
+ }
+ if (options.hasOwnProperty('reqContentType')) { // request body
+ st = st.set('Content-Type', options.reqContentType);
+ }
+ if (options.hasOwnProperty('auth') && options.auth.hasOwnProperty('basic')) { // resolve basic auth
+ if (this.auth.hasOwnProperty(options.auth.basic)) {
+ st = st.auth(options.auth.basic, this.auth[options.auth.basic].pass)
+ }
+ else {
+ st = st.auth(options.auth.basic.name, options.auth.basic.pass)
+ }
+ }
+ if (options.hasOwnProperty('contentType')) {
+ st = st.expect('Content-type', options.contentType).expect(options.httpStatus);
+ }
+ else {
+ st = st.expect('Content-type', /json/).expect(options.httpStatus);
+ }
+ if (options.hasOwnProperty('res')) { // evaluate result
+ return st.end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql(options.res);
+ done();
+ });
+ }
+ else if (this.res.hasOwnProperty(options.httpStatus) && options.default !== false) { // evaluate default results
+ return st.end((err, res) => {
+ if (err) return done (err);
+ should(res.body).be.eql(this.res[options.httpStatus]);
+ done();
+ });
+ }
+ // check changelog, takes log: {collection, skip, data/(dataAdd, dataIgn)}
+ else if (options.hasOwnProperty('log')) {
+ return st.end(err => {
+ if (err) return done (err);
+ ChangelogModel.findOne({}).sort({_id: -1}).skip(options.log.skip? options.log.skip : 0)
+ .lean().exec((err, data) => { // latest entry
+ if (err) return done(err);
+ 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('collection_name', options.log.collection);
+ if (options.log.hasOwnProperty('data')) {
+ should(data).have.property('data', options.log.data);
+ }
+ else {
+ const ignore = ['_id', '__v'];
+ if (options.log.hasOwnProperty('dataIgn')) {
+ ignore.push(...options.log.dataIgn);
+ }
+ let tmp = options.req ? options.req : {};
+ if (options.log.hasOwnProperty('dataAdd')) {
+ _.assign(tmp, options.log.dataAdd)
+ }
+ should(IdValidate.stringify(_.omit(data.data, ignore))).be.eql(_.omit(tmp, ignore));
+ }
+ if (data.user_id) {
+ should(data.user_id.toString()).be.eql(this.auth[options.auth.basic].id);
+ }
+ done();
+ });
+ });
+ }
+ else { // return object to do .end() manually
+ return st;
+ }
+ }
- static afterEach (server, done) {
- server.close(done);
- }
+ static afterEach (server, done) {
+ server.close(done);
+ }
- static after(done) {
- db.disconnect(done);
- }
-}
\ No newline at end of file
+ static after(done) {
+ db.disconnect(done);
+ }
+}
diff --git a/src/test/loadDev.ts b/src/test/loadDev.ts
index 15a6868..40bd5c5 100644
--- a/src/test/loadDev.ts
+++ b/src/test/loadDev.ts
@@ -3,12 +3,12 @@ import db from '../db';
// script to load test db into dev db for a clean start
db.connect('dev', () => {
- console.info('dropping data...');
- db.drop(() => { // reset database
- console.info('loading data...');
- db.loadJson(require('./db.json'), () => {
- console.info('done');
- process.exit(0);
- });
- });
+ console.info('dropping data...');
+ db.drop(() => { // reset database
+ console.info('loading data...');
+ db.loadJson(require('./db.json'), () => {
+ console.info('done');
+ process.exit(0);
+ });
+ });
});