diff --git a/api/schemas.yaml b/api/schemas.yaml index 84722a5..3f5098c 100644 --- a/api/schemas.yaml +++ b/api/schemas.yaml @@ -145,6 +145,11 @@ Template: properties: name: type: string + example: humidity + version: + type: number + readOnly: true + example: 1 parameters: type: array items: @@ -152,8 +157,20 @@ Template: properties: name: type: string + example: kf range: type: object + example: + min: 0 + max: 2 + +TreatmentTemplate: + allOf: + - $ref: 'api.yaml#/components/schemas/Template' + properties: + number_prefix: + type: string + example: B Email: properties: diff --git a/api/template.yaml b/api/template.yaml index 5b362fb..37f374a 100644 --- a/api/template.yaml +++ b/api/template.yaml @@ -14,23 +14,15 @@ schema: type: array items: - $ref: 'api.yaml#/components/schemas/Template' - example: - _id: 5ea0450ed851c30a90e70894 - name: heat aging - parameters: - - name: method - range: - values: - - copper - - hot air + $ref: 'api.yaml#/components/schemas/TreatmentTemplate' 401: $ref: 'api.yaml#/components/responses/401' 500: $ref: 'api.yaml#/components/responses/500' -/template/treatment/{name}: + +/template/treatment/{id}: parameters: - - $ref: 'api.yaml#/components/parameters/Name' + - $ref: 'api.yaml#/components/parameters/Id' get: summary: treatment method details description: 'Auth: basic, levels: read, write, maintain, admin' @@ -44,17 +36,7 @@ content: application/json: schema: - allOf: - - $ref: 'api.yaml#/components/schemas/Template' - example: - _id: 5ea0450ed851c30a90e70894 - name: heat aging - parameters: - - name: method - range: - values: - - copper - - hot air + $ref: 'api.yaml#/components/schemas/TreatmentTemplate' 401: $ref: 'api.yaml#/components/responses/401' 404: @@ -62,8 +44,9 @@ 500: $ref: 'api.yaml#/components/responses/500' put: - summary: add/change treatment method + summary: change treatment method description: 'Auth: basic, levels: maintain, admin' + x-doc: With a change a new version is set, resulting in a new template with a new id tags: - /template security: @@ -73,33 +56,14 @@ content: application/json: schema: - allOf: - - $ref: 'api.yaml#/components/schemas/Template' - example: - name: heat aging - parameters: - - name: method - range: - values: - - copper - - hot air + $ref: 'api.yaml#/components/schemas/TreatmentTemplate' responses: 200: description: treatment details content: application/json: schema: - allOf: - - $ref: 'api.yaml#/components/schemas/Template' - example: - _id: 5ea0450ed851c30a90e70894 - name: heat aging - parameters: - - name: method - range: - values: - - copper - - hot air + $ref: 'api.yaml#/components/schemas/TreatmentTemplate' 400: $ref: 'api.yaml#/components/responses/400' 401: @@ -110,26 +74,37 @@ $ref: 'api.yaml#/components/responses/404' 500: $ref: 'api.yaml#/components/responses/500' - delete: - summary: delete treatment method + +/template/treatment/new: + post: + summary: add treatment method description: 'Auth: basic, levels: maintain, admin' tags: - /template security: - BasicAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: 'api.yaml#/components/schemas/TreatmentTemplate' responses: 200: - $ref: 'api.yaml#/components/responses/Ok' + description: treatment details + content: + application/json: + schema: + $ref: 'api.yaml#/components/schemas/TreatmentTemplate' 400: $ref: 'api.yaml#/components/responses/400' 401: $ref: 'api.yaml#/components/responses/401' 403: $ref: 'api.yaml#/components/responses/403' - 404: - $ref: 'api.yaml#/components/responses/404' 500: $ref: 'api.yaml#/components/responses/500' + /template/measurements: get: summary: all available measurement methods @@ -147,21 +122,13 @@ type: array items: $ref: 'api.yaml#/components/schemas/Template' - example: - _id: 5ea0450ed851c30a90e70894 - name: humidity - parameters: - - name: kf - range: - min: 0 - max: 2 401: $ref: 'api.yaml#/components/responses/401' 500: $ref: 'api.yaml#/components/responses/500' -/template/measurement/{name}: +/template/measurement/{id}: parameters: - - $ref: 'api.yaml#/components/parameters/Name' + - $ref: 'api.yaml#/components/parameters/Id' get: summary: measurement method details description: 'Auth: basic, levels: read, write, maintain, admin' @@ -175,16 +142,7 @@ content: application/json: schema: - allOf: - - $ref: 'api.yaml#/components/schemas/Template' - example: - _id: 5ea0450ed851c30a90e70894 - name: humidity - parameters: - - name: kf - range: - min: 0 - max: 2 + $ref: 'api.yaml#/components/schemas/Template' 400: $ref: 'api.yaml#/components/responses/400' 401: @@ -194,7 +152,7 @@ 500: $ref: 'api.yaml#/components/responses/500' put: - summary: add/change measurement method + summary: change measurement method description: 'Auth: basic, levels: maintain, admin' tags: - /template @@ -205,32 +163,14 @@ content: application/json: schema: - allOf: - - $ref: 'api.yaml#/components/schemas/Template' - example: - _id: 5ea0450ed851c30a90e70894 - name: humidity - parameters: - - name: kf - range: - min: 0 - max: 2 + $ref: 'api.yaml#/components/schemas/Template' responses: 200: description: measurement details content: application/json: schema: - allOf: - - $ref: 'api.yaml#/components/schemas/Template' - example: - _id: 5ea0450ed851c30a90e70894 - name: humidity - parameters: - - name: kf - range: - min: 0 - max: 2 + $ref: 'api.yaml#/components/schemas/Template' 400: $ref: 'api.yaml#/components/responses/400' 401: @@ -241,23 +181,33 @@ $ref: 'api.yaml#/components/responses/404' 500: $ref: 'api.yaml#/components/responses/500' - delete: - summary: delete measurement method + +/template/measurement/new: + post: + summary: add measurement method description: 'Auth: basic, levels: maintain, admin' tags: - /template security: - BasicAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: 'api.yaml#/components/schemas/Template' responses: 200: - $ref: 'api.yaml#/components/responses/Ok' + description: measurement details + content: + application/json: + schema: + $ref: 'api.yaml#/components/schemas/Template' 400: $ref: 'api.yaml#/components/responses/400' 401: $ref: 'api.yaml#/components/responses/401' 403: $ref: 'api.yaml#/components/responses/403' - 404: - $ref: 'api.yaml#/components/responses/404' 500: $ref: 'api.yaml#/components/responses/500' \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4629b37..4c3c77d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,30 @@ "js-yaml": "^3.13.1" } }, + "@apidevtools/openapi-schemas": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.0.3.tgz", + "integrity": "sha512-QoPaxGXfgqgGpK1p21FJ400z56hV681a8DOcZt3J5z0WIHgFeaIZ4+6bX5ATqmOoCpRCsH4ITEwKaOyFMz7wOA==" + }, + "@apidevtools/swagger-methods": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.1.tgz", + "integrity": "sha512-1Vlm18XYW6Yg7uHunroXeunWz5FShPFAdxBbPy8H6niB2Elz9QQsCoYHMbcc11EL1pTxaIr9HXz2An/mHXlX1Q==" + }, + "@apidevtools/swagger-parser": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-9.0.1.tgz", + "integrity": "sha512-Irqybg4dQrcHhZcxJc/UM4vO7Ksoj1Id5e+K94XUOzllqX1n47HEA50EKiXTCQbykxuJ4cYGIivjx/MRSTC5OA==", + "requires": { + "@apidevtools/json-schema-ref-parser": "^8.0.0", + "@apidevtools/openapi-schemas": "^2.0.2", + "@apidevtools/swagger-methods": "^3.0.0", + "@jsdevtools/ono": "^7.1.0", + "call-me-maybe": "^1.0.1", + "openapi-types": "^1.3.5", + "z-schema": "^4.2.2" + } + }, "@babel/code-frame": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", @@ -1353,6 +1377,16 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, "log-symbols": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", @@ -1743,6 +1777,11 @@ "wrappy": "1" } }, + "openapi-types": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-1.3.5.tgz", + "integrity": "sha512-11oi4zYorsgvg5yBarZplAqbpev5HkuVNPlZaPTknPDzAynq+lnJdXAmruGWP0s+dNYZS7bjM+xrTpJw7184Fg==" + }, "p-cancelable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", @@ -2505,6 +2544,11 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, + "validator": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-12.2.0.tgz", + "integrity": "sha512-jJfE/DW6tIK1Ek8nCfNFqt8Wb3nzMoAbocBF6/Icgg1ZFSBpObdnwVY2jQj6qUqzhx5jc71fpvBWyLGO7Xl+nQ==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -2709,6 +2753,17 @@ "lodash": "^4.17.15", "yargs": "^13.3.0" } + }, + "z-schema": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-4.2.3.tgz", + "integrity": "sha512-zkvK/9TC6p38IwcrbnT3ul9in1UX4cm1y/VZSs4GHKIiDCrlafc+YQBgQBUdDXLAoZHf2qvQ7gJJOo6yT1LH6A==", + "requires": { + "commander": "^2.7.1", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^12.0.0" + } } } } diff --git a/package.json b/package.json index e58c0a0..4ec763a 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "license": "ISC", "dependencies": { "@apidevtools/json-schema-ref-parser": "^8.0.0", + "@apidevtools/swagger-parser": "^9.0.1", "@hapi/joi": "^17.1.1", "@types/bcrypt": "^3.0.0", "@types/body-parser": "^1.19.0", diff --git a/src/api.ts b/src/api.ts index 77a60ca..f85393f 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,5 +1,6 @@ import swagger from 'swagger-ui-express'; import jsonRefParser, {JSONSchema} from '@apidevtools/json-schema-ref-parser'; +import oasParser from '@apidevtools/swagger-parser'; // modifies the normal swagger-ui-express package @@ -19,7 +20,15 @@ export default class api { apiDoc = doc; apiDoc.paths = apiDoc.paths.allOf.reduce((s, e) => Object.assign(s, e)); // bundle routes apiDoc = this.resolveXDoc(apiDoc); - swagger.setup(apiDoc); + oasParser.validate(apiDoc, (err, api) => { + if (err) { + console.error(err); + } + else { + console.info('API ok, version ' + api.info.version); + swagger.setup(apiDoc); + } + }); }); return swagger.setup(apiDoc, {customCssUrl: '/static/styles/swagger.css'}) } diff --git a/src/routes/material.spec.ts b/src/routes/material.spec.ts index e7767de..0faf04e 100644 --- a/src/routes/material.spec.ts +++ b/src/routes/material.spec.ts @@ -1,7 +1,7 @@ import should from 'should/as-function'; import MaterialModel from '../models/material'; import TestHelper from "../test/helper"; -// TODO: status + // TODO: numbers with color only (no number) // TODO: deal with numbers with leading zeros diff --git a/src/routes/material.ts b/src/routes/material.ts index 7601796..a912f5e 100644 --- a/src/routes/material.ts +++ b/src/routes/material.ts @@ -8,7 +8,7 @@ import IdValidate from './validate/id'; import res400 from './validate/res400'; import mongoose from 'mongoose'; -// TODO: remove f() for await + const router = express.Router(); diff --git a/src/routes/template.spec.ts b/src/routes/template.spec.ts index d3f973a..3c05991 100644 --- a/src/routes/template.spec.ts +++ b/src/routes/template.spec.ts @@ -3,7 +3,8 @@ import TemplateTreatmentModel from '../models/treatment_template'; import TemplateMeasurementModel from '../models/measurement_template'; import TestHelper from "../test/helper"; -// TODO: remove DELETE methods, only updates possible +// TODO: convert name to id, criteria for new name, criteria for new version, criteria for prefix + describe('/template', () => { let server; before(done => TestHelper.before(done)); @@ -26,6 +27,8 @@ describe('/template', () => { should(treatment).have.only.keys('_id', 'name', 'parameters'); should(treatment).have.property('_id').be.type('string'); should(treatment).have.property('name').be.type('string'); + should(treatment).have.property('version').be.type('number'); + should(treatment).have.property('number_prefix').be.type('string'); should(treatment.parameters).matchEach(number => { should(number).have.only.keys('name', 'range'); should(number).have.property('name').be.type('string'); @@ -52,28 +55,28 @@ describe('/template', () => { }); }); - describe('GET /template/treatment/{name}', () => { + describe('GET /template/treatment/{id}', () => { it('returns the right treatment template', done => { TestHelper.request(server, done, { method: 'get', - url: '/template/treatment/heat%20treatment', + url: '/template/treatment/200000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 200, - res: {_id: '200000000000000000000001', name: 'heat treatment', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]} + res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, number_prefix: 'A', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]} }); }); it('rejects an API key', done => { TestHelper.request(server, done, { method: 'get', - url: '/template/treatment/heat%20treatment', + url: '/template/treatment/200000000000000000000001', auth: {key: 'janedoe'}, httpStatus: 401 }); }); - it('rejects an unknown name', done => { + it('rejects an unknown id', done => { TestHelper.request(server, done, { method: 'get', - url: '/template/treatment/xxx', + url: '/template/treatment/000000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 404 }); @@ -81,7 +84,7 @@ describe('/template', () => { it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'get', - url: '/template/treatment/heat%20treatment', + url: '/template/treatment/200000000000000000000001', httpStatus: 401 }); }); @@ -91,38 +94,50 @@ describe('/template', () => { it('returns the right treatment template', done => { TestHelper.request(server, done, { method: 'put', - url: '/template/treatment/heat%20treatment', + url: '/template/treatment/200000000000000000000001', auth: {basic: 'admin'}, httpStatus: 200, req: {}, - res: {_id: '200000000000000000000001', name: 'heat treatment', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]} + res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, number_prefix: 'A', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]} }); }); it('keeps unchanged properties', done => { TestHelper.request(server, done, { method: 'put', - url: '/template/treatment/heat%20treatment', + url: '/template/treatment/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}}]}, - res: {_id: '200000000000000000000001', name: 'heat treatment', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]} + res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, number_prefix: 'A', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]} + }); + }); + it('keeps only one unchanged property', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/template/treatment/200000000000000000000001', + auth: {basic: 'admin'}, + httpStatus: 200, + req: {name: 'heat treatment'}, + res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, number_prefix: 'A', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]} }); }); it('changes the given properties', done => { TestHelper.request(server, done, { method: 'put', - url: '/template/treatment/heat%20treatment', + url: '/template/treatment/200000000000000000000001', auth: {basic: 'admin'}, httpStatus: 200, req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]} }).end((err, res) => { if (err) return done(err); - should(res.body).be.eql({_id: '200000000000000000000001', name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]}); + should(res.body).be.eql({_id: '200000000000000000000001', name: 'heat aging', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {min: 1}}]}); TemplateTreatmentModel.find({name: 'heat aging'}).lean().exec((err, data:any) => { if (err) return done(err); should(data).have.lengthOf(1); - should(data[0]).have.only.keys('_id', 'name', 'parameters', '__v'); + should(data[0]).have.only.keys('_id', 'name', 'version', 'number_prefix', 'parameters', '__v'); should(data[0]).have.property('name', 'heat aging'); + should(data[0]).have.property('version', 2); + should(data[0]).have.property('number_prefix', 'A'); should(data[0]).have.property('parameters').have.lengthOf(1); should(data[0].parameters[0]).have.property('name', 'time'); should(data[0].parameters[0]).have.property('range'); @@ -131,50 +146,122 @@ describe('/template', () => { }); }); }); + it('allows changing only one property'); // TODO: adapt PUT to other PUTs and do POST, everything for measurement too it('supports values ranges', done => { TestHelper.request(server, done, { method: 'put', - url: '/template/treatment/heat%20treatment', + url: '/template/treatment/200000000000000000000001', auth: {basic: 'admin'}, httpStatus: 200, req: {parameters: [{name: 'time', range: {values: [1, 2, 5]}}]}, - res: {_id: '200000000000000000000001', name: 'heat treatment', parameters: [{name: 'time', range: {values: [1, 2, 5]}}]} + res: {_id: '200000000000000000000001', name: 'heat treatment', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {values: [1, 2, 5]}}]} }); }); it('supports min max ranges', done => { TestHelper.request(server, done, { method: 'put', - url: '/template/treatment/heat%20treatment', + url: '/template/treatment/200000000000000000000001', auth: {basic: 'admin'}, httpStatus: 200, req: {parameters: [{name: 'time', range: {min: 1, max: 11}}]}, - res: {_id: '200000000000000000000001', name: 'heat treatment', parameters: [{name: 'time', range: {min: 1, max: 11}}]} + res: {_id: '200000000000000000000001', name: 'heat treatment', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {min: 1, max: 11}}]} }); }); it('supports empty ranges', done => { TestHelper.request(server, done, { method: 'put', - url: '/template/treatment/heat%20treatment', + url: '/template/treatment/200000000000000000000001', auth: {basic: 'admin'}, httpStatus: 200, req: {parameters: [{name: 'time', range: {}}]}, - res: {_id: '200000000000000000000001', name: 'heat treatment', parameters: [{name: 'time', range: {}}]} + res: {_id: '200000000000000000000001', name: 'heat treatment', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {}}]} }); }); - it('adds a new template for an unknown name', done => { + it('rejects an invalid id', done => { TestHelper.request(server, done, { method: 'put', - url: '/template/treatment/heat%20aging', + url: '/template/treatment/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/treatment/000000000000000000000001', + auth: {basic: 'admin'}, + httpStatus: 404, + req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]} + }); + }); + it('rejects already existing number prefixes', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/template/treatment/200000000000000000000001', + auth: {basic: 'admin'}, + httpStatus: 400, + req: {number_prefix: 'B', parameters: [{name: 'time', range: {min: 1}}]}, + res: {status: 'Number prefix already taken'} + }); + }); + it('rejects an API key', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/template/treatment/200000000000000000000001', + auth: {key: 'admin'}, + httpStatus: 401, + req: {} + }); + }); + it('rejects requests from a write user', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/template/treatment/200000000000000000000001', + auth: {basic: 'janedoe'}, + httpStatus: 403, + req: {} + }); + }); + it('rejects unauthorized requests', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/template/treatment/200000000000000000000001', + httpStatus: 401, + req: {} + }); + }); + }); + + describe('POST /template/treatment/new', () => { + it('returns the right treatment template', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/template/treatment/200000000000000000000001', + auth: {basic: 'admin'}, + httpStatus: 200, + req: {}, + res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, number_prefix: 'A', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]} + }); + }); + it('stores the template', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/template/treatment/200000000000000000000001', auth: {basic: 'admin'}, httpStatus: 200, req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]} - }).end(err => { + }).end((err, res) => { if (err) return done(err); + should(res.body).be.eql({_id: '200000000000000000000001', name: 'heat aging', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {min: 1}}]}); TemplateTreatmentModel.find({name: 'heat aging'}).lean().exec((err, data:any) => { if (err) return done(err); should(data).have.lengthOf(1); - should(data[0]).have.only.keys('_id', 'name', 'parameters', '__v'); + should(data[0]).have.only.keys('_id', 'name', 'version', 'number_prefix', 'parameters', '__v'); should(data[0]).have.property('name', 'heat aging'); + should(data[0]).have.property('version', 2); + should(data[0]).have.property('number_prefix', 'A'); + should(data[0]).have.property('parameters').have.lengthOf(1); should(data[0].parameters[0]).have.property('name', 'time'); should(data[0].parameters[0]).have.property('range'); should(data[0].parameters[0].range).have.property('min', 1); @@ -182,7 +269,7 @@ describe('/template', () => { }); }); }); - it('rejects a missing name for a new name', done => { + it('rejects a missing name', done => { TestHelper.request(server, done, { method: 'put', url: '/template/treatment/heat%20aging', @@ -192,7 +279,7 @@ describe('/template', () => { res: {status: 'Invalid body format', details: '"name" is required'} }); }); - it('rejects missing parameters for a new name', done => { + it('rejects missing parameters', done => { TestHelper.request(server, done, { method: 'put', url: '/template/treatment/heat%20aging', @@ -202,7 +289,7 @@ describe('/template', () => { res: {status: 'Invalid body format', details: '"parameters" is required'} }); }); - it('rejects a missing parameter name for a new name', done => { + it('rejects a missing parameter name', done => { TestHelper.request(server, done, { method: 'put', url: '/template/treatment/heat%20aging', @@ -212,7 +299,7 @@ describe('/template', () => { res: {status: 'Invalid body format', details: '"parameters[0].name" is required'} }); }); - it('rejects a missing parameter range for a new name', done => { + it('rejects a missing parameter range', done => { TestHelper.request(server, done, { method: 'put', url: '/template/treatment/heat%20aging', @@ -222,7 +309,7 @@ describe('/template', () => { res: {status: 'Invalid body format', details: '"parameters[0].range" is required'} }); }); - it('rejects a an invalid parameter range property for a new name', done => { + it('rejects a an invalid parameter range property', done => { TestHelper.request(server, done, { method: 'put', url: '/template/treatment/heat%20aging', @@ -232,16 +319,6 @@ describe('/template', () => { res: {status: 'Invalid body format', details: '"parameters[0].range.xx" is not allowed'} }); }); - it('rejects already existing names', done => { - TestHelper.request(server, done, { - method: 'put', - url: '/template/treatment/heat%20treatment', - auth: {basic: 'admin'}, - httpStatus: 400, - req: {name: 'heat treatment 2', parameters: [{name: 'time', range: {min: 1}}]}, - res: {status: 'Template name already taken'} - }); - }); it('rejects wrong properties', done => { TestHelper.request(server, done, { method: 'put', @@ -252,83 +329,6 @@ describe('/template', () => { res: {status: 'Invalid body format', details: '"name" is required'} }); }); - it('rejects an API key', done => { - TestHelper.request(server, done, { - method: 'put', - url: '/template/treatment/heat%20treatment', - auth: {key: 'admin'}, - httpStatus: 401, - req: {} - }); - }); - it('rejects requests from a write user', done => { - TestHelper.request(server, done, { - method: 'put', - url: '/template/treatment/heat%20treatment', - auth: {basic: 'janedoe'}, - httpStatus: 403, - req: {} - }); - }); - it('rejects unauthorized requests', done => { - TestHelper.request(server, done, { - method: 'put', - url: '/template/treatment/heat%20treatment', - httpStatus: 401, - req: {} - }); - }); - }); - - describe('DELETE /template/treatment/{name}', () => { - it('deletes the template', done => { - TestHelper.request(server, done, { - method: 'delete', - url: '/template/treatment/heat%20treatment', - auth: {basic: 'admin'}, - httpStatus: 200 - }).end((err, res) => { - if (err) return done(err); - should(res.body).be.eql({status: 'OK'}); - TemplateTreatmentModel.find({name: 'heat treatment'}).lean().exec((err, data:any) => { - if (err) return done(err); - should(data).have.lengthOf(0); - done(); - }); - }); - }); - it('rejects deleting a template still in use'); - it('rejects an API key', done => { - TestHelper.request(server, done, { - method: 'delete', - url: '/template/treatment/heat%20treatment', - auth: {key: 'admin'}, - httpStatus: 401 - }); - }); - it('rejects requests from a write user', done => { - TestHelper.request(server, done, { - method: 'delete', - url: '/template/treatment/heat%20treatment', - auth: {basic: 'janedoe'}, - httpStatus: 403 - }) - }); - it('returns 404 for an unknown name', done => { - TestHelper.request(server, done, { - method: 'delete', - url: '/template/treatment/xxx', - auth: {basic: 'admin'}, - httpStatus: 404 - }) - }); - it('rejects unauthorized requests', done => { - TestHelper.request(server, done, { - method: 'delete', - url: '/template/treatment/heat%20treatment', - httpStatus: 401 - }) - }); }); }); @@ -603,56 +603,5 @@ describe('/template', () => { }); }); }); - - describe('DELETE /template/measurement/{name}', () => { - it('deletes the template', done => { - TestHelper.request(server, done, { - method: 'delete', - url: '/template/measurement/spectrum', - auth: {basic: 'admin'}, - httpStatus: 200 - }).end((err, res) => { - if (err) return done(err); - should(res.body).be.eql({status: 'OK'}); - TemplateMeasurementModel.find({name: 'spectrum'}).lean().exec((err, data:any) => { - if (err) return done(err); - should(data).have.lengthOf(0); - done(); - }); - }); - }); - it('rejects deleting a template still in use'); - it('rejects an API key', done => { - TestHelper.request(server, done, { - method: 'delete', - url: '/template/measurement/spectrum', - auth: {key: 'admin'}, - httpStatus: 401 - }); - }); - it('rejects requests from a write user', done => { - TestHelper.request(server, done, { - method: 'delete', - url: '/template/measurement/spectrum', - auth: {basic: 'janedoe'}, - httpStatus: 403 - }) - }); - it('returns 404 for an unknown name', done => { - TestHelper.request(server, done, { - method: 'delete', - url: '/template/measurement/xxx', - auth: {basic: 'admin'}, - httpStatus: 404 - }) - }); - it('rejects unauthorized requests', done => { - TestHelper.request(server, done, { - method: 'delete', - url: '/template/measurement/spectrum', - httpStatus: 401 - }) - }); - }); }); }); \ No newline at end of file diff --git a/src/routes/template.ts b/src/routes/template.ts index 5f1477c..55088f9 100644 --- a/src/routes/template.ts +++ b/src/routes/template.ts @@ -71,20 +71,20 @@ router.put('/template/:collection(measurement|treatment)/:name', (req, res, next }); }); -router.delete('/template/:collection(measurement|treatment)/:name', (req, res, next) => { - if (!req.auth(res, ['maintain', 'admin'], 'basic')) return; - - (req.params.collection === 'treatment' ? TemplateTreatmentModel : TemplateMeasurementModel) - .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.delete('/template/:collection(measurement|treatment)/:name', (req, res, next) => { +// if (!req.auth(res, ['maintain', 'admin'], 'basic')) return; +// +// (req.params.collection === 'treatment' ? TemplateTreatmentModel : TemplateMeasurementModel) +// .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'}); +// } +// }); +// }); module.exports = router; \ No newline at end of file diff --git a/src/test/db.json b/src/test/db.json index 6ac2156..619fb75 100644 --- a/src/test/db.json +++ b/src/test/db.json @@ -308,6 +308,8 @@ { "_id": {"$oid":"200000000000000000000001"}, "name": "heat treatment", + "version": 1, + "number_prefix": "A", "parameters": [ { "name": "material", @@ -331,6 +333,8 @@ { "_id": {"$oid":"200000000000000000000002"}, "name": "heat treatment 2", + "version": 2, + "number_prefix": "B", "parameters": [ { "name": "material", @@ -344,6 +348,7 @@ { "_id": {"$oid":"300000000000000000000001"}, "name": "spectrum", + "version": 1, "parameters": [ { "name": "dpt", @@ -357,6 +362,7 @@ { "_id": {"$oid":"300000000000000000000002"}, "name": "kf", + "version": 2, "parameters": [ { "name": "weight %", @@ -378,6 +384,7 @@ { "_id": {"$oid":"300000000000000000000003"}, "name": "mt 3", + "version": 1, "parameters": [ { "name": "val1",