electrodb 1.12.1 → 2.1.0
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/.travis.yml +1 -1
- package/README.md +712 -668
- package/index.d.ts +192 -179
- package/package.json +1 -1
- package/src/clauses.js +15 -32
- package/src/entity.js +184 -92
- package/src/errors.js +6 -0
- package/src/filters.js +2 -2
- package/src/operations.js +1 -1
- package/src/schema.js +68 -12
- package/src/service.js +48 -117
- package/src/types.js +15 -2
- package/src/util.js +63 -0
- package/src/validations.js +12 -0
- package/src/where.js +2 -2
- package/notes +0 -45
- package/output +0 -106
- package/taskdata.json +0 -1
- package/typez.ts +0 -1736
package/src/schema.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { CastTypes, ValueTypes, KeyCasing, AttributeTypes, AttributeMutationMethods, AttributeWildCard, PathTypes } = require("./types");
|
|
1
|
+
const { CastTypes, ValueTypes, KeyCasing, AttributeTypes, AttributeMutationMethods, AttributeWildCard, PathTypes, TableIndex } = require("./types");
|
|
2
2
|
const AttributeTypeNames = Object.keys(AttributeTypes);
|
|
3
3
|
const ValidFacetTypes = [AttributeTypes.string, AttributeTypes.number, AttributeTypes.boolean, AttributeTypes.enum];
|
|
4
4
|
const e = require("./errors");
|
|
@@ -100,6 +100,9 @@ class Attribute {
|
|
|
100
100
|
this.isKeyField = !!definition.isKeyField;
|
|
101
101
|
this.unformat = this._makeDestructureKey(definition);
|
|
102
102
|
this.format = this._makeStructureKey(definition);
|
|
103
|
+
this.padding = definition.padding;
|
|
104
|
+
this.applyFixings = this._makeApplyFixings(definition);
|
|
105
|
+
this.applyPadding = this._makePadding(definition);
|
|
103
106
|
this.indexes = [...(definition.indexes || [])];
|
|
104
107
|
let {isWatched, isWatcher, watchedBy, watching, watchAll} = Attribute._destructureWatcher(definition);
|
|
105
108
|
this._isWatched = isWatched
|
|
@@ -233,29 +236,72 @@ class Attribute {
|
|
|
233
236
|
return set || ((attr) => attr);
|
|
234
237
|
}
|
|
235
238
|
|
|
236
|
-
|
|
237
|
-
return (
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
value = `${prefix}${key}${postfix}`;
|
|
239
|
+
_makeApplyFixings({ prefix = "", postfix = "", casing= KeyCasing.none } = {}) {
|
|
240
|
+
return (value) => {
|
|
241
|
+
if ([AttributeTypes.string, AttributeTypes.enum].includes(this.type)) {
|
|
242
|
+
value = `${prefix}${value}${postfix}`;
|
|
241
243
|
}
|
|
244
|
+
|
|
242
245
|
return u.formatAttributeCasing(value, casing);
|
|
243
246
|
}
|
|
244
247
|
}
|
|
245
248
|
|
|
246
|
-
|
|
249
|
+
_makeStructureKey() {
|
|
250
|
+
return (key) => {
|
|
251
|
+
return this.applyPadding(key);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
_isPaddingEligible(padding = {} ) {
|
|
256
|
+
return !!padding && padding.length && v.isStringHasLength(padding.char);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
_makePadding({ padding = {} }) {
|
|
260
|
+
return (value) => {
|
|
261
|
+
if (typeof value !== 'string') {
|
|
262
|
+
return value;
|
|
263
|
+
} else if (this._isPaddingEligible(padding)) {
|
|
264
|
+
return u.addPadding({padding, value});
|
|
265
|
+
} else {
|
|
266
|
+
return value;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
_makeRemoveFixings({prefix = "", postfix = "", casing= KeyCasing.none} = {}) {
|
|
247
272
|
return (key) => {
|
|
248
273
|
let value = "";
|
|
249
274
|
if (![AttributeTypes.string, AttributeTypes.enum].includes(this.type) || typeof key !== "string") {
|
|
250
|
-
|
|
251
|
-
} else if (key.length > prefix.length) {
|
|
275
|
+
value = key;
|
|
276
|
+
} else if (prefix.length > 0 && key.length > prefix.length) {
|
|
252
277
|
for (let i = prefix.length; i < key.length - postfix.length; i++) {
|
|
253
278
|
value += key[i];
|
|
254
279
|
}
|
|
255
280
|
} else {
|
|
256
281
|
value = key;
|
|
257
282
|
}
|
|
258
|
-
|
|
283
|
+
|
|
284
|
+
return value;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
_makeDestructureKey({prefix = "", postfix = "", casing= KeyCasing.none, padding = {}} = {}) {
|
|
289
|
+
return (key) => {
|
|
290
|
+
let value = "";
|
|
291
|
+
if (![AttributeTypes.string, AttributeTypes.enum].includes(this.type) || typeof key !== "string") {
|
|
292
|
+
return key;
|
|
293
|
+
} else if (key.length > prefix.length) {
|
|
294
|
+
value = u.removeFixings({prefix, postfix, value: key});
|
|
295
|
+
} else {
|
|
296
|
+
value = key;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// todo: if an attribute is also used as a pk or sk directly in one index, but a composite in another, then padding is going to be broken
|
|
300
|
+
// if (padding && padding.length) {
|
|
301
|
+
// value = u.removePadding({padding, value});
|
|
302
|
+
// }
|
|
303
|
+
|
|
304
|
+
return value;
|
|
259
305
|
};
|
|
260
306
|
}
|
|
261
307
|
|
|
@@ -958,7 +1004,7 @@ class Schema {
|
|
|
958
1004
|
let definition = facets.byField[field][indexName];
|
|
959
1005
|
if (definition.facets.length > 1) {
|
|
960
1006
|
throw new e.ElectroError(
|
|
961
|
-
e.ErrorCodes.
|
|
1007
|
+
e.ErrorCodes.InvalidIndexWithAttributeName,
|
|
962
1008
|
`Invalid definition for "${definition.type}" field on index "${u.formatIndexNameForDisplay(indexName)}". The ${definition.type} field "${definition.field}" shares a field name with an attribute defined on the Entity, and therefore is not allowed to contain composite references to other attributes. Please either change the field name of the attribute, or redefine the index to use only the single attribute "${definition.field}".`
|
|
963
1009
|
)
|
|
964
1010
|
}
|
|
@@ -1005,10 +1051,19 @@ class Schema {
|
|
|
1005
1051
|
`Invalid use of a collection on index "${u.formatIndexNameForDisplay(indexName)}". The ${definition.type} field "${definition.field}" shares a field name with an attribute defined on the Entity, and therefore the index is not allowed to participate in a Collection. Please either change the field name of the attribute, or remove all collection(s) from the index.`
|
|
1006
1052
|
)
|
|
1007
1053
|
}
|
|
1054
|
+
|
|
1055
|
+
if (definition.field === field) {
|
|
1056
|
+
if (attribute.padding !== undefined) {
|
|
1057
|
+
throw new e.ElectroError(
|
|
1058
|
+
e.ErrorCodes.InvalidAttributeDefinition,
|
|
1059
|
+
`Invalid padding definition for the attribute "${name}". Padding is not currently supported for attributes that are also defined as table indexes.`
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1008
1063
|
}
|
|
1009
1064
|
}
|
|
1010
1065
|
|
|
1011
|
-
let isKey = !!facets.byIndex && facets.byIndex[
|
|
1066
|
+
let isKey = !!facets.byIndex && facets.byIndex[TableIndex].all.find((facet) => facet.name === name);
|
|
1012
1067
|
let definition = {
|
|
1013
1068
|
name,
|
|
1014
1069
|
field,
|
|
@@ -1033,6 +1088,7 @@ class Schema {
|
|
|
1033
1088
|
properties: attribute.properties,
|
|
1034
1089
|
parentPath: attribute.parentPath,
|
|
1035
1090
|
parentType: attribute.parentType,
|
|
1091
|
+
padding: attribute.padding,
|
|
1036
1092
|
};
|
|
1037
1093
|
|
|
1038
1094
|
if (definition.type === AttributeTypes.custom) {
|
package/src/service.js
CHANGED
|
@@ -243,13 +243,8 @@ class Service {
|
|
|
243
243
|
|
|
244
244
|
cleanseRetrievedData(collection = "", entities, data = {}, config = {}) {
|
|
245
245
|
if (config.raw) {
|
|
246
|
-
|
|
247
|
-
return [data.LastEvaluatedKey, data];
|
|
248
|
-
} else {
|
|
249
|
-
return data;
|
|
250
|
-
}
|
|
246
|
+
return data;
|
|
251
247
|
}
|
|
252
|
-
|
|
253
248
|
data.Items = data.Items || [];
|
|
254
249
|
let index = this.collectionSchema[collection].index;
|
|
255
250
|
let results = {};
|
|
@@ -268,18 +263,15 @@ class Service {
|
|
|
268
263
|
if (!entityAlias) {
|
|
269
264
|
continue;
|
|
270
265
|
}
|
|
271
|
-
|
|
272
266
|
// pager=false because we don't want the entity trying to parse the lastEvaluatedKey
|
|
273
|
-
let items = this.collectionSchema[collection].entities[entityAlias].formatResponse({Item: record}, index, {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
return [page, results];
|
|
280
|
-
} else {
|
|
281
|
-
return results;
|
|
267
|
+
let items = this.collectionSchema[collection].entities[entityAlias].formatResponse({Item: record}, index, {
|
|
268
|
+
...config,
|
|
269
|
+
pager: false,
|
|
270
|
+
parse: undefined
|
|
271
|
+
});
|
|
272
|
+
results[entityAlias].push(items.data);
|
|
282
273
|
}
|
|
274
|
+
return results;
|
|
283
275
|
}
|
|
284
276
|
|
|
285
277
|
findKeyOwner(lastEvaluatedKey) {
|
|
@@ -287,92 +279,25 @@ class Service {
|
|
|
287
279
|
.find((entity) => entity.ownsLastEvaluatedKey(lastEvaluatedKey));
|
|
288
280
|
}
|
|
289
281
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
let matchingEntities = [];
|
|
295
|
-
if (pager === null) {
|
|
296
|
-
return matchingEntities;
|
|
297
|
-
}
|
|
298
|
-
for (let entity of Object.values(this.collectionSchema[collection].entities)) {
|
|
299
|
-
if (entity.ownsPager(pager, this.collectionSchema[collection].index)) {
|
|
300
|
-
matchingEntities.push(entity);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
return matchingEntities;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
findNamedPagerOwner(pager = {}) {
|
|
307
|
-
let identifiers = this._getEntityIdentifiers(this.entities);
|
|
308
|
-
for (let identifier of identifiers) {
|
|
309
|
-
let hasCorrectFieldProperties = typeof pager[identifier.nameField] === "string" && typeof pager[identifier.versionField] === "string";
|
|
310
|
-
let hasMatchingFieldValues = pager[identifier.nameField] === identifier.name && pager[identifier.versionField] === identifier.version;
|
|
311
|
-
if (hasCorrectFieldProperties && hasMatchingFieldValues) {
|
|
312
|
-
return identifier.entity;
|
|
313
|
-
}
|
|
282
|
+
expectKeyOwner(lastEvaluatedKey) {
|
|
283
|
+
const owner = this.findKeyOwner(lastEvaluatedKey);
|
|
284
|
+
if (owner === undefined) {
|
|
285
|
+
throw new e.ElectroError(e.ErrorCodes.NoOwnerForCursor, `Supplied cursor does not resolve to Entity within the Service ${this.service.name}`);
|
|
314
286
|
}
|
|
287
|
+
return owner;
|
|
315
288
|
}
|
|
316
289
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
} else if (type === Pager.named || type === undefined) {
|
|
321
|
-
let owner = this.findNamedPagerOwner(pager);
|
|
322
|
-
if (owner === undefined) {
|
|
323
|
-
throw new e.ElectroError(e.ErrorCodes.NoOwnerForPager, "Supplied Pager does not resolve to Entity within Service");
|
|
324
|
-
}
|
|
325
|
-
return owner;
|
|
326
|
-
} else if (type === Pager.item) {
|
|
327
|
-
let owners = this.findItemPagerOwner(collection, pager);
|
|
328
|
-
if (owners.length === 1) {
|
|
329
|
-
return owners[0];
|
|
330
|
-
} else {
|
|
331
|
-
throw new e.ElectroError(e.ErrorCodes.PagerNotUnique, "Supplied Pager did not resolve to single Entity");
|
|
332
|
-
}
|
|
333
|
-
} else {
|
|
334
|
-
throw new e.ElectroError(e.ErrorCodes.InvalidOptions, `Invalid value for option "pager" provider: "${pager}". Allowed values include ${u.commaSeparatedString(Object.keys(Pager))}.`)
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
findPagerIdentifiers(collection, pager = {}) {
|
|
339
|
-
if (this.collectionSchema[collection] === undefined) {
|
|
340
|
-
throw new Error("Invalid collection");
|
|
341
|
-
}
|
|
342
|
-
let matchingIdentifiers = [];
|
|
343
|
-
if (pager === null) {
|
|
344
|
-
return matchingIdentifiers;
|
|
345
|
-
}
|
|
346
|
-
for (let entity of Object.values(this.collectionSchema[collection].entities)) {
|
|
347
|
-
if (entity.ownsPager(pager, this.collectionSchema[collection].index)) {
|
|
348
|
-
matchingIdentifiers.push({
|
|
349
|
-
[entity.identifiers.entity]: entity.getName(),
|
|
350
|
-
[entity.identifiers.version]: entity.getVersion(),
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
return matchingIdentifiers;
|
|
290
|
+
findCursorOwner(cursor) {
|
|
291
|
+
return Object.values(this.entities)
|
|
292
|
+
.find(entity => entity.ownsCursor(cursor));
|
|
355
293
|
}
|
|
356
294
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
return null;
|
|
362
|
-
} else {
|
|
363
|
-
let entity = this.findKeyOwner(lastEvaluatedKey);
|
|
364
|
-
if (!entity) {
|
|
365
|
-
return lastReturned !== undefined
|
|
366
|
-
? this._formatReturnPager(config, index, lastReturned)
|
|
367
|
-
: null
|
|
368
|
-
}
|
|
369
|
-
let page = entity._formatKeysToItem(index, lastEvaluatedKey);
|
|
370
|
-
if (config.pager === "named") {
|
|
371
|
-
page[entity.identifiers.entity] = entity.getName();
|
|
372
|
-
page[entity.identifiers.version] = entity.getVersion();
|
|
373
|
-
}
|
|
374
|
-
return page;
|
|
295
|
+
expectCursorOwner(cursor) {
|
|
296
|
+
const owner = this.findCursorOwner(cursor);
|
|
297
|
+
if (owner === undefined) {
|
|
298
|
+
throw new e.ElectroError(e.ErrorCodes.NoOwnerForCursor, `Supplied cursor does not resolve to Entity within the Service ${this.service.name}`);
|
|
375
299
|
}
|
|
300
|
+
return owner;
|
|
376
301
|
}
|
|
377
302
|
|
|
378
303
|
_getTableName() {
|
|
@@ -389,21 +314,7 @@ class Service {
|
|
|
389
314
|
_makeCollectionChain(name = "", attributes = {}, initialClauses = {}, expressions = {}, entities = {}, entity = {}, facets = {}) {
|
|
390
315
|
let filterBuilder = new FilterFactory(attributes, FilterOperations);
|
|
391
316
|
let whereBuilder = new WhereFactory(attributes, FilterOperations);
|
|
392
|
-
|
|
393
|
-
let pageClause = {...initialClauses.page};
|
|
394
|
-
let pageAction = initialClauses.page.action;
|
|
395
|
-
pageClause.action = (entity, state, page = null, options = {}) => {
|
|
396
|
-
try {
|
|
397
|
-
if (page === null) {
|
|
398
|
-
return pageAction(entity, state, page, options);
|
|
399
|
-
}
|
|
400
|
-
let owner = this.expectPagerOwner(options.pager, name, page);
|
|
401
|
-
return pageAction(owner, state, page, options);
|
|
402
|
-
} catch(err) {
|
|
403
|
-
return Promise.reject(err);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
let clauses = {...initialClauses, page: pageClause};
|
|
317
|
+
let clauses = {...initialClauses};
|
|
407
318
|
|
|
408
319
|
clauses = filterBuilder.injectFilterClauses(clauses);
|
|
409
320
|
clauses = whereBuilder.injectWhereClauses(clauses);
|
|
@@ -412,6 +323,14 @@ class Service {
|
|
|
412
323
|
// expressions, // DynamoDB doesnt return what I expect it would when provided with these entity filters
|
|
413
324
|
parse: (options, data) => {
|
|
414
325
|
return this.cleanseRetrievedData(name, entities, data, options);
|
|
326
|
+
},
|
|
327
|
+
formatCursor: {
|
|
328
|
+
serialize: (key) => {
|
|
329
|
+
return this.expectKeyOwner(key).serializeCursor(key);
|
|
330
|
+
},
|
|
331
|
+
deserialize: (cursor) => {
|
|
332
|
+
return this.expectCursorOwner(cursor).deserilizeCursor(cursor);
|
|
333
|
+
}
|
|
415
334
|
}
|
|
416
335
|
};
|
|
417
336
|
|
|
@@ -508,7 +427,7 @@ class Service {
|
|
|
508
427
|
return [!!collectionDifferences.length, collectionDifferences];
|
|
509
428
|
}
|
|
510
429
|
|
|
511
|
-
_compareEntityAttributes(definition = {}, providedAttributes = {}) {
|
|
430
|
+
_compareEntityAttributes(entityName, definition = {}, providedAttributes = {}, keys) {
|
|
512
431
|
let results = {
|
|
513
432
|
additions: {},
|
|
514
433
|
invalid: [],
|
|
@@ -519,17 +438,29 @@ class Service {
|
|
|
519
438
|
results.additions[name] = detail;
|
|
520
439
|
} else if (defined.field !== detail.field) {
|
|
521
440
|
results.invalid.push(
|
|
522
|
-
`
|
|
441
|
+
`The attribute "${name}" with Table Field "${detail.field}" does not match established Table Field "${defined.field}"`,
|
|
523
442
|
);
|
|
524
443
|
}
|
|
444
|
+
if (defined && detail && (defined.padding || detail.padding)) {
|
|
445
|
+
const definedPadding = defined.padding || {};
|
|
446
|
+
const detailPadding = detail.padding || {};
|
|
447
|
+
if (keys.pk.facets.includes(name) &&
|
|
448
|
+
(definedPadding.length !== detailPadding.length ||
|
|
449
|
+
definedPadding.char !== detailPadding.char)
|
|
450
|
+
) {
|
|
451
|
+
results.invalid.push(
|
|
452
|
+
`The attribute "${name}" contains inconsistent padding definitions that impact how keys are formed`,
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
525
456
|
}
|
|
526
457
|
return [!!results.invalid.length, results];
|
|
527
458
|
}
|
|
528
459
|
|
|
529
|
-
_processEntityAttributes(definition = {}, providedAttributes = {}) {
|
|
530
|
-
let [attributesAreIncompatible, attributeResults] = this._compareEntityAttributes(definition, providedAttributes);
|
|
460
|
+
_processEntityAttributes(entityName, definition = {}, providedAttributes = {}, keys) {
|
|
461
|
+
let [attributesAreIncompatible, attributeResults] = this._compareEntityAttributes(entityName, definition, providedAttributes, keys);
|
|
531
462
|
if (attributesAreIncompatible) {
|
|
532
|
-
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, `
|
|
463
|
+
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, `Inconsistent attribute(s) on the entity "${entityName}". The following attribute(s) are defined with incompatible or conflicting definitions across participating entities: ${attributeResults.invalid.join(", ")}. These attribute definitions must match among all members of the collection.`);
|
|
533
464
|
} else {
|
|
534
465
|
return {
|
|
535
466
|
...definition,
|
|
@@ -647,7 +578,7 @@ class Service {
|
|
|
647
578
|
this.collectionSchema[collection].table = entity._getTableName();
|
|
648
579
|
}
|
|
649
580
|
this.collectionSchema[collection].keys = this._processEntityKeys(name, this.collectionSchema[collection].keys, providedIndex);
|
|
650
|
-
this.collectionSchema[collection].attributes = this._processEntityAttributes(this.collectionSchema[collection].attributes, entity.model.schema.attributes);
|
|
581
|
+
this.collectionSchema[collection].attributes = this._processEntityAttributes(name, this.collectionSchema[collection].attributes, entity.model.schema.attributes, this.collectionSchema[collection].keys);
|
|
651
582
|
this.collectionSchema[collection].entities[name] = entity;
|
|
652
583
|
this.collectionSchema[collection].identifiers = this._processEntityIdentifiers(this.collectionSchema[collection].identifiers, entity.getIdentifierExpressions(name));
|
|
653
584
|
this.collectionSchema[collection].index = this._processEntityCollectionIndex(this.collectionSchema[collection].index, providedIndex.index, name, collection);
|
package/src/types.js
CHANGED
|
@@ -110,7 +110,8 @@ const AttributeMutationMethods = {
|
|
|
110
110
|
const Pager = {
|
|
111
111
|
raw: "raw",
|
|
112
112
|
named: "named",
|
|
113
|
-
item: "item"
|
|
113
|
+
item: "item",
|
|
114
|
+
cursor: "cursor"
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
const UnprocessedTypes = {
|
|
@@ -194,6 +195,15 @@ const TerminalOperation = {
|
|
|
194
195
|
page: 'page',
|
|
195
196
|
}
|
|
196
197
|
|
|
198
|
+
const AllPages = 'all';
|
|
199
|
+
|
|
200
|
+
const ResultOrderOption = {
|
|
201
|
+
'asc': true,
|
|
202
|
+
'desc': false
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const ResultOrderParam = 'ScanIndexForward';
|
|
206
|
+
|
|
197
207
|
module.exports = {
|
|
198
208
|
Pager,
|
|
199
209
|
KeyTypes,
|
|
@@ -223,5 +233,8 @@ module.exports = {
|
|
|
223
233
|
AttributeProxySymbol,
|
|
224
234
|
ElectroInstanceTypes,
|
|
225
235
|
EventSubscriptionTypes,
|
|
226
|
-
AttributeMutationMethods
|
|
236
|
+
AttributeMutationMethods,
|
|
237
|
+
AllPages,
|
|
238
|
+
ResultOrderOption,
|
|
239
|
+
ResultOrderParam,
|
|
227
240
|
};
|
package/src/util.js
CHANGED
|
@@ -161,9 +161,71 @@ function getUnique(arr1, arr2) {
|
|
|
161
161
|
]));
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
const cursorFormatter = {
|
|
165
|
+
serialize: (key) => {
|
|
166
|
+
if (!key) {
|
|
167
|
+
return null;
|
|
168
|
+
} else if (typeof val !== 'string') {
|
|
169
|
+
key = JSON.stringify(key);
|
|
170
|
+
}
|
|
171
|
+
return Buffer.from(key).toString('base64url');
|
|
172
|
+
},
|
|
173
|
+
deserialize: (cursor) => {
|
|
174
|
+
if (!cursor) {
|
|
175
|
+
return undefined;
|
|
176
|
+
} else if (typeof cursor !== 'string') {
|
|
177
|
+
throw new Error(`Invalid cursor provided, expected type 'string' recieved: ${JSON.stringify(cursor)}`);
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
return JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8'));
|
|
181
|
+
} catch(err) {
|
|
182
|
+
throw new Error('Unable to parse cursor');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function removeFixings({prefix = '', postfix = '', value = ''} = {}) {
|
|
188
|
+
const start = value.toLowerCase().startsWith(prefix.toLowerCase()) ? prefix.length : 0;
|
|
189
|
+
const end = value.length - (value.toLowerCase().endsWith(postfix.toLowerCase()) ? postfix.length : 0);
|
|
190
|
+
|
|
191
|
+
let formatted = '';
|
|
192
|
+
for (let i = start; i < end; i++) {
|
|
193
|
+
formatted += value[i];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return formatted;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function addPadding({padding = {}, value = ''} = {}) {
|
|
200
|
+
return value.padStart(padding.length, padding.char);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function removePadding({padding = {}, value = ''} = {}) {
|
|
204
|
+
if (!padding.length || value.length >= padding.length) {
|
|
205
|
+
return value;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
let formatted = '';
|
|
209
|
+
let useRemaining = false;
|
|
210
|
+
for (let i = 0; i < value.length; i++) {
|
|
211
|
+
const char = value[i];
|
|
212
|
+
if (useRemaining || i >= padding.length) {
|
|
213
|
+
formatted += char;
|
|
214
|
+
} else if (char !== padding.char) {
|
|
215
|
+
formatted += char;
|
|
216
|
+
useRemaining = true;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return formatted;
|
|
221
|
+
}
|
|
222
|
+
|
|
164
223
|
module.exports = {
|
|
165
224
|
getUnique,
|
|
166
225
|
batchItems,
|
|
226
|
+
addPadding,
|
|
227
|
+
removePadding,
|
|
228
|
+
removeFixings,
|
|
167
229
|
parseJSONPath,
|
|
168
230
|
getInstanceType,
|
|
169
231
|
getModelVersion,
|
|
@@ -171,6 +233,7 @@ module.exports = {
|
|
|
171
233
|
genericizeJSONPath,
|
|
172
234
|
commaSeparatedString,
|
|
173
235
|
formatAttributeCasing,
|
|
236
|
+
cursorFormatter,
|
|
174
237
|
applyBetaModelOverrides,
|
|
175
238
|
formatIndexNameForDisplay,
|
|
176
239
|
BatchGetOrderMaintainer,
|
package/src/validations.js
CHANGED
|
@@ -64,6 +64,18 @@ const Attribute = {
|
|
|
64
64
|
type: "any",
|
|
65
65
|
format: "isFunction",
|
|
66
66
|
},
|
|
67
|
+
padding: {
|
|
68
|
+
type: "object",
|
|
69
|
+
required: ['length', 'char'],
|
|
70
|
+
properties: {
|
|
71
|
+
length: {
|
|
72
|
+
type: 'number'
|
|
73
|
+
},
|
|
74
|
+
char: {
|
|
75
|
+
type: 'string',
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
67
79
|
},
|
|
68
80
|
};
|
|
69
81
|
|
package/src/where.js
CHANGED
|
@@ -120,7 +120,7 @@ class WhereFactory {
|
|
|
120
120
|
injected[name] = {
|
|
121
121
|
name,
|
|
122
122
|
action: this.buildClause(filter),
|
|
123
|
-
children: ["params", "go", "
|
|
123
|
+
children: ["params", "go", "where", ...modelFilters],
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
126
|
filterChildren.push("where");
|
|
@@ -129,7 +129,7 @@ class WhereFactory {
|
|
|
129
129
|
action: (entity, state, fn) => {
|
|
130
130
|
return this.buildClause(fn)(entity, state);
|
|
131
131
|
},
|
|
132
|
-
children: ["params", "go", "
|
|
132
|
+
children: ["params", "go", "where", ...modelFilters],
|
|
133
133
|
};
|
|
134
134
|
for (let parent of filterParents) {
|
|
135
135
|
injected[parent] = { ...injected[parent] };
|
package/notes
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
3
|
-
todo: better param typing
|
|
4
|
-
done: get typing
|
|
5
|
-
todo: get implementation
|
|
6
|
-
done: get[] typing
|
|
7
|
-
todo: get[] implementation
|
|
8
|
-
done: delete typing
|
|
9
|
-
todo: delete implementation
|
|
10
|
-
done: delete[] typing
|
|
11
|
-
todo: delete[] implementation
|
|
12
|
-
done: put[] typing
|
|
13
|
-
todo: put[] implementation
|
|
14
|
-
done: put typing
|
|
15
|
-
todo: put implementation
|
|
16
|
-
todo: update typing
|
|
17
|
-
done: update implementation
|
|
18
|
-
done: SetRecord
|
|
19
|
-
done: RemoveRecord
|
|
20
|
-
done: DataUpdateMethodRecord
|
|
21
|
-
|
|
22
|
-
done: scan typing
|
|
23
|
-
todo: scan implementation
|
|
24
|
-
done: query typing
|
|
25
|
-
todo: query implementation
|
|
26
|
-
done: find typing
|
|
27
|
-
todo: find implementation
|
|
28
|
-
done: match typing
|
|
29
|
-
todo: match implementation
|
|
30
|
-
done: parse typing
|
|
31
|
-
todo: parse implementation (requires cursor?)
|
|
32
|
-
todo: collection query
|
|
33
|
-
todo: collection impl
|
|
34
|
-
|
|
35
|
-
todo: add where to go?
|
|
36
|
-
todo: rethink option names
|
|
37
|
-
|
|
38
|
-
todo: add cursor to query options
|
|
39
|
-
todo: remove old doc notes on `join` and slim down `service`
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
find and test any add on set and number
|
|
44
|
-
try number enum?
|
|
45
|
-
integration test enum set ts
|
package/output
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
page {
|
|
2
|
-
"data": [
|
|
3
|
-
{
|
|
4
|
-
"prop2": "e3b142b1-e5d7-4b17-8a66-38fcdf6ef1ef",
|
|
5
|
-
"prop1": "value1",
|
|
6
|
-
"prop4": "value4"
|
|
7
|
-
},
|
|
8
|
-
{
|
|
9
|
-
"prop2": "e8ed19db-3ac4-45d6-8fb8-281ada117e96",
|
|
10
|
-
"prop1": "value1",
|
|
11
|
-
"prop4": "value4"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"prop2": "dcca03f5-7478-4afb-9297-20a68ff56559",
|
|
15
|
-
"prop1": "value1",
|
|
16
|
-
"prop4": "value4"
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"prop2": "03ccd79e-c5e5-4179-8213-196eafcbd4b9",
|
|
20
|
-
"prop1": "value1",
|
|
21
|
-
"prop4": "value4"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"prop2": "2900735e-aaa4-4661-a52d-7aecb752e694",
|
|
25
|
-
"prop1": "value1",
|
|
26
|
-
"prop4": "value4"
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
"prop2": "0f1da461-5257-4f6c-8cb3-af7b56819100",
|
|
30
|
-
"prop1": "value1",
|
|
31
|
-
"prop4": "value4"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"prop2": "7562c43b-8ac9-483c-b950-4f0e1366c11a",
|
|
35
|
-
"prop1": "value1",
|
|
36
|
-
"prop4": "value4"
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
"prop2": "ee844e97-dc1c-4242-bd2b-45685e39d7fd",
|
|
40
|
-
"prop1": "value1",
|
|
41
|
-
"prop4": "value4"
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"prop2": "4b55e242-9875-412e-b483-24e51dabf6c8",
|
|
45
|
-
"prop1": "value1",
|
|
46
|
-
"prop4": "value4"
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
"prop2": "77a5b66e-c658-4ec6-9630-4d2469653bd4",
|
|
50
|
-
"prop1": "value1",
|
|
51
|
-
"prop4": "value4"
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"prop2": "194efbfe-7be6-4662-9982-a1c8eb6ab36d",
|
|
55
|
-
"prop1": "value1",
|
|
56
|
-
"prop4": "value4"
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"prop2": "value2",
|
|
60
|
-
"prop1": "value1",
|
|
61
|
-
"prop4": "value4"
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
"prop2": "516d8256-3b3e-41e1-a44a-0720fe748e23",
|
|
65
|
-
"prop1": "value1",
|
|
66
|
-
"prop4": "value4"
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
"prop2": "35bf3a7e-9dff-4817-a157-2be09c5fc8a8",
|
|
70
|
-
"prop1": "value1",
|
|
71
|
-
"prop4": "value4"
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
"prop2": "3836452b-6c00-415e-85ce-a9596644b6eb",
|
|
75
|
-
"prop1": "value1",
|
|
76
|
-
"prop4": "value4"
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
"prop2": "value2abc",
|
|
80
|
-
"prop1": "value1",
|
|
81
|
-
"prop4": "value4"
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
"prop2": "887e5301-113c-4ca6-b6d0-ebaf27e217d5",
|
|
85
|
-
"prop1": "value1",
|
|
86
|
-
"prop4": "value4"
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
"prop2": "6c6d9363-3821-40c8-b3d0-7ec2c6d3f1eb",
|
|
90
|
-
"prop1": "value1",
|
|
91
|
-
"prop4": "value4"
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
"prop2": "fcff14a3-ffa9-498e-a0ae-596c747b027b",
|
|
95
|
-
"prop1": "value1",
|
|
96
|
-
"prop4": "value4"
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
"prop2": "1d199eeb-ccc1-4c25-a1b6-1685126d44b3",
|
|
100
|
-
"prop1": "value1",
|
|
101
|
-
"prop4": "value4"
|
|
102
|
-
}
|
|
103
|
-
],
|
|
104
|
-
"count": 2,
|
|
105
|
-
"next": null
|
|
106
|
-
}
|