@azuliani/node-service 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Client/Client.js +246 -0
- package/Client/MonitoredSocket.js +61 -0
- package/Client/PullClient.js +28 -0
- package/Client/RPCClient.js +83 -0
- package/Client/SharedObjectClient.js +283 -0
- package/Client/SinkClient.js +21 -0
- package/Client/SourceClient.js +30 -0
- package/LICENSE +22 -0
- package/README.md +116 -0
- package/Service/PushService.js +29 -0
- package/Service/RPCService.js +39 -0
- package/Service/Service.js +179 -0
- package/Service/SharedObjectService.js +125 -0
- package/Service/SinkService.js +37 -0
- package/Service/SourceService.js +31 -0
- package/index.js +9 -0
- package/misc/Validation.js +275 -0
- package/package.json +36 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const assert = require("assert");
|
|
4
|
+
const inspector = require("schema-inspector");
|
|
5
|
+
const Tinycache = require("tinycache");
|
|
6
|
+
|
|
7
|
+
function RPCValidation(endpoint, inout, obj, parseDates){
|
|
8
|
+
assert(parseDates === true || parseDates === false);
|
|
9
|
+
let schema;
|
|
10
|
+
let datePaths;
|
|
11
|
+
|
|
12
|
+
if (inout === "input") {
|
|
13
|
+
schema = endpoint.requestSchema;
|
|
14
|
+
if (!endpoint.inDatePaths) {
|
|
15
|
+
endpoint.inDatePaths = extractDatepaths(schema);
|
|
16
|
+
}
|
|
17
|
+
datePaths = endpoint.inDatePaths;
|
|
18
|
+
|
|
19
|
+
} else if (inout === "output") {
|
|
20
|
+
schema = endpoint.replySchema;
|
|
21
|
+
if (!endpoint.outDatePaths) {
|
|
22
|
+
endpoint.outDatePaths = extractDatepaths(schema);
|
|
23
|
+
}
|
|
24
|
+
datePaths = endpoint.outDatePaths;
|
|
25
|
+
} else {
|
|
26
|
+
throw new Error("doValidation called with wrong argument");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (parseDates && datePaths.length) {
|
|
30
|
+
for(let path of datePaths) {
|
|
31
|
+
applyDatepath(path, obj);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!schema){
|
|
36
|
+
console.error("There's no schema for RPC Call " + endpoint.name + ". Fix this!");
|
|
37
|
+
} else {
|
|
38
|
+
const validation = inspector.validate(schema, obj);
|
|
39
|
+
if (!validation.valid){
|
|
40
|
+
throw new Error("Validation failed! " + validation.format());
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function SourceSinkValidation(endpoint, obj, parseDates){
|
|
46
|
+
assert(parseDates === true || parseDates === false);
|
|
47
|
+
|
|
48
|
+
const schema = endpoint.messageSchema;
|
|
49
|
+
|
|
50
|
+
if(schema.skip){
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (schema && !endpoint.datePaths) {
|
|
55
|
+
endpoint.datePaths = extractDatepaths(schema);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!schema){
|
|
59
|
+
console.error("There's no schema for Source/Sink " + endpoint.name + ". Fix this!");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (parseDates && endpoint.datePaths && endpoint.datePaths.length) {
|
|
63
|
+
for(let path of endpoint.datePaths) {
|
|
64
|
+
applyDatepath(path, obj);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const validation = inspector.validate(schema, obj);
|
|
69
|
+
|
|
70
|
+
if (!validation.valid){
|
|
71
|
+
throw new Error("Validation failed! " + validation.format());
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function SharedObjectValidation(endpoint, obj, hint){
|
|
76
|
+
|
|
77
|
+
if (!endpoint.objectSchema){
|
|
78
|
+
console.error("There's no schema for SharedObject " + endpoint.name + ". Fix this!");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!hint) {
|
|
82
|
+
hint = [];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Check if we need to run validation
|
|
86
|
+
if(endpoint.objectSchema.skip){
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const subs = _getSubsForHint(endpoint.objectSchema, obj, hint);
|
|
91
|
+
|
|
92
|
+
const schema = subs.schema;
|
|
93
|
+
obj = subs.obj;
|
|
94
|
+
|
|
95
|
+
const validation = inspector.validate(schema, obj);
|
|
96
|
+
|
|
97
|
+
if (!validation.valid) {
|
|
98
|
+
throw new Error("Validation failed! " + validation.format());
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function parseDiffDates(endpoint, diff) {
|
|
103
|
+
const schema = endpoint.objectSchema;
|
|
104
|
+
|
|
105
|
+
if (schema.skip) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (schema && !schema.skip && !endpoint.datePaths) {
|
|
110
|
+
endpoint.datePaths = extractDatepaths(schema);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!endpoint.datePaths || endpoint.datePaths.length === 0) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (!endpoint.slicedCache) {
|
|
118
|
+
endpoint.slicedCache = new Tinycache();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let slicedPaths = endpoint.slicedCache.get(diff.path);
|
|
122
|
+
|
|
123
|
+
if (!slicedPaths) {
|
|
124
|
+
slicedPaths = endpoint.datePaths.filter((path) => {
|
|
125
|
+
for (let i = 0; i < diff.path.length; i++) {
|
|
126
|
+
if (!(path[i] === "*" || path[i] === diff.path[i])) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}).map(x => x.slice(diff.path.length - 1)).filter(x => x.length);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
endpoint.slicedCache.put(diff.path, slicedPaths, 10000);
|
|
135
|
+
|
|
136
|
+
if (diff.rhs) {
|
|
137
|
+
for (let datePath of slicedPaths) {
|
|
138
|
+
if (datePath[0] === diff.path[diff.path.length-1] || datePath[0] === "*") {
|
|
139
|
+
// Kinda yuk
|
|
140
|
+
let memo = datePath[0];
|
|
141
|
+
|
|
142
|
+
datePath[0] = "rhs";
|
|
143
|
+
applyDatepath(datePath, diff);
|
|
144
|
+
datePath[0] = "lhs";
|
|
145
|
+
applyDatepath(datePath, diff);
|
|
146
|
+
|
|
147
|
+
datePath[0] = memo;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
if (diff.kind === "A" && diff.item) {
|
|
154
|
+
for (let datePath of slicedPaths) {
|
|
155
|
+
if (datePath[0] === diff.path[diff.path.length - 1] || datePath[0] === "*") {
|
|
156
|
+
// Kinda yuk
|
|
157
|
+
|
|
158
|
+
let slicedPath = datePath.slice(1);
|
|
159
|
+
slicedPath[0] = "rhs";
|
|
160
|
+
applyDatepath(slicedPath, diff.item);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function parseFullDates(endpoint, obj) {
|
|
169
|
+
assert(endpoint.objectSchema);
|
|
170
|
+
|
|
171
|
+
const schema = endpoint.objectSchema;
|
|
172
|
+
|
|
173
|
+
if(schema.skip){
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (schema && !endpoint.datePaths) {
|
|
178
|
+
endpoint.datePaths = extractDatepaths(schema);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (endpoint.datePaths && endpoint.datePaths.length) {
|
|
182
|
+
for(let path of endpoint.datePaths) {
|
|
183
|
+
applyDatepath(path, obj);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const validation = inspector.validate(schema, obj);
|
|
188
|
+
|
|
189
|
+
if (!validation.valid){
|
|
190
|
+
throw new Error("Validation failed! " + validation.format());
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function _getSubsForHint(schema, obj, hint){
|
|
195
|
+
let i = 0;
|
|
196
|
+
while(i < hint.length){
|
|
197
|
+
if (!(hint[i] in obj)) {
|
|
198
|
+
break; // On delete, validate entire parent. Otherwise possible missing items may not be caught.
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
obj = obj[hint[i]];
|
|
202
|
+
|
|
203
|
+
if (schema.type === 'object') {
|
|
204
|
+
if (hint[i] in schema.properties) {
|
|
205
|
+
schema = schema.properties[hint[i]];
|
|
206
|
+
} else if ('*' in schema.properties) {
|
|
207
|
+
schema = schema.properties['*'];
|
|
208
|
+
} else{
|
|
209
|
+
throw new Error("Unknown property, and no catch all!")
|
|
210
|
+
}
|
|
211
|
+
} else if (schema.type === 'array') {
|
|
212
|
+
schema = schema.items;
|
|
213
|
+
} else {
|
|
214
|
+
// Hinting on anything else is not currently supported, crash on possible weirdness.
|
|
215
|
+
throw new Error("Please only do hinting on objects/arrays.");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
i++;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return {schema, obj};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
module.exports = {
|
|
225
|
+
RPCValidation,
|
|
226
|
+
SharedObjectValidation,
|
|
227
|
+
parseDiffDates,
|
|
228
|
+
parseFullDates,
|
|
229
|
+
SourceValidation: SourceSinkValidation,
|
|
230
|
+
SinkValidation: SourceSinkValidation
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
function extractDatepaths(schema) {
|
|
234
|
+
let out = [];
|
|
235
|
+
if (schema && schema.type === "object" && schema.properties) {
|
|
236
|
+
for (let prop in schema.properties) {
|
|
237
|
+
if (schema.properties[prop].type === "object") {
|
|
238
|
+
let recurse = extractDatepaths(schema.properties[prop]);
|
|
239
|
+
out = [...out, ...recurse.map(x => [prop, ...x])];
|
|
240
|
+
} else if (schema.properties[prop].type === "array") {
|
|
241
|
+
let recurse = extractDatepaths(schema.properties[prop].items);
|
|
242
|
+
out = [...out, ...recurse.map(x=>[prop,"*",...x])];
|
|
243
|
+
} else if (schema.properties[prop].type === "date") {
|
|
244
|
+
out.push([prop]);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return out;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function applyDatepath(path, obj) {
|
|
253
|
+
if (!obj[path[0]] && !(path[0] === "*" && typeof obj === "object")) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (path.length === 1) {
|
|
258
|
+
if (path[0] !== "*") {
|
|
259
|
+
obj[path[0]] = new Date(obj[path[0]])
|
|
260
|
+
} else {
|
|
261
|
+
for(let key in obj) {
|
|
262
|
+
obj[key] = new Date(obj[key]);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
let nextPath = path.slice(1);
|
|
267
|
+
if (path[0] !== "*") {
|
|
268
|
+
applyDatepath(nextPath, obj[path[0]]);
|
|
269
|
+
} else {
|
|
270
|
+
for(let key in obj) {
|
|
271
|
+
applyDatepath(nextPath, obj[key]);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@azuliani/node-service",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Node.js library for building services with ZeroMQ-based messaging patterns",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"index.js",
|
|
8
|
+
"Client/",
|
|
9
|
+
"Service/",
|
|
10
|
+
"misc/"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "node --test test/*.test.js",
|
|
14
|
+
"bench:nested": "node benchmark/nested-subobjects.js"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/azuliani/node-service.git"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/azuliani/node-service/issues"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/azuliani/node-service#readme",
|
|
24
|
+
"author": "Alexander Zuliani",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"overrides": {
|
|
27
|
+
"lodash": ">=4.17.23"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@azuliani/deep-diff": "0.0.1",
|
|
31
|
+
"fast-copy": "^3.0.2",
|
|
32
|
+
"schema-inspector": "^2.1.0",
|
|
33
|
+
"tinycache": "^1.1.2",
|
|
34
|
+
"zeromq": "^6.1.2"
|
|
35
|
+
}
|
|
36
|
+
}
|