Skip to content

Commit 5f0c105

Browse files
authored
Merge pull request #15215 from Automattic/vkarpov15/gh-15202
fix(schema): improve reason for UUID cast error, currently a TypeError
2 parents 25bd7d8 + 1e8db47 commit 5f0c105

File tree

3 files changed

+83
-70
lines changed

3 files changed

+83
-70
lines changed

lib/cast/uuid.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
'use strict';
2+
3+
const MongooseBuffer = require('../types/buffer');
4+
5+
const UUID_FORMAT = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/i;
6+
const Binary = MongooseBuffer.Binary;
7+
8+
module.exports = function castUUID(value) {
9+
if (value == null) {
10+
return value;
11+
}
12+
13+
function newBuffer(initbuff) {
14+
const buff = new MongooseBuffer(initbuff);
15+
buff._subtype = 4;
16+
return buff;
17+
}
18+
19+
if (typeof value === 'string') {
20+
if (UUID_FORMAT.test(value)) {
21+
return stringToBinary(value);
22+
} else {
23+
throw new Error(`"${value}" is not a valid UUID string`);
24+
}
25+
}
26+
27+
if (Buffer.isBuffer(value)) {
28+
return newBuffer(value);
29+
}
30+
31+
if (value instanceof Binary) {
32+
return newBuffer(value.value(true));
33+
}
34+
35+
// Re: gh-647 and gh-3030, we're ok with casting using `toString()`
36+
// **unless** its the default Object.toString, because "[object Object]"
37+
// doesn't really qualify as useful data
38+
if (value.toString && value.toString !== Object.prototype.toString) {
39+
if (UUID_FORMAT.test(value.toString())) {
40+
return stringToBinary(value.toString());
41+
}
42+
}
43+
44+
throw new Error(`"${value}" cannot be casted to a UUID`);
45+
};
46+
47+
module.exports.UUID_FORMAT = UUID_FORMAT;
48+
49+
/**
50+
* Helper function to convert the input hex-string to a buffer
51+
* @param {String} hex The hex string to convert
52+
* @returns {Buffer} The hex as buffer
53+
* @api private
54+
*/
55+
56+
function hex2buffer(hex) {
57+
// use buffer built-in function to convert from hex-string to buffer
58+
const buff = hex != null && Buffer.from(hex, 'hex');
59+
return buff;
60+
}
61+
62+
/**
63+
* Convert a String to Binary
64+
* @param {String} uuidStr The value to process
65+
* @returns {MongooseBuffer} The binary to store
66+
* @api private
67+
*/
68+
69+
function stringToBinary(uuidStr) {
70+
// Protect against undefined & throwing err
71+
if (typeof uuidStr !== 'string') uuidStr = '';
72+
const hex = uuidStr.replace(/[{}-]/g, ''); // remove extra characters
73+
const bytes = hex2buffer(hex);
74+
const buff = new MongooseBuffer(bytes);
75+
buff._subtype = 4;
76+
77+
return buff;
78+
}

lib/schema/uuid.js

Lines changed: 3 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,13 @@
77
const MongooseBuffer = require('../types/buffer');
88
const SchemaType = require('../schemaType');
99
const CastError = SchemaType.CastError;
10+
const castUUID = require('../cast/uuid');
1011
const utils = require('../utils');
1112
const handleBitwiseOperator = require('./operators/bitwise');
1213

13-
const UUID_FORMAT = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/i;
14+
const UUID_FORMAT = castUUID.UUID_FORMAT;
1415
const Binary = MongooseBuffer.Binary;
1516

16-
/**
17-
* Helper function to convert the input hex-string to a buffer
18-
* @param {String} hex The hex string to convert
19-
* @returns {Buffer} The hex as buffer
20-
* @api private
21-
*/
22-
23-
function hex2buffer(hex) {
24-
// use buffer built-in function to convert from hex-string to buffer
25-
const buff = hex != null && Buffer.from(hex, 'hex');
26-
return buff;
27-
}
28-
29-
/**
30-
* Convert a String to Binary
31-
* @param {String} uuidStr The value to process
32-
* @returns {MongooseBuffer} The binary to store
33-
* @api private
34-
*/
35-
36-
function stringToBinary(uuidStr) {
37-
// Protect against undefined & throwing err
38-
if (typeof uuidStr !== 'string') uuidStr = '';
39-
const hex = uuidStr.replace(/[{}-]/g, ''); // remove extra characters
40-
const bytes = hex2buffer(hex);
41-
const buff = new MongooseBuffer(bytes);
42-
buff._subtype = 4;
43-
44-
return buff;
45-
}
46-
4717
/**
4818
* Convert binary to a uuid string
4919
* @param {Buffer|Binary|String} uuidBin The value to process
@@ -109,44 +79,7 @@ SchemaUUID.prototype.constructor = SchemaUUID;
10979
* ignore
11080
*/
11181

112-
SchemaUUID._cast = function(value) {
113-
if (value == null) {
114-
return value;
115-
}
116-
117-
function newBuffer(initbuff) {
118-
const buff = new MongooseBuffer(initbuff);
119-
buff._subtype = 4;
120-
return buff;
121-
}
122-
123-
if (typeof value === 'string') {
124-
if (UUID_FORMAT.test(value)) {
125-
return stringToBinary(value);
126-
} else {
127-
throw new CastError(SchemaUUID.schemaName, value, this.path);
128-
}
129-
}
130-
131-
if (Buffer.isBuffer(value)) {
132-
return newBuffer(value);
133-
}
134-
135-
if (value instanceof Binary) {
136-
return newBuffer(value.value(true));
137-
}
138-
139-
// Re: gh-647 and gh-3030, we're ok with casting using `toString()`
140-
// **unless** its the default Object.toString, because "[object Object]"
141-
// doesn't really qualify as useful data
142-
if (value.toString && value.toString !== Object.prototype.toString) {
143-
if (UUID_FORMAT.test(value.toString())) {
144-
return stringToBinary(value.toString());
145-
}
146-
}
147-
148-
throw new CastError(SchemaUUID.schemaName, value, this.path);
149-
};
82+
SchemaUUID._cast = castUUID;
15083

15184
/**
15285
* Attaches a getter for all UUID instances.

test/schema.uuid.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ describe('SchemaUUID', function() {
6363
const errors = res.errors;
6464
assert.strictEqual(Object.keys(errors).length, 1);
6565
assert.ok(errors.x instanceof mongoose.Error.CastError);
66+
67+
assert.ok(errors.x.reason.message.includes('not a valid UUID string'), errors.x.reason.message);
6668
});
6769

6870
it('should work with $in and $nin and $all', async function() {

0 commit comments

Comments
 (0)