From 6253904af930fb3703ab1671e01312af6f97bc6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Thu, 4 May 2017 13:26:23 +0100 Subject: [PATCH 1/8] feat: add hooks controller --- dadi/lib/controller/hooks.js | 118 +++++++++++++++++++++++++++++++++++ dadi/lib/index.js | 74 ++++++++-------------- 2 files changed, 145 insertions(+), 47 deletions(-) create mode 100644 dadi/lib/controller/hooks.js diff --git a/dadi/lib/controller/hooks.js b/dadi/lib/controller/hooks.js new file mode 100644 index 00000000..55a7e8b8 --- /dev/null +++ b/dadi/lib/controller/hooks.js @@ -0,0 +1,118 @@ +const fs = require('fs') +const path = require('path') +const help = require(path.join(__dirname, '/../help')) + +const HOOK_PREFIX = 'hook:' + +const HooksController = function (parameters) { + this.components = parameters.components + this.docs = parameters.docs + this.path = parameters.path +} + +HooksController.prototype._findHooks = function (filterByName) { + let hooks = [] + + Object.keys(this.components).find(key => { + if (key.indexOf(HOOK_PREFIX) === 0) { + const hookName = key.replace(HOOK_PREFIX, '') + + if (filterByName && filterByName !== hookName) { + return + } + + hooks.push(hookName) + + if (filterByName) { + return true + } + } + }) + + return hooks.sort() +} + +HooksController.prototype._writeHook = function (name, content) { + const filePath = path.join(this.path, name + '.js') + + return new Promise((resolve, reject) => { + fs.writeFile(filePath, content, err => { + if (err) return reject(err) + + resolve() + }) + }) +} + +HooksController.prototype.get = function (req, res, next) { + // Return the content of a specific hook + if (req.params.hookName) { + const name = req.params.hookName + const hook = this._findHooks(name)[0] + + if (!hook) { + return help.sendBackText(404, res, next)(null, '') + } + + const content = fs.readFileSync(this.components[HOOK_PREFIX + name]) + + return help.sendBackText(200, res, next)(null, content.toString()) + } else { + // List all hooks + const hooks = this._findHooks().map(key => { + let hook = { + name: key + } + + const docs = this.docs[HOOK_PREFIX + key] + + if (docs && docs[0]) { + hook.description = docs[0].description + hook.params = docs[0].params + hook.returns = docs[0].returns + } + + return hook + }) + + const data = { + hooks: hooks + } + + return help.sendBackJSON(200, res, next)(null, data) + } +} + +HooksController.prototype.post = function (req, res, next) { + const name = req.params.hookName + const hook = this._findHooks(name)[0] + + if (hook) { + return help.sendBackJSON(409, res, next)(null, { + err: 'Hook already exists' + }) + } + + return this._writeHook(name, req.body).then(() => { + return help.sendBackText(200, res, next)(null, '') + }).catch(err => { + return help.sendBackText(200, res, next)(err, '') + }) +} + +HooksController.prototype.put = function (req, res, next) { + const name = req.params.hookName + const hook = this._findHooks(name)[0] + + if (!hook) { + return help.sendBackText(404, res, next)(null, '') + } + + return this._writeHook(name, req.body).then(() => { + return help.sendBackText(200, res, next)(null, '') + }).catch(err => { + return help.sendBackText(200, res, next)(err, '') + }) +} + +module.exports = HooksController diff --git a/dadi/lib/index.js b/dadi/lib/index.js index c13a9bf4..b91997c9 100755 --- a/dadi/lib/index.js +++ b/dadi/lib/index.js @@ -21,6 +21,7 @@ var api = require(path.join(__dirname, '/api')) var auth = require(path.join(__dirname, '/auth')) var cache = require(path.join(__dirname, '/cache')) var Controller = require(path.join(__dirname, '/controller')) +var HooksController = require(path.join(__dirname, '/controller/hooks')) var MediaController = require(path.join(__dirname, '/controller/media')) var dadiStatus = require('@dadi/status') var help = require(path.join(__dirname, '/help')) @@ -214,7 +215,7 @@ Server.prototype.start = function (done) { this.loadCollectionRoute() this.loadEndpointsRoute() - this.loadHooksRoute() + this.loadHooksRoute(options) this.readyState = 1 @@ -379,6 +380,10 @@ Server.prototype.loadConfigApi = function () { // handler if required. if (url.parse(req.url).pathname.indexOf('endpoints') > 0) return next() + // the hooks config endpoint also shares this structure, so if the + // URL starts with /api, we move on to the next handler. + if (req.params.version === 'api') return next() + var method = req.method && req.method.toLowerCase() if (method !== 'post') return next() @@ -599,54 +604,31 @@ Server.prototype.loadEndpointsRoute = function () { } // route to retrieve list of available hooks -Server.prototype.loadHooksRoute = function () { - var self = this - - this.app.use('/api/hooks', function (req, res, next) { - var method = req.method && req.method.toLowerCase() - if (method !== 'get') return help.sendBackJSON(400, res, next)(null, {'error': 'Invalid method'}) - - var data = {} - var hooks = [] - - _.each(self.components, function (value, key) { - if (key.indexOf('hook:') === 0) { - var hook = { - name: key.replace('hook:', '') - } - - var docs = self.docs[key] - if (docs && docs[0]) { - hook.description = docs[0].description - hook.params = docs[0].params - hook.returns = docs[0].returns - } +Server.prototype.loadHooksRoute = function (options) { + const hooksController = new HooksController({ + components: this.components, + docs: this.docs, + path: options.hookPath + }) - hooks.push(hook) - } - }) + this.app.use('/api/hooks', (req, res, next) => { + const method = req.method && req.method.toLowerCase() - data.hooks = _.sortBy(hooks, 'name') + if (method === 'get') { + return hooksController[method](req, res, next) + } - return help.sendBackJSON(200, res, next)(null, data) + return help.sendBackJSON(400, res, next)(null, {'error': 'Invalid method'}) }) - this.app.use('/api/hooks/:hook/config', function (req, res, next) { - var method = req.method && req.method.toLowerCase() - if (method !== 'get') return help.sendBackJSON(400, res, next)(null, {'error': 'Invalid method'}) - - _.each(self.components, function (value, key) { - if (key.indexOf('hook:') === 0) { - var hook = key.replace('hook:', '') + this.app.use('/api/hooks/:hookName/config', (req, res, next) => { + const method = req.method && req.method.toLowerCase() - if (hook === req.params.hook) { - var content = fs.readFileSync(value) - return help.sendBackText(200, res, next)(null, content.toString()) - } - } - }) + if (typeof hooksController[method] === 'function') { + return hooksController[method](req, res, next) + } - return help.sendBackJSON(404, res, next)(null, {}) + return help.sendBackJSON(400, res, next)(null, {'error': 'Invalid method'}) }) } @@ -941,12 +923,10 @@ Server.prototype.addHook = function (options) { try { opts.component = require(filepath) - } catch (e) { + } catch (err) { // if file was removed "un-use" this component - if (e && e.code === 'ENOENT') { - self.removeMonitor(filepath) - self.removeComponent(opts.route) - } + self.removeMonitor(filepath) + self.removeComponent(opts.route) } }) From c542789635691e58b4375947cb9bd2331cb5f96b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Thu, 4 May 2017 13:29:47 +0100 Subject: [PATCH 2/8] test: add acceptance tests for hooks endpoint --- config/config.test.json | 89 +++++++++++++++++++++++ test/acceptance/app.js | 153 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 config/config.test.json diff --git a/config/config.test.json b/config/config.test.json new file mode 100644 index 00000000..f913e8f2 --- /dev/null +++ b/config/config.test.json @@ -0,0 +1,89 @@ +{ + "app": { + "name": "Repo Default" + }, + "server": { + "host": "127.0.0.1", + "port": 8000 + }, + "database": { + "hosts": [ + { + "host": "127.0.0.1", + "port": 27017 + } + ], + "username": "", + "password": "", + "database": "test", + "ssl": false, + "replicaSet": "", + "enableCollectionDatabases": false, + "secondary": { + "hosts": [ + { + "host": "127.0.0.1", + "port": 27018 + } + ], + "username": "", + "password": "", + "replicaSet": "", + "ssl": false + }, + "testdb": { + "hosts": [ + { + "host": "127.0.0.1", + "port": 27017 + } + ], + "username": "", + "password": "" + } + }, + "auth": { + "tokenUrl": "/token", + "tokenTtl": 1800, + "database": { + "hosts": [ + { + "host": "127.0.0.1", + "port": 27017 + } + ], + "username": "", + "password": "", + "database": "test" + }, + "clientCollection": "clientStore", + "tokenCollection": "tokenStore" + }, + "caching": { + "ttl": 300, + "directory": { + "enabled": true, + "path": "./cache/api", + "extension": "json" + }, + "redis": { + "enabled": false, + "host": "127.0.0.1", + "port": 6379 + } + }, + "paths": { + "collections": "test/acceptance/workspace/collections", + "endpoints": "test/acceptance/workspace/endpoints", + "hooks": "test/acceptance/workspace/hooks" + }, + "logging": { + "enabled": true, + "level": "info", + "path": "./log", + "filename": "dadi-api", + "extension": "log" + }, + "feedback": false, + "cors": false +} \ No newline at end of file diff --git a/test/acceptance/app.js b/test/acceptance/app.js index 14db43e9..270c12c7 100755 --- a/test/acceptance/app.js +++ b/test/acceptance/app.js @@ -3223,7 +3223,7 @@ describe('Application', function () { it('should return 400 if request method is not supported', function (done) { request(connectionString) - .put('/api/hooks/xx/config') + .put('/api/hooks') .set('Authorization', 'Bearer ' + bearerToken) .expect(400, done) }) @@ -3245,6 +3245,157 @@ describe('Application', function () { done() }) }) + + it('should create a hook with a POST request', function (done) { + const hookName = 'myHook1' + const hookContent = ` + module.exports = (obj, type, data) => { + return obj + } + `.trim() + + request(connectionString) + .post(`/api/hooks/${hookName}/config`) + .send(hookContent) + .set('content-type', 'text/plain') + .set('Authorization', 'Bearer ' + bearerToken) + .end(function (err, res) { + res.statusCode.should.eql(200) + res.text.should.eql('') + + setTimeout(() => { + request(connectionString) + .get(`/api/hooks/${hookName}/config`) + .set('Authorization', 'Bearer ' + bearerToken) + .end((err, res) => { + res.statusCode.should.eql(200) + res.text.should.eql(hookContent) + + const hooksPath = config.get('paths.hooks') + + // Deleting hook file + fs.unlinkSync(path.join(hooksPath, `${hookName}.js`)) + + // Give it some time for the monitor to kick in + setTimeout(() => { + done() + }, 200) + }) + }, 200) + }) + }) + + it('should return 409 when sending a POST request to a hook that already exists', function (done) { + const hookName = 'myHook1' + const hookContent = ` + module.exports = (obj, type, data) => { + return obj + } + `.trim() + + request(connectionString) + .post(`/api/hooks/${hookName}/config`) + .send(hookContent) + .set('content-type', 'text/plain') + .set('Authorization', 'Bearer ' + bearerToken) + .end(function (err, res) { + setTimeout(() => { + request(connectionString) + .post(`/api/hooks/${hookName}/config`) + .send(hookContent) + .set('content-type', 'text/plain') + .set('Authorization', 'Bearer ' + bearerToken) + .end((err, res) => { + res.statusCode.should.eql(409) + + const hooksPath = config.get('paths.hooks') + + // Deleting hook file + fs.unlinkSync(path.join(hooksPath, `${hookName}.js`)) + + // Give it some time for the monitor to kick in + setTimeout(() => { + done() + }, 200) + }) + }, 200) + }) + }) + + it('should update a hook with a PUT request', function (done) { + const hookName = 'myHook1' + const hookOriginalContent = ` + module.exports = (obj, type, data) => { + return obj + } + `.trim() + const hookUpdatedContent = ` + module.exports = (obj, type, data) => { + obj = 'Something else' + + return obj + } + `.trim() + + request(connectionString) + .post(`/api/hooks/${hookName}/config`) + .send(hookOriginalContent) + .set('content-type', 'text/plain') + .set('Authorization', 'Bearer ' + bearerToken) + .end((err, res) => { + setTimeout(() => { + request(connectionString) + .put(`/api/hooks/${hookName}/config`) + .set('content-type', 'text/plain') + .send(hookUpdatedContent) + .set('Authorization', 'Bearer ' + bearerToken) + .end((err, res) => { + res.statusCode.should.eql(200) + res.text.should.eql('') + + setTimeout(() => { + request(connectionString) + .get(`/api/hooks/${hookName}/config`) + .set('Authorization', 'Bearer ' + bearerToken) + .end((err, res) => { + res.statusCode.should.eql(200) + res.text.should.eql(hookUpdatedContent) + + const hooksPath = config.get('paths.hooks') + + // Deleting hook file + fs.unlinkSync(path.join(hooksPath, `${hookName}.js`)) + + // Give it some time for the monitor to kick in + setTimeout(() => { + done() + }, 200) + }) + }, 200) + }) + }, 200) + }) + }) + + it('should return 404 when sending a PUT request to a hook that does not exist', function (done) { + const hookName = 'myHook1' + const hookUpdatedContent = ` + module.exports = (obj, type, data) => { + return obj + } + `.trim() + + request(connectionString) + .put(`/api/hooks/${hookName}/config`) + .send(hookUpdatedContent) + .set('content-type', 'text/plain') + .set('Authorization', 'Bearer ' + bearerToken) + .end(function (err, res) { + res.statusCode.should.eql(404) + + done() + }) + }) }) describe('config api', function () { From 3aa266a88a4bf6375627f9386178e34da1c396f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Thu, 4 May 2017 13:31:36 +0100 Subject: [PATCH 3/8] test: update sample test config --- config/config.test.json | 89 ---------------------------------- config/config.test.json.sample | 3 +- 2 files changed, 2 insertions(+), 90 deletions(-) delete mode 100644 config/config.test.json diff --git a/config/config.test.json b/config/config.test.json deleted file mode 100644 index f913e8f2..00000000 --- a/config/config.test.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "app": { - "name": "Repo Default" - }, - "server": { - "host": "127.0.0.1", - "port": 8000 - }, - "database": { - "hosts": [ - { - "host": "127.0.0.1", - "port": 27017 - } - ], - "username": "", - "password": "", - "database": "test", - "ssl": false, - "replicaSet": "", - "enableCollectionDatabases": false, - "secondary": { - "hosts": [ - { - "host": "127.0.0.1", - "port": 27018 - } - ], - "username": "", - "password": "", - "replicaSet": "", - "ssl": false - }, - "testdb": { - "hosts": [ - { - "host": "127.0.0.1", - "port": 27017 - } - ], - "username": "", - "password": "" - } - }, - "auth": { - "tokenUrl": "/token", - "tokenTtl": 1800, - "database": { - "hosts": [ - { - "host": "127.0.0.1", - "port": 27017 - } - ], - "username": "", - "password": "", - "database": "test" - }, - "clientCollection": "clientStore", - "tokenCollection": "tokenStore" - }, - "caching": { - "ttl": 300, - "directory": { - "enabled": true, - "path": "./cache/api", - "extension": "json" - }, - "redis": { - "enabled": false, - "host": "127.0.0.1", - "port": 6379 - } - }, - "paths": { - "collections": "test/acceptance/workspace/collections", - "endpoints": "test/acceptance/workspace/endpoints", - "hooks": "test/acceptance/workspace/hooks" - }, - "logging": { - "enabled": true, - "level": "info", - "path": "./log", - "filename": "dadi-api", - "extension": "log" - }, - "feedback": false, - "cors": false -} \ No newline at end of file diff --git a/config/config.test.json.sample b/config/config.test.json.sample index 49b8f043..4fc67cf5 100755 --- a/config/config.test.json.sample +++ b/config/config.test.json.sample @@ -74,7 +74,8 @@ }, "paths": { "collections": "test/acceptance/workspace/collections", - "endpoints": "test/acceptance/workspace/endpoints" + "endpoints": "test/acceptance/workspace/endpoints", + "hooks": "test/acceptance/workspace/hooks" }, "logging": { "enabled": true, From 302f23d727340606ea2ba1ce97fe4f317c3ef57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Thu, 4 May 2017 13:39:08 +0100 Subject: [PATCH 4/8] refactor: make hooks read file asynchronous --- dadi/lib/controller/hooks.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dadi/lib/controller/hooks.js b/dadi/lib/controller/hooks.js index 55a7e8b8..fd3c52ad 100644 --- a/dadi/lib/controller/hooks.js +++ b/dadi/lib/controller/hooks.js @@ -54,9 +54,9 @@ HooksController.prototype.get = function (req, res, next) { return help.sendBackText(404, res, next)(null, '') } - const content = fs.readFileSync(this.components[HOOK_PREFIX + name]) - - return help.sendBackText(200, res, next)(null, content.toString()) + fs.readFile(this.components[HOOK_PREFIX + name], (err, content) => { + return help.sendBackText(200, res, next)(null, content.toString()) + }) } else { // List all hooks const hooks = this._findHooks().map(key => { From 499b4cb0925e23534ae850afe9afad15094248d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Thu, 4 May 2017 13:54:10 +0100 Subject: [PATCH 5/8] feat: add DELETE method to hooks endpoint --- dadi/lib/controller/hooks.js | 29 ++++++++++++++++++++- test/acceptance/app.js | 50 ++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/dadi/lib/controller/hooks.js b/dadi/lib/controller/hooks.js index fd3c52ad..9fdb8528 100644 --- a/dadi/lib/controller/hooks.js +++ b/dadi/lib/controller/hooks.js @@ -10,6 +10,18 @@ const HooksController = function (parameters) { this.path = parameters.path } +HooksController.prototype._deleteHook = function (name) { + const filePath = path.join(this.path, name + '.js') + + return new Promise((resolve, reject) => { + fs.unlink(filePath, err => { + if (err) return reject(err) + + resolve() + }) + }) +} + HooksController.prototype._findHooks = function (filterByName) { let hooks = [] @@ -44,6 +56,21 @@ HooksController.prototype._writeHook = function (name, content) { }) } +HooksController.prototype.delete = function (req, res, next) { + const name = req.params.hookName + const hook = this._findHooks(name)[0] + + if (!hook) { + return help.sendBackText(404, res, next)(null, '') + } + + this._deleteHook(name).then(() => { + return help.sendBackText(200, res, next)(null, '') + }).catch(err => { + return help.sendBackText(200, res, next)(err, '') + }) +} + HooksController.prototype.get = function (req, res, next) { // Return the content of a specific hook if (req.params.hookName) { @@ -55,7 +82,7 @@ HooksController.prototype.get = function (req, res, next) { } fs.readFile(this.components[HOOK_PREFIX + name], (err, content) => { - return help.sendBackText(200, res, next)(null, content.toString()) + return help.sendBackText(200, res, next)(err, content.toString()) }) } else { // List all hooks diff --git a/test/acceptance/app.js b/test/acceptance/app.js index 270c12c7..4bbc64e9 100755 --- a/test/acceptance/app.js +++ b/test/acceptance/app.js @@ -3396,6 +3396,56 @@ describe('Application', function () { done() }) }) + + it('should delete a hook with a DELETE request', function (done) { + const hookName = 'myHook1' + const hookContent = ` + module.exports = (obj, type, data) => { + return obj + } + `.trim() + + request(connectionString) + .post(`/api/hooks/${hookName}/config`) + .send(hookContent) + .set('content-type', 'text/plain') + .set('Authorization', 'Bearer ' + bearerToken) + .end((err, res) => { + setTimeout(() => { + request(connectionString) + .delete(`/api/hooks/${hookName}/config`) + .set('Authorization', 'Bearer ' + bearerToken) + .end((err, res) => { + res.statusCode.should.eql(200) + res.text.should.eql('') + + setTimeout(() => { + request(connectionString) + .get(`/api/hooks/${hookName}/config`) + .set('Authorization', 'Bearer ' + bearerToken) + .end((err, res) => { + res.statusCode.should.eql(404) + + done() + }) + }, 200) + }) + }, 200) + }) + }) + + it('should return 404 when sending a DELETE request to a hook that does not exist', function (done) { + const hookName = 'myHook1' + + request(connectionString) + .delete(`/api/hooks/${hookName}/config`) + .set('Authorization', 'Bearer ' + bearerToken) + .end(function (err, res) { + res.statusCode.should.eql(404) + + done() + }) + }) }) describe('config api', function () { From 0d7c0dbda07c887a90ac5a3cdcd23e132d6f825e Mon Sep 17 00:00:00 2001 From: James Lambie Date: Fri, 5 May 2017 10:20:11 +1200 Subject: [PATCH 6/8] refactor: add strict mode --- dadi/lib/controller/hooks.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dadi/lib/controller/hooks.js b/dadi/lib/controller/hooks.js index 9fdb8528..1ee9d4d7 100644 --- a/dadi/lib/controller/hooks.js +++ b/dadi/lib/controller/hooks.js @@ -1,3 +1,5 @@ +'use strict' + const fs = require('fs') const path = require('path') const help = require(path.join(__dirname, '/../help')) From 4f636f3456b103592bdcfcab7579e7d45412fb58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Tue, 9 May 2017 11:42:49 +0100 Subject: [PATCH 7/8] refactor: add 405 status code --- dadi/lib/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dadi/lib/index.js b/dadi/lib/index.js index b91997c9..92870529 100755 --- a/dadi/lib/index.js +++ b/dadi/lib/index.js @@ -481,7 +481,7 @@ Server.prototype.loadCollectionRoute = function () { this.app.use('/api/collections', function (req, res, next) { var method = req.method && req.method.toLowerCase() - if (method !== 'get') return help.sendBackJSON(400, res, next)(null, {'error': 'Invalid method'}) + if (method !== 'get') return help.sendBackJSON(405, res, next)(null, {'error': 'Invalid method'}) var data = {} var collections = [] @@ -562,7 +562,7 @@ Server.prototype.loadEndpointsRoute = function () { this.app.use('/api/endpoints', function (req, res, next) { var method = req.method && req.method.toLowerCase() - if (method !== 'get') return help.sendBackJSON(400, res, next)(null, {'error': 'Invalid method'}) + if (method !== 'get') return help.sendBackJSON(405, res, next)(null, {'error': 'Invalid method'}) var data = {} var endpoints = [] @@ -618,7 +618,7 @@ Server.prototype.loadHooksRoute = function (options) { return hooksController[method](req, res, next) } - return help.sendBackJSON(400, res, next)(null, {'error': 'Invalid method'}) + return help.sendBackJSON(405, res, next)(null, {'error': 'Invalid method'}) }) this.app.use('/api/hooks/:hookName/config', (req, res, next) => { @@ -628,7 +628,7 @@ Server.prototype.loadHooksRoute = function (options) { return hooksController[method](req, res, next) } - return help.sendBackJSON(400, res, next)(null, {'error': 'Invalid method'}) + return help.sendBackJSON(405, res, next)(null, {'error': 'Invalid method'}) }) } From 7eec88048ef58d348723197a48bed20f3195b685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Tue, 9 May 2017 11:43:17 +0100 Subject: [PATCH 8/8] test: update test suite to look for 405 status code --- test/acceptance/app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/acceptance/app.js b/test/acceptance/app.js index 4bbc64e9..89361af9 100755 --- a/test/acceptance/app.js +++ b/test/acceptance/app.js @@ -3221,11 +3221,11 @@ describe('Application', function () { }) }) - it('should return 400 if request method is not supported', function (done) { + it('should return 405 if request method is not supported', function (done) { request(connectionString) .put('/api/hooks') .set('Authorization', 'Bearer ' + bearerToken) - .expect(400, done) + .expect(405, done) }) it('should return 404 if specified hook is not found', function (done) {