-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
184 lines (153 loc) · 4.97 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
'use strict';
const Hoek = require('@hapi/hoek');
const Boom = require('@hapi/boom');
/**
* Check if the error is a duplicate key error and reply with a sensible error
* @throws {Error} If the received error isnt a duplicate key error
* @return {Function}
*/
function duplicateKeyError() {
return function (err) {
const dup = /^E11000.*index:((.*\$|\s+)?([^\s]+))/.exec(err.message);
if (dup) {
throw Boom.conflict('Duplicate key error: ' + dup[3]);
}
throw err;
};
}
/**
* If the result is empty or NotFoundException reply with NotFound else reply with the result
* @param {Hapi.Toolkit} h
* @param {Integer} [code] http status code for success
* @return {Function}
*/
function notFoundReply(h, code) {
return (result) => {
if (!result || result instanceof NotFoundException) {
throw Boom.notFound();
}
if (code !== undefined) {
h.response(result).code(code);
}
return result;
};
}
/**
* Reply with 400 Bad Request when validation fails
* @param {Error} err
*/
function validationError(err) {
if (!err.name || err.name !== 'ValidationError') {
throw err;
}
const errors = Object.keys(err.errors).map((key) => {
return {field: err.errors[key].path, message: err.errors[key].message};
});
throw Boom.badRequest('Validation failed: ' + errors[0].message, {_expose: errors});
}
function mongoError(err) {
let message = 'Error saving to database';
if (err.message.match(/must not start with/)) {
message = err.message;
throw Boom.badRequest(message);
}
if (err.code === 11000) {
throw Boom.conflict('Duplicate key');
}
throw Boom.internal(message);
}
/**
* Throws NotFoundException if passed response has n < 1 or response is empty.
* @param {Object} res response from mongodb.update or Mongoose.remove
* @return {Object} res
*/
function notFoundExceptionIfNoMatch(res) {
if (!res) {
throw new NotFoundException('Empty database result');
}
// support mixed types of results
let n;
if (res.n !== undefined) {
n = res.n;
}
if (res.result?.n !== undefined) {
n = res.result.n;
}
if (res.matchedCount !== undefined) {
n = res.matchedCount;
}
// n is the number of matched document
// more info on response here https://docs.mongodb.org/manual/reference/command/update/#update-command-output
if (n < 1) {
throw new NotFoundException('No document matched for update');
}
return res;
}
/**
* Throws NotFoundException if passed data is falsy or empty if its an array
* @param {Array|Object} data]
* @throws {NotFoundException} If passed data is falsy or empty if its an array
* @return {Array|Object} returns same data that was passed.
*/
function notFoundExceptionIfEmpty(data) {
if (!data) {
throw new NotFoundException('Empty data');
}
if (data instanceof Array && !data.length) {
throw new NotFoundException('Empty data array');
}
return data;
}
function NotFoundException(message) {
this.message = message;
this.name = 'NotFoundException';
Error.captureStackTrace(this, NotFoundException);
}
NotFoundException.prototype = Object.create(Error.prototype);
NotFoundException.prototype.constructor = NotFoundException;
function connect(mongoose, server, options) {
Hoek.assert(typeof options.uri === 'string', 'Database service requires option uri.');
let dieTimeoutId;
// fix for running tests when connection isnt closed when watch and reruns tests.
if (mongoose.connection.readyState === 1 || mongoose.connection.readyState === 2) {
server.log(['mongoose', 'info'], 'Already connecting/connected..');
return Promise.resolve();
}
if (options.dieConnectTimeout) {
dieTimeoutId = dieIfNotConnected(options.dieConnectTimeout);
}
mongoose.connection.on('connected', () => {
clearTimeout(dieTimeoutId);
server.log(['mongoose', 'info', 'connected'], 'Connected to database.');
});
mongoose.connection.on('close', () => {
server.log(['mongoose', 'info'], 'Disconnected from database.');
if (options.dieConnectTimeout) {
dieTimeoutId = dieIfNotConnected(options.dieConnectTimeout);
}
});
mongoose.connection.on('error', (err) => server.log(['mongoose', 'error'], err));
const events = ['disconnecting', 'connecting', 'open', 'reconnected'];
events.forEach((event) => {
mongoose.connection.on(event, () => server.log(['mongoose', 'info', event], `Mongoose connection event: ${event}`));
});
server.log(['mongoose', 'info'], 'Connecting to database..');
return mongoose.connect(options.uri, options.options || {}).catch((err) => {
server.log(['mongoose', 'error'], err);
throw err;
});
function die() {
throw new Error('Unable to establish connection with database');
}
function dieIfNotConnected(timeout) {
return setTimeout(() => die(), timeout);
}
}
exports.duplicateKeyError = duplicateKeyError;
exports.notFoundReply = notFoundReply;
exports.validationError = validationError;
exports.mongoError = mongoError;
exports.notFoundExceptionIfEmpty = notFoundExceptionIfEmpty;
exports.NotFoundException = NotFoundException;
exports.notFoundExceptionIfNoMatch = notFoundExceptionIfNoMatch;
exports.connect = connect;