@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.
@@ -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
+ }