forked from stephenplusplus/gcloud-datastore-schema
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
136 lines (110 loc) · 3.5 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
'use strict'
var arrify = require('arrify')
var is = require('is')
/* eslint-disable no-undef, semi, indent */
var customTypes = {
Double: function (value) {
return new entity.Double(value);
}.toString(),
GeoPoint: function (coordindates) {
return new entity.GeoPoint(coordindates);
}.toString(),
Int: function (value) {
return new entity.Int(value);
}.toString()
}
/* eslint-enable no-undef, semi, indent */
var getKind = function (key) {
if (key.kind) return key.kind
var path = key.path
if (path.length % 2 === 0) {
return path[path.length - 2]
} else {
return path[path.length - 1]
}
}
var validate = function (schema, data) {
var errorMessages = []
var dataProperties = Object.keys(data)
for (var prop in schema) {
var validator = schema[prop]
var value = data[prop]
if (dataProperties.indexOf(prop) > -1) {
dataProperties.splice(dataProperties.indexOf(prop), 1)
}
// Is nested schema?
if (is.object(validator)) {
validate(validator, value)
continue
}
if (!validator || !is.fn(validator)) {
errorMessages.push('Schema definition not found for property: ' + prop)
continue
}
if (!is.defined(value)) {
errorMessages.push('Schema definition expected property: ' + prop)
continue
}
// Is it a native type?
if (validator === global[validator.name]) {
if (!is.type(data[prop], validator.name.toLowerCase())) {
// Some types don't exist on `is`, e.g. Buffer. One last check:
if (!(value instanceof global[validator.name])) {
errorMessages.push([
'Schema definition violated for property: "' + prop + '".',
'Expected type: ' + validator.name + ', received: ' + JSON.stringify(value)
].join(' '))
}
}
continue
}
// Is it a custom Datastore type?
for (var customTypeName in customTypes) {
var customTypeStringified = customTypes[customTypeName]
if (validator.toString() === customTypeStringified) {
if (value.constructor.name !== customTypeName) {
errorMessages.push([
'Schema definition violated for property: "' + prop + '".',
'Expected type: ' + customTypeName + ', received: ' + value.constructor.name
].join(' '))
}
continue
}
}
// Assume it's a custom validator function
if (!validator(value)) {
errorMessages.push('Schema definition violated for property: ' + prop)
continue
}
}
if (dataProperties.length > 0) {
errorMessages.push('Unexpected properties found: ' + dataProperties.join(', '))
}
return errorMessages
}
module.exports = function (dataset) {
var schemas = {}
dataset.register = function (kind, schema) {
schemas[kind] = schema
}
var save = dataset.save
dataset.save = function (entities, callback) {
var schemaValidationError = new Error('Schema validation failed')
schemaValidationError.code = 'ESCHEMAVIOLATION'
schemaValidationError.errors = []
arrify(entities).forEach(function (entity) {
var kind = getKind(entity.key)
if (!schemas[kind]) return
var validationFailureMessages = validate(schemas[kind], entity.data)
if (validationFailureMessages.length > 0) {
schemaValidationError.errors.push({
kind: kind,
errors: validationFailureMessages
})
}
})
if (schemaValidationError.errors.length > 0) callback(schemaValidationError)
else save.apply(dataset, arguments)
}
return dataset
}