From 20dc7b1f73f268e1250ed7ffb57276cdb160f247 Mon Sep 17 00:00:00 2001 From: tywalch Date: Fri, 14 Aug 2020 16:33:44 -0400 Subject: [PATCH 1/8] initial changes and experimentation --- src/entity.js | 129 +++++++++++++++++++++++++++++++++--- src/schema.js | 2 +- test/connected.crud.spec.js | 12 ++-- 3 files changed, 128 insertions(+), 15 deletions(-) diff --git a/src/entity.js b/src/entity.js index bce873b7..2245e53d 100644 --- a/src/entity.js +++ b/src/entity.js @@ -225,9 +225,10 @@ class Entity { results = this.model.schema.applyAttributeGetters(data); } } + if (config.pager) { - let nextPage = response.LastEvaluatedKey || null; + let nextPage = this._formatReturnPager(response.LastEvaluatedKey || null); results = [nextPage, results]; } @@ -243,6 +244,62 @@ class Entity { } } + _regexpEscape(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + + _deconstructKeys(keyType, key) { + let index = ""; + let accessPattern = this.model.translations.indexes.fromIndexToAccessPattern[index]; + let {prefix} = this.model.prefixes[index][keyType]; + let {facets} = this.model.indexes[accessPattern][keyType]; + let names = []; + let pattern = `^${this._regexpEscape(prefix)}`; + for (let facet of facets) { + let { label, name } = this.model.schema.attributes[facet]; + pattern += `#${this._regexpEscape(label)}_(.+)`; + names.push(name); + } + pattern += "$"; + + let regex = RegExp(pattern); + let match = key.match(regex); + let results = {} + if (!match) { + results[keyType] = key + } else { + for (let i = 0; i < names.length; i++) { + results[names[i]] = match[i+1]; + } + } + return results; + } + + _formatReturnPager(item) { + return item; + if (item === null || typeof item !== "object" || Object.keys(item).length === 0) { + return item; + } + let pager = {}; + for (let {name} of this.model.facets.byIndex[""].all) { + pager[name] = item[name]; + } + return pager; + } + + _formatSuppliedPager(item) { + return item; + if (typeof item !== "object" || Object.keys(item).length === 0) { + return item; + } + // let expectedFacets = this.model.facets.byIndex[""].all.map(facet => facet.name); + let pk = this._expectFacets(item, this.model.facets.byIndex[""].pk) + let sk = this._expectFacets(item, this.model.facets.byIndex[""].sk) + let index = ""; + let keys = this._makeIndexKeys(index, pk, sk); + return this._makeParameterKey(index, keys.pk, ...keys.sk); + } + _applyParameterOptions(params, ...options) { let config = { includeKeys: false, @@ -272,7 +329,7 @@ class Entity { } if (Object.keys(config.page || {}).length) { - parameters.ExclusiveStartKey = config.page; + parameters.ExclusiveStartKey = this._formatSuppliedPager(config.page); } return parameters; @@ -883,11 +940,50 @@ class Entity { return [!!missing.length, missing, matching]; } - _makeKeyPrefixes(service, entity, version = "1") { - return { - pk: `$${service}_${version}`, - sk: `$${entity}`, - }; + _makeKeyPrefixes(service, entity, version = "1", tableIndex) { + /* + Collections will prefix the sort key so they can be queried with + a "begins_with" operator when crossing entities. It is also possible + that the user defined a custom key on either the PK or SK. In the case + of a customKey AND a collection, the collection is ignored to favor + the custom key. + */ + + let keys = { + pk: { + prefix: "", + isCustom: tableIndex.customFacets.pk + }, + sk: { + prefix: "", + isCustom: tableIndex.customFacets.sk + } + } + + let pk = `$${service}_${version}`; + let sk = ""; + + // If the index is in a collections, prepend the sk; + if (tableIndex.collection) { + sk = `$${tableIndex.collection}#${entity}` + } else { + sk = `$${entity}` + } + + // If no sk, append the sk properties to the pk + if (Object.keys(tableIndex.sk).length === 0) { + pk += sk; + } + + // If keys arent custom, set the prefixes + if (!keys.pk.isCustom) { + keys.pk.prefix = pk.toLowerCase(); + } + if (!keys.sk.isCustom) { + keys.sk.prefix = sk.toLowerCase(); + } + + return keys; } _validateIndex(index) { @@ -957,7 +1053,10 @@ class Entity { skFacets.push({}); } let facets = this.model.facets.byIndex[index]; - let prefixes = this._getPrefixes(facets); + let prefixes = this.model.prefixes[index]; + if (!prefixes) { + throw new Error(`Invalid index: ${index}`); + } // let sk = []; let pk = this._makeKey( prefixes.pk.prefix, @@ -1138,7 +1237,9 @@ class Entity { let parsedPKFacets = this._parseFacets(index.pk.facets); let { facetArray, facetLabels } = parsedPKFacets; customFacets.pk = parsedPKFacets.isCustom; + // labels can be set via the attribute definiton or as part of the facetTemplate. facets.labels = Object.assign({}, facets.labels, facetLabels); + let pk = { accessPattern, facetLabels, @@ -1270,9 +1371,18 @@ class Entity { return normalized; } + _normalizePrefixes(service, entity, version, indexes) { + let prefixes = {}; + for (let accessPattern of Object.keys(indexes)) { + let item = indexes[accessPattern]; + prefixes[item.index] = this._makeKeyPrefixes(service, entity, version, item); + } + return prefixes; + } + _parseModel(model) { let { service, entity, table, version = "1" } = model; - let prefixes = this._makeKeyPrefixes(service, entity, version); + let { facets, indexes, @@ -1284,6 +1394,7 @@ class Entity { } = this._normalizeIndexes(model.indexes); let schema = new Schema(model.attributes, facets); let filters = this._normalizeFilters(model.filters); + let prefixes = this._normalizePrefixes(service, entity, version, indexes); return { service, version, diff --git a/src/schema.js b/src/schema.js index 49bd5bd0..9124beac 100644 --- a/src/schema.js +++ b/src/schema.js @@ -5,7 +5,7 @@ class Attribute { constructor(definition = {}) { this.name = definition.name; this.field = definition.field || definition.name; - this.label = definition.label || ""; + this.label = definition.label || definition.name; this.readOnly = !!definition.readOnly; this.required = !!definition.required; this.cast = this._makeCast(definition.name, definition.cast); diff --git a/test/connected.crud.spec.js b/test/connected.crud.spec.js index 639078d2..ab28b647 100644 --- a/test/connected.crud.spec.js +++ b/test/connected.crud.spec.js @@ -787,16 +787,18 @@ describe("Entity", async () => { expect(results).to.be.an("array").and.have.length(2); // Scan may not return records, dont force a bad test then let [index, stores] = results; - if (stores.length) { - expect(index).to.have.a.property('pk').and.to.have.a.property('sk'); + if (stores && stores.Items.length) { + expect(index).to.have.a.property('pk'); + expect(index).to.have.a.property('sk') expect(stores.Items).to.be.an("array") - expect(stores.Items[0]).to.have.a.property('pk').and.to.have.a.property('sk'); + expect(stores.Items[0]).to.have.a.property('pk') + expect(stores.Items[0]).to.have.a.property('sk'); let [nextIndex, nextStores] = await MallStores.scan.page(index); - expect(stores).to.be.an("array").and.have.length(2); expect(nextIndex).to.not.deep.equal(index); expect(nextStores).to.be.an("array"); if (nextStores.length) { - expect(nextStores[0]).to.not.have.a.property('pk').and.to.not.have.a.property('sk'); + expect(nextStores[0]).to.not.have.a.property('pk') + expect(nextStores[0]).to.not.have.a.property('sk'); } } }).timeout(10000); From e72e8ac9fe7a20f3edb35959f7062b8de0012a89 Mon Sep 17 00:00:00 2001 From: tywalch Date: Sun, 16 Aug 2020 11:57:31 -0400 Subject: [PATCH 2/8] scan is returning values do not match filter params, looks like an bug in the docclient --- src/entity.js | 99 ++++++++++++++++++++++++++------------------------- src/schema.js | 2 +- 2 files changed, 51 insertions(+), 50 deletions(-) diff --git a/src/entity.js b/src/entity.js index 2245e53d..148c83ce 100644 --- a/src/entity.js +++ b/src/entity.js @@ -5,28 +5,6 @@ const { FilterFactory, FilterTypes } = require("./filters"); const validations = require("./validations"); const { clauses } = require("./clauses"); -const utilities = { - structureFacets: function ( - structure, - { index, type, name }, - i, - attributes, - indexSlot, - ) { - let next = attributes[i + 1] !== undefined ? attributes[i + 1].name : ""; - let facet = { index, name, type, next }; - structure.byAttr[name] = structure.byAttr[name] || []; - structure.byAttr[name].push(facet); - structure.byType[type] = structure.byType[type] || []; - structure.byType[type].push(facet); - structure.byFacet[name] = structure.byFacet[name] || []; - structure.byFacet[name][i] = structure.byFacet[name][i] || []; - structure.byFacet[name][i].push(facet); - structure.bySlot[i] = structure.bySlot[i] || []; - structure.bySlot[i][indexSlot] = facet; - } -}; - class Entity { constructor(model, config = {}) { this._validateModel(model); @@ -50,6 +28,7 @@ class Entity { ); }; } + this.modelAttributeIdentifier = "__edb_e_" } find(facets = {}) { @@ -249,24 +228,33 @@ class Entity { } _deconstructKeys(keyType, key) { + if (typeof key !== "string" || key.length === 0) { + return null; + } let index = ""; let accessPattern = this.model.translations.indexes.fromIndexToAccessPattern[index]; - let {prefix} = this.model.prefixes[index][keyType]; + let {prefix, isCustom} = this.model.prefixes[index][keyType]; let {facets} = this.model.indexes[accessPattern][keyType]; let names = []; let pattern = `^${this._regexpEscape(prefix)}`; for (let facet of facets) { let { label, name } = this.model.schema.attributes[facet]; - pattern += `#${this._regexpEscape(label)}_(.+)`; + if (isCustom) { + pattern += `${this._regexpEscape(label === undefined ? "" : label)}(.+)`; + } else { + pattern += `#${this._regexpEscape(label === undefined ? name : label)}_(.+)`; + } + names.push(name); } pattern += "$"; let regex = RegExp(pattern); + let match = key.match(regex); let results = {} if (!match) { - results[keyType] = key + throw new Error("Entity keys do not match model"); } else { for (let i = 0; i < names.length; i++) { results[names[i]] = match[i+1]; @@ -276,23 +264,25 @@ class Entity { } _formatReturnPager(item) { - return item; if (item === null || typeof item !== "object" || Object.keys(item).length === 0) { return item; } - let pager = {}; - for (let {name} of this.model.facets.byIndex[""].all) { - pager[name] = item[name]; + let index = "" + let pkName = this.model.translations.keys[index].pk; + let skName = this.model.translations.keys[index].sk; + let pkFacets = this._deconstructKeys(KeyTypes.pk, item[pkName]); + let skFacets = this._deconstructKeys(KeyTypes.sk, item[skName]); + let pager = {...pkFacets}; + if (skFacets && Object.keys(skFacets).length) { + pager = {...skFacets, ...pkFacets}; } return pager; } _formatSuppliedPager(item) { - return item; if (typeof item !== "object" || Object.keys(item).length === 0) { return item; } - // let expectedFacets = this.model.facets.byIndex[""].all.map(facet => facet.name); let pk = this._expectFacets(item, this.model.facets.byIndex[""].pk) let sk = this._expectFacets(item, this.model.facets.byIndex[""].sk) let index = ""; @@ -442,7 +432,7 @@ class Entity { TableName: this.model.table, ExpressionAttributeNames: this._mergeExpressionsAttributes( filter.ExpressionAttributeNames, - keyExpressions.ExpressionAttributeNames, + keyExpressions.ExpressionAttributeNames ), ExpressionAttributeValues: this._mergeExpressionsAttributes( filter.ExpressionAttributeValues, @@ -450,6 +440,9 @@ class Entity { ), FilterExpression: `(begins_with(#pk, :pk)`, }; + params.ExpressionAttributeNames["#" + this.modelAttributeIdentifier] = this.modelAttributeIdentifier; + params.ExpressionAttributeValues[":" + this.modelAttributeIdentifier] = this.model.entity; + params.FilterExpression = `${params.FilterExpression} AND #${this.modelAttributeIdentifier} = :${this.modelAttributeIdentifier}` if (hasSortKey) { params.FilterExpression = `${params.FilterExpression} AND begins_with(#sk, :sk))`; } @@ -502,7 +495,7 @@ class Entity { Item: { ...transatedFields, ...updatedKeys, - __edb_e__: this.model.entity, + [this.modelAttributeIdentifier]: this.model.entity, }, TableName: this.model.table, }; @@ -1057,7 +1050,6 @@ class Entity { if (!prefixes) { throw new Error(`Invalid index: ${index}`); } - // let sk = []; let pk = this._makeKey( prefixes.pk.prefix, facets.pk, @@ -1082,9 +1074,9 @@ class Entity { let facet = facets[i]; let { label, name } = this.model.schema.attributes[facet]; if (isCustom) { - key = `${key}${label}`; + key = `${key}${label === undefined ? "" : label}`; } else { - key = `${key}#${label || name}_`; + key = `${key}#${label === undefined ? name : label}_`; } if (supplied[name] !== undefined) { key = `${key}${supplied[name]}`; @@ -1247,13 +1239,14 @@ class Entity { type: KeyTypes.pk, field: index.pk.field || "", facets: [...facetArray], + isCustom: parsedPKFacets.isCustom }; let sk = {}; - + let parsedSKFacets = {} if (hasSk) { - let parseSKFacets = this._parseFacets(index.sk.facets); - let { facetArray, facetLabels } = parseSKFacets; - customFacets.sk = parseSKFacets.isCustom; + parsedSKFacets = this._parseFacets(index.sk.facets); + let { facetArray, facetLabels } = parsedSKFacets; + customFacets.sk = parsedSKFacets.isCustom; facets.labels = Object.assign({}, facets.labels, facetLabels); sk = { facetLabels, @@ -1262,14 +1255,11 @@ class Entity { type: KeyTypes.sk, field: index.sk.field || "", facets: [...facetArray], + isCustom: parsedSKFacets.isCustom }; facets.fields.push(sk.field); } - // if (inCollection) { - // collections[index.collection] = index.collection; - // } - let definition = { pk, sk, @@ -1333,16 +1323,27 @@ class Entity { all: attributes, collection: index.collection, }; - - attributes.forEach((facet, j) => - utilities.structureFacets(facets, facet, j, attributes, i), - ); + + + attributes.forEach(({index, type, name}, j) => { + let next = attributes[j + 1] !== undefined ? attributes[j + 1].name : ""; + let facet = { index, name, type, next }; + facets.byAttr[name] = facets.byAttr[name] || []; + facets.byAttr[name].push(facet); + facets.byType[type] = facets.byType[type] || []; + facets.byType[type].push(facet); + facets.byFacet[name] = facets.byFacet[name] || []; + facets.byFacet[name][j] = facets.byFacet[name][j] || []; + facets.byFacet[name][j].push(facet); + facets.bySlot[j] = facets.bySlot[j] || []; + facets.bySlot[j][i] = facet; + }); } if (facets.byIndex[""] === undefined) { throw new Error("Schema is missing an index definition for the table's main index. Please update the schema to include an index without a specified name to define the table's natural index"); } - + return { facets, indexHasSortKeys, diff --git a/src/schema.js b/src/schema.js index 9124beac..0362a472 100644 --- a/src/schema.js +++ b/src/schema.js @@ -5,7 +5,7 @@ class Attribute { constructor(definition = {}) { this.name = definition.name; this.field = definition.field || definition.name; - this.label = definition.label || definition.name; + this.label = definition.label; this.readOnly = !!definition.readOnly; this.required = !!definition.required; this.cast = this._makeCast(definition.name, definition.cast); From 6faac5b25b3c73a39e6b7b9138c070e6bd20c429 Mon Sep 17 00:00:00 2001 From: tywalch Date: Tue, 29 Sep 2020 16:49:18 -0400 Subject: [PATCH 3/8] final code touches to page, additional testing --- src/entity.js | 103 +++++++---- src/where.js | 8 +- test/connected.crud.spec.js | 66 ++----- test/connected.page.spec.js | 325 +++++++++++++++++++++++++++++++++++ test/connected.where.spec.js | 65 ++++++- test/debug.js | 256 +++++++++++++++++++++++++++ test/offline.entity.spec.js | 11 +- 7 files changed, 740 insertions(+), 94 deletions(-) create mode 100644 test/connected.page.spec.js create mode 100644 test/debug.js diff --git a/src/entity.js b/src/entity.js index b9d42cbd..aae0eba5 100644 --- a/src/entity.js +++ b/src/entity.js @@ -35,7 +35,7 @@ class Entity { ); }; } - this.modelAttributeIdentifier = "__edb_e_" + this.modelAttributeIdentifier = "__edb_e__" } find(facets = {}) { @@ -184,7 +184,7 @@ class Entity { return data; } - formatResponse(response, config = {}) { + formatResponse(index, response, config = {}) { let stackTrace = new Error(); try { let results; @@ -218,7 +218,10 @@ class Entity { if (config.pager) { - let nextPage = this._formatReturnPager(response.LastEvaluatedKey || null); + let nextPage = response.LastEvaluatedKey || null; + if (!config.raw && !config.lastEvaluatedKeyRaw) { + nextPage = this._formatReturnPager(index, nextPage, results[results.length - 1]); + } results = [nextPage, results]; } @@ -238,11 +241,11 @@ class Entity { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } - _deconstructKeys(keyType, key) { + _deconstructKeys(index, keyType, key, backupFacets = {}) { if (typeof key !== "string" || key.length === 0) { return null; } - let index = ""; + // let index = ""; let accessPattern = this.model.translations.indexes.fromIndexToAccessPattern[index]; let {prefix, isCustom} = this.model.prefixes[index][keyType]; let {facets} = this.model.indexes[accessPattern][keyType]; @@ -255,17 +258,24 @@ class Entity { } else { pattern += `#${this._regexpEscape(label === undefined ? name : label)}_(.+)`; } - names.push(name); } pattern += "$"; - let regex = RegExp(pattern); - let match = key.match(regex); let results = {} if (!match) { - throw new Error("Entity keys do not match model"); + if (Object.keys(backupFacets || {}).length === 0) { + // this can occur when a scan is performed but returns no results given the current filters or record timing + return {}; + } + for (let facet of facets) { + if (backupFacets[facet] === undefined) { + throw new Error("Warning: LastEvaluatedKey contains entity that does not match the entity used to query. Use {lastEvaulatedKeyRaw: true} option."); + } else { + results[facet] = backupFacets[facet]; + } + } } else { for (let i = 0; i < names.length; i++) { results[names[i]] = match[i+1]; @@ -274,31 +284,56 @@ class Entity { return results; } - _formatReturnPager(item) { - if (item === null || typeof item !== "object" || Object.keys(item).length === 0) { - return item; - } - let index = "" + _deconstructIndex(index = "", lastEvaluated, lastReturned) { let pkName = this.model.translations.keys[index].pk; let skName = this.model.translations.keys[index].sk; - let pkFacets = this._deconstructKeys(KeyTypes.pk, item[pkName]); - let skFacets = this._deconstructKeys(KeyTypes.sk, item[skName]); - let pager = {...pkFacets}; + let pkFacets = this._deconstructKeys(index, KeyTypes.pk, lastEvaluated[pkName], lastReturned); + let skFacets = this._deconstructKeys(index, KeyTypes.sk, lastEvaluated[skName], lastReturned); + let facets = {...pkFacets}; if (skFacets && Object.keys(skFacets).length) { - pager = {...skFacets, ...pkFacets}; + facets = {...skFacets, ...pkFacets}; + } + return facets; + } + + _formatReturnPager(index = "", lastEvaluated, lastReturned) { + if (lastEvaluated === null || typeof lastEvaluated !== "object" || Object.keys(lastEvaluated).length === 0) { + return lastEvaluated; } + + let pager = this._deconstructIndex(index, lastEvaluated, lastReturned); + let tableIndex = ""; + // lastEvaluatedKeys from query calls include the index pk/sk as well as the table index's pk/sk + if (index !== tableIndex) { + pager = {...pager, ...this._deconstructIndex(tableIndex, lastEvaluated, lastReturned)}; + } + + if (Object.keys(pager).length === 0) { + // In this case no suitable record could be found be the deconstructed pager. + // This can be valid in cases where a scan is performed but returns no results. + return null; + } + return pager; } - _formatSuppliedPager(item) { + _constructPagerIndex(index = "", item) { + let pk = this._expectFacets(item, this.model.facets.byIndex[index].pk) + let sk = this._expectFacets(item, this.model.facets.byIndex[index].sk) + let keys = this._makeIndexKeys(index, pk, sk); + return this._makeParameterKey(index, keys.pk, ...keys.sk); + } + + _formatSuppliedPager(index = "", item) { if (typeof item !== "object" || Object.keys(item).length === 0) { return item; } - let pk = this._expectFacets(item, this.model.facets.byIndex[""].pk) - let sk = this._expectFacets(item, this.model.facets.byIndex[""].sk) - let index = ""; - let keys = this._makeIndexKeys(index, pk, sk); - return this._makeParameterKey(index, keys.pk, ...keys.sk); + let tableIndex = ""; + let pager = this._constructPagerIndex(index, item); + if (index !== tableIndex) { + pager = {...pager, ...this._constructPagerIndex(tableIndex, item)} + } + return pager } _applyParameterOptions(params, ...options) { @@ -316,6 +351,7 @@ class Entity { if (option.originalErr) config.originalErr = true; if (option.raw) config.raw = true; if (option.pager) config.pager = true; + if (option.lastEvaluatedKeyRaw === true) config.lastEvaluatedKeyRaw = true; config.page = Object.assign({}, config.page, option.page); config.params = Object.assign({}, config.params, option.params); return config; @@ -330,7 +366,11 @@ class Entity { } if (Object.keys(config.page || {}).length) { - parameters.ExclusiveStartKey = this._formatSuppliedPager(config.page); + if (config.raw || config.lastEvaluatedKeyRaw) { + parameters.ExclusiveStartKey = config.page; + } else { + parameters.ExclusiveStartKey = this._formatSuppliedPager(params.IndexName, config.page); + } } return parameters; @@ -343,7 +383,8 @@ class Entity { raw: options.raw, params: options.params || {}, page: options.page, - pager: !!options.pager + pager: !!options.pager, + lastEvaluatedKeyRaw: !!options.lastEvaluatedKeyRaw }; let parameters = Object.assign({}, params); let stackTrace = new Error(); @@ -351,9 +392,9 @@ class Entity { let response = await this.client[method](parameters).promise(); if (method === "put" || method === "create") { // a VERY hacky way to deal with PUTs - return this.formatResponse(parameters, config); + return this.formatResponse(parameters.IndexName, parameters, config); } else { - return this.formatResponse(response, config); + return this.formatResponse(parameters.IndexName, response, config); } } catch (err) { if (config.originalErr) { @@ -449,7 +490,7 @@ class Entity { _makeScanParam(filter = {}) { let indexBase = ""; let hasSortKey = this.model.lookup.indexHasSortKeys[indexBase]; - let facets = this.model.facets.byIndex[indexBase]; + // let facets = this.model.facets.byIndex[indexBase]; let {pk, sk} = this._makeIndexKeys(indexBase); let keys = this._makeParameterKey( indexBase, @@ -785,11 +826,11 @@ class Entity { ); if (isIncomplete) { throw new Error( - `Incomplete facets: Without the facets ${incomplete + `Incomplete facets: Without the facets '${incomplete .filter((val) => val !== undefined) .join( ", ", - )} the following access patterns ${incompleteAccessPatterns + )}' the following access patterns ${incompleteAccessPatterns .filter((val) => val !== undefined) .join(", ")}cannot be updated.`, ); diff --git a/src/where.js b/src/where.js index 625c1f03..9e2941a8 100644 --- a/src/where.js +++ b/src/where.js @@ -59,11 +59,11 @@ class WhereFactory { } let expression = template(path, ...attrValues); return expression.trim(); - } else if (typeof property === "string") { - // todo: parse string + // } else if (typeof property === "string") { + // // todo: parse string } else { // todo: proper error logging. - throw new Error("INVALID PROPERTY") + throw new Error(`Invalid Attribute in where clause passed to operation '${type}'. Use injected attributes only.`); } } } @@ -117,7 +117,7 @@ class WhereFactory { let operations = this._buildOperations(setName, setValue, getValueCount); let expression = filterFn(attributes, operations, ...params); if (typeof expression !== "string") { - throw new Error("Invalid filter response. Expected result to be of type string"); + throw new Error("Invalid response from where clause callback. Expected return result to be of type string"); } state.query.filter[expressionType] = this._concatFilterExpression( state.query.filter[expressionType], diff --git a/test/connected.crud.spec.js b/test/connected.crud.spec.js index ab28b647..62cc683e 100644 --- a/test/connected.crud.spec.js +++ b/test/connected.crud.spec.js @@ -339,6 +339,17 @@ describe("Entity", async () => { expect(err.message).to.be.equal("The conditional request failed"); } expect(patchResultsTwo).to.be.null + }); + + it("Should pass back the original dynamodb error when originalErr is set to true", async () => { + let id = uuidv4(); + let sector = "A1"; + let [success, err] = await MallStores.get({sector, id}) + .go({originalErr: true, params: {TableName: "blahblah"}}) + .then(() => [true, null]) + .catch(err => [false, err]); + expect(success).to.be.false; + expect(err.message).to.be.equal("Requested resource not found") }) }); @@ -386,51 +397,6 @@ describe("Entity", async () => { }); }); - // describe("scan", async () => { - // it ("Should scan for created records", async () => { - // let entity = uuidv4(); - // let db = new Entity({ - // service: "testing", - // entity: entity, - // table: "electro", - // version: "1", - // attributes: { - // id: { - // type: "string" - // }, - // bb: { - // type: "string" - // } - // }, - // indexes: { - // main: { - // pk: { - // field: "pk", - // facets: ["id"] - // }, - // sk: { - // field: "sk", - // facets: ["id"] - // } - // } - // } - // }, {client}); - // let puts = []; - // for (let i = 0; i < 5; i++) { - // console.log("putz", db.put({id: `${i}`, bb: `${i}`}).params()); - // puts.push(db.put({id: `${i}`, bb: `${i}`}).go({})); - // } - // await Promise.all(puts); - // await sleep(250); - // let recordparams = db.scan.filter(({id}) => id.gte("3")).params(); - // let records = await db.scan.filter(({id}) => id.gte("3")).go(); - // if (!records.length) { - // console.log("ENTITYz", recordparams); - // } - // expect(records).to.be.an("array").and.to.have.lengthOf(3); - // }) - // }) - describe("Getters/Setters", async () => { let db = new Entity( { @@ -793,12 +759,12 @@ describe("Entity", async () => { expect(stores.Items).to.be.an("array") expect(stores.Items[0]).to.have.a.property('pk') expect(stores.Items[0]).to.have.a.property('sk'); - let [nextIndex, nextStores] = await MallStores.scan.page(index); + let [nextIndex, nextStores] = await MallStores.scan.page(index, {raw: true}); expect(nextIndex).to.not.deep.equal(index); - expect(nextStores).to.be.an("array"); - if (nextStores.length) { - expect(nextStores[0]).to.not.have.a.property('pk') - expect(nextStores[0]).to.not.have.a.property('sk'); + expect(nextStores.Items).to.be.an("array"); + if (nextStores.Items.length) { + expect(nextStores.Items[0]).to.have.a.property('pk'); + expect(nextStores.Items[0]).to.have.a.property('sk'); } } }).timeout(10000); diff --git a/test/connected.page.spec.js b/test/connected.page.spec.js new file mode 100644 index 00000000..45c8a4ab --- /dev/null +++ b/test/connected.page.spec.js @@ -0,0 +1,325 @@ +process.env.AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1; +const AWS = require("aws-sdk"); +const { expect } = require("chai"); +AWS.config.update({ region: "us-east-1"}); +const client = new AWS.DynamoDB.DocumentClient(); +const uuid = require("uuid").v4; +const {Entity} = require("../src/entity"); + +const TasksModel = { + entity: uuid(), + version: "1", + service: "taskapp", + table: "electro", + attributes: { + task: { + type: "string", + default: () => uuid(), + }, + project: { + type: "string", + required: true, + }, + employee: { + type: "string" + }, + description: { + type: "string", + }, + points: { + type: "number", + }, + type: { + type: ["story", "defect", "epic"] + }, + comments: { + type: "any" + } + }, + indexes: { + task: { + pk: { + field: "pk", + facets: ["task"], + }, + sk: { + field: "sk", + facets: ["project", "employee"], + }, + }, + projects: { + index: "gsi1pk-gsi1sk-index", + pk: { + field: "gsi1pk", + facets: ["project"], + }, + sk: { + field: "gsi1sk", + facets: ["employee", "task"], + }, + }, + assigned: { + collection: "assignments", + index: "gsi2pk-gsi2sk-index", + pk: { + field: "gsi2pk", + facets: ["employee"], + }, + sk: { + field: "gsi2sk", + facets: ["project", "points"], + }, + }, + }, +}; + +class Tasks extends Entity { + constructor(model, {client} = {}) { + super(model, {client}); + this.name = model.entity + this.loaded = []; + } + static employees = [ + "Jack", + "Tyler", + "David", + "Shane", + "Zack", + "Stephanie", + "Georgina", + "Michele", + "Ronda", + "Paula", + "Fred" + ] + + static projects =[ + "135-53", + "460-63", + "372-55", + "552-77", + "636-33", + "360-56" + ] + + static types = ["story", "defect", "epic"] + + static points = [1, 2, 3, 5, 8, 13, 21, 50, 100]; + + static sentences = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum condimentum eros ut auctor cursus. Vivamus ac malesuada purus. Phasellus scelerisque tellus non nisi tempus, eget sagittis metus tempus. Mauris eu sapien non magna vulputate lobortis. Maecenas posuere enim et dolor ultrices, et tempus ligula scelerisque. Mauris vehicula turpis nec mi blandit convallis. Curabitur lacinia quis eros in blandit. Aliquam sed mauris auctor, tincidunt risus sed, fringilla turpis. Vestibulum molestie nec mauris vel sollicitudin. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Integer vitae orci sem. Vivamus finibus molestie lectus vel tempus. Curabitur rutrum, mauris sit amet blandit imperdiet, nibh purus molestie diam, sit amet maximus odio quam lacinia nisi. Proin laoreet dictum auctor. Mauris placerat commodo nisl in condimentum. Pellentesque non magna diam. Quisque in varius metus. Aenean lorem tellus, gravida nec egestas eu, rutrum id lectus. Ut id lacus leo. Donec dignissim id eros vitae auctor. Nunc tincidunt diam id fermentum placerat. Aliquam efficitur felis metus, id tincidunt lacus tincidunt a. Aliquam erat volutpat. Integer nec quam at metus suscipit viverra. Pellentesque non imperdiet est. Proin suscipit justo ex, eget condimentum leo imperdiet ac. Fusce efficitur purus a convallis euismod. Integer eget nibh erat. Mauris id venenatis urna. Nullam orci lorem, sollicitudin sit amet sapien ornare, euismod lacinia lacus. Aenean vel eros sagittis, dignissim orci et, posuere nunc. Maecenas vel est elit. Nam ac dui nec justo aliquet volutpat vel eget risus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer eu nisl purus. Vestibulum finibus lorem euismod, facilisis ex quis, commodo enim. Nullam placerat lobortis lacus, vitae blandit risus gravida in. Duis quis ultricies orci, non rhoncus ligula. Praesent rhoncus urna sed aliquam sodales. Nunc blandit ut quam id porta. Aliquam erat volutpat. Morbi ultrices ornare ante id dictum. Suspendisse potenti. Quisque interdum imperdiet erat. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vestibulum tincidunt erat quis vestibulum venenatis. Cras vel convallis urna. Proin imperdiet odio nisi, et euismod tortor porttitor at. Pellentesque pharetra mi sed quam cursus viverra. Pellentesque tellus nisi, placerat sed nibh iaculis, viverra rutrum ligula. Fusce condimentum scelerisque lorem non efficitur. Suspendisse ac malesuada lectus. Etiam id cursus orci, ut sollicitudin augue. Morbi et metus dapibus, feugiat risus et, accumsan ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin sodales et nibh at lacinia. Morbi velit tellus, ultricies ac venenatis ac, dignissim eu neque. Quisque placerat magna eget nibh porttitor, a ultricies turpis pellentesque. Phasellus fringilla egestas erat, id interdum sem imperdiet et. Duis diam lorem, feugiat at lacinia sit amet, elementum at libero. Sed vehicula eu velit ut porta. Phasellus ac fermentum urna, a viverra justo. Aliquam vel mi et libero suscipit finibus ut vel purus. Aenean id leo ut felis sollicitudin finibus id sed urna. Donec et purus purus. Morbi euismod condimentum augue, et hendrerit justo elementum et. Nulla dictum et mauris id hendrerit. Aenean non dolor lobortis, convallis metus quis, euismod felis. Curabitur et pretium nunc, a accumsan enim. Donec tempor orci vel pharetra commodo. Mauris sit amet metus porttitor, porttitor ipsum non, aliquet sapien. Proin eu pharetra dolor, sed ultricies arcu. Donec venenatis elementum nisl at varius. Ut sed scelerisque risus. Vestibulum quis leo non sapien eleifend auctor id consequat velit. Maecenas porta felis vitae velit dictum elementum. Vivamus rutrum sodales cursus. Nullam eros ante, fermentum nec nunc non, bibendum iaculis dui. Mauris enim justo, feugiat vitae massa eget, lobortis iaculis purus. Suspendisse tellus nibh, semper vitae purus sed, luctus cursus turpis. Donec id vehicula odio. Nunc non diam ac est vestibulum mattis. Phasellus mattis ipsum eu condimentum convallis. Vivamus tempor massa eu ullamcorper condimentum. Nam ultricies in mauris sit amet pellentesque. Suspendisse dui lectus, pellentesque in volutpat nec, consectetur vitae mi. Duis mi libero, laoreet at turpis vitae, rutrum commodo nulla. Maecenas viverra arcu in elit aliquet malesuada. Integer diam erat, egestas et nulla a, gravida condimentum massa. Nulla malesuada ex ut cursus viverra. In commodo lacinia libero, ac elementum lectus maximus ut. Donec eget lorem quis mi suscipit sagittis. Nunc placerat odio quis mauris iaculis, maximus rutrum est tempor. Integer sollicitudin ipsum urna, at malesuada diam accumsan quis. Praesent eu eleifend urna. Pellentesque molestie diam sit amet tristique condimentum. Aliquam fringilla et nisi vel fermentum. Vivamus elit leo, maximus in dolor ac, vestibulum commodo mi. Ut id nisi nec massa faucibus vulputate sit amet nec ligula. Integer sapien purus, bibendum in sollicitudin a, mollis a purus. Donec quis libero nisl. Mauris blandit ipsum nibh, at ultricies sapien volutpat quis. Integer nec leo efficitur, posuere nisi sed, placerat ante. Nullam malesuada nec tellus eu condimentum. Vestibulum porttitor fermentum dui, vel efficitur neque rhoncus vel. Maecenas sollicitudin gravida leo non mattis. Morbi vehicula mauris eu sagittis ullamcorper. Nam pellentesque molestie consectetur. Vivamus finibus enim justo, eu lacinia arcu iaculis et. Nullam iaculis diam sit amet velit varius, egestas rhoncus nulla venenatis. Duis at eros nibh. Sed sit amet finibus arcu. Aliquam venenatis felis sit amet odio blandit, ut faucibus tellus volutpat. Integer sit amet leo neque. Nullam ullamcorper ullamcorper turpis, vitae feugiat eros varius egestas. Morbi varius luctus augue, quis egestas eros fermentum vel. Nam semper tincidunt ornare. Sed at blandit justo, nec volutpat erat. Vivamus consequat, sapien at laoreet vulputate, urna ante pharetra libero, at faucibus lacus nunc sit amet turpis. Cras luctus diam sit amet bibendum fringilla. Nullam at erat a metus consectetur congue. Nulla accumsan nisl ac lacus vulputate iaculis. Sed interdum aliquet ligula, sit amet rutrum nisi aliquam et. Etiam faucibus at erat nec congue. Integer sed sapien ac lacus finibus pellentesque. Sed convallis erat enim, eget mattis tellus tincidunt ac. Cras nec ultricies purus, sed vestibulum est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vivamus sit amet nulla quis odio feugiat venenatis. Proin quis elit ante. Vestibulum nec magna quis magna bibendum dignissim. Morbi venenatis ligula et enim semper tincidunt. In hac habitasse platea dictumst. Nulla sodales arcu eu ipsum imperdiet suscipit id sed augue. Pellentesque accumsan diam purus, sed elementum orci consequat quis. Sed nec dolor quis turpis finibus fermentum. Donec imperdiet elementum leo varius fermentum. Maecenas vitae euismod leo, a euismod nunc. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque sit amet enim et libero eleifend eleifend. Quisque efficitur neque nulla, quis lobortis orci tempor et. Duis non viverra ligula. Sed elementum, ex sit amet molestie lobortis, sapien diam condimentum tortor, nec mollis mi odio ac lacus. Mauris non augue turpis. Cras venenatis faucibus augue sit amet vulputate. Pellentesque sit amet tempus nisl. Aenean ultrices neque nec ipsum malesuada tincidunt. Pellentesque iaculis mauris magna, ut ornare nulla congue sed. Nullam quis convallis leo. Donec bibendum a arcu pharetra dictum. Sed varius commodo lectus eu bibendum. Vivamus vel condimentum ligula, nec pharetra nunc. Suspendisse malesuada est in eros accumsan, nec tempus diam lacinia. Integer vel dui in ante tempor pharetra in id magna. Aliquam erat volutpat. Cras ligula est, fringilla quis egestas a, ornare at nisi. Integer cursus imperdiet libero ut eleifend. Donec mollis arcu eu lacus euismod egestas. Sed a arcu nec turpis vestibulum porta eu nec elit. Vivamus id tellus vitae lacus euismod vehicula in eget purus. Suspendisse potenti. Praesent vel lectus pulvinar, feugiat urna eu, sodales lacus. Ut sed leo in nisl vestibulum bibendum eget id velit. Donec efficitur lacus ut commodo gravida. Duis placerat, turpis eget placerat pulvinar, nisi est ultricies velit, in ullamcorper lorem odio vel lorem. Cras velit velit, cursus imperdiet suscipit sed, pretium quis purus. Cras neque est, sodales nec congue in, faucibus eget magna. Nulla feugiat, massa at blandit lacinia, tellus dui suscipit velit, id eleifend lacus diam at orci. Donec sit amet ultrices nulla. Maecenas quis sapien a libero laoreet ultrices. Curabitur et ullamcorper elit. Phasellus faucibus volutpat orci. Curabitur suscipit mattis aliquet. Etiam vehicula varius ante, sed blandit leo mattis quis. Donec eu sapien cursus lacus tempor ultricies. Proin fermentum diam id ligula aliquam varius. Vestibulum ac finibus sapien. Donec ultricies eros vel tellus porttitor cursus. Morbi auctor ipsum metus, sed malesuada orci convallis vel. Vestibulum quis leo eget quam malesuada faucibus ac eu erat. Praesent arcu eros, pulvinar non ante id, luctus sodales risus. In non arcu eget justo rutrum tempus vel sit amet nulla. Maecenas eu diam nisl. Phasellus vitae felis vehicula, pulvinar enim nec, pulvinar felis. Nullam ac justo dui. Nam lectus nibh, porttitor in lacus sit amet, hendrerit maximus orci. Integer scelerisque massa porttitor felis ultrices accumsan. Duis rhoncus lectus ut risus suscipit placerat. Morbi tristique egestas mi ac accumsan. Proin vulputate tempor dui quis congue. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam nisl ipsum, lacinia id interdum non, venenatis vel nisl. Maecenas tincidunt urna nisl, id ullamcorper ipsum placerat et. Etiam congue lacus vel justo aliquam, id sollicitudin turpis tempor. Phasellus vehicula arcu sed risus hendrerit interdum. Vivamus sit amet risus lobortis, egestas diam non, placerat tellus. In vitae diam augue. In blandit, metus sed pharetra mattis, mauris nulla consectetur eros, sit amet cursus metus mauris nec lacus. Vivamus suscipit ac elit a ultricies. Aenean accumsan sapien at lectus cursus eleifend. Sed iaculis non metus sit amet lacinia. Aliquam id sapien a sapien vehicula tincidunt. Aenean maximus venenatis nunc et lobortis. Aliquam tristique auctor sapien a sagittis. Curabitur congue tellus turpis, at lacinia ex tincidunt ultrices. Phasellus ornare vel enim non tincidunt. Sed quis odio viverra, scelerisque est a, efficitur quam. Suspendisse dictum ultricies risus, id venenatis dui pulvinar et. Pellentesque gravida ligula nisi, et elementum magna egestas ac. Sed ornare est in interdum pretium. Proin sit amet tincidunt neque. Phasellus ac lacus vitae libero finibus eleifend quis et turpis. Nullam accumsan semper tortor non ultricies. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque semper urna id ligula placerat, in aliquet velit dapibus. Proin in velit velit. Donec mattis felis eros, pharetra facilisis nisl tristique vel. Praesent cursus rhoncus accumsan. Nullam egestas bibendum elit, at finibus metus sodales id. Nulla facilisi. Nam euismod sem arcu, nec vulputate dui blandit et. Integer pellentesque a velit ornare volutpat. In facilisis sodales risus a imperdiet. Vestibulum bibendum, dui et mollis dictum, quam tortor consectetur augue, id accumsan tortor arcu vitae eros. Etiam et libero tortor. Ut at nisl vitae mi iaculis convallis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Mauris eleifend pellentesque fringilla. Mauris vel risus justo. Mauris sed molestie lacus. Mauris sagittis id arcu a fringilla. Cras eget neque sed lorem venenatis mollis non vel mi. Aenean feugiat sem non volutpat malesuada. Sed enim mi, rhoncus a imperdiet eget, feugiat cursus nisl. Donec ut auctor nibh. Mauris vitae tempor ligula, eget sagittis lacus. Sed vitae aliquet nisi. Sed porttitor ullamcorper orci, eget volutpat sem consectetur in. Nam pretium egestas pretium. Etiam eu mattis est. Ut pulvinar lectus sit amet lectus venenatis laoreet. Nullam lectus quam, scelerisque id laoreet ut, elementum et dolor. Etiam imperdiet eu magna id ornare. Ut blandit tristique tellus. Proin feugiat dolor sit amet fermentum dignissim. Vestibulum aliquam vestibulum fermentum. Nulla auctor, dolor porta hendrerit efficitur, ex turpis facilisis diam, a sollicitudin nulla ante eu risus. Nulla iaculis, mi a mattis sollicitudin, nibh lorem rhoncus felis, vel vestibulum lacus leo ut turpis. Nullam eget ipsum id metus elementum tristique non ac mi. Praesent ut pulvinar eros. Aenean id purus ipsum. In auctor tellus quis lacus imperdiet vehicula. Duis volutpat sodales maximus. Suspendisse placerat tellus tortor. Aenean bibendum erat quis enim faucibus finibus. Aenean urna risus, dapibus ac pellentesque vitae, gravida quis nibh. Ut dui augue, suscipit et purus vel, tristique condimentum dolor. Donec vestibulum iaculis posuere. Maecenas congue nulla sed faucibus porta. Proin in feugiat nisl.`.split(".") + + getRandomNumber(min, max) { + return Math.floor(Math.random() * (max - min) ) + min; + } + + generateRandomComments() { + let comments = []; + for (let i = 0; i < this.getRandomNumber(2, 6); i++) { + let text = []; + for (let j = 0; j < this.getRandomNumber(4, 10); j++) { + text.push(Tasks.sentences[this.getRandomNumber(0, Tasks.sentences.length)]); + } + comments.push({ + author: Tasks.employees[this.getRandomNumber(0, Tasks.employees.length)], + text: text.join(".") + "." + }) + } + return comments; + } + + generateRandomRecord() { + return { + employee: Tasks.employees[this.getRandomNumber(0, Tasks.employees.length)], + project: Tasks.projects[this.getRandomNumber(0, Tasks.projects.length)], + type: Tasks.types[this.getRandomNumber(0, Tasks.types.length)], + points: Tasks.points[this.getRandomNumber(0, Tasks.points.length)], + description: Tasks.sentences[this.getRandomNumber(0, Tasks.sentences.length)], + comments: this.generateRandomComments() + } + } + + static deepCopy(o) { + return JSON.parse(JSON.stringify(o)); + } + + static compareTasks(tasksOne = [], tasksTwo = []) { + if (tasksOne.length !== tasksTwo.length) { + throw new Error("Tasks not the same length"); + } + tasksOne = Tasks.deepCopy(tasksOne).sort((a, z) => (a.pk + a.sk) - (z.pk + z.sk)); + tasksTwo = Tasks.deepCopy(tasksOne).sort((a, z) => (a.pk + a.sk) - (z.pk + z.sk)); + for (let i = 0; i < tasksOne.length; i++) { + let pkMatch = tasksOne[i].pk = tasksTwo[i].pk; + let skMatch = tasksOne[i].sk = tasksTwo[i].sk; + if (!pkMatch) { + throw new Error(`No PK Match, tasksOne: ${tasksOne[i].pk}, tasksTwo: ${tasksTwo[i].pk}`); + } + if (!skMatch) { + throw new Error(`No SK Match, tasksOne: ${tasksOne[i].sk}, tasksTwo: ${tasksTwo[i].sk}`); + } + } + } + + async load(n = 0) { + let inserts = []; + for (let i = 0; i < n; i++) { + let randomRecord = this.generateRandomRecord(); + this.loaded.push(randomRecord); + inserts.push(this.put(randomRecord).go()) + } + return Promise.all(inserts) + } + + async paginate(limit, pages, query, test = (data) => data) { + var next; + let results = []; + for (let i = 0; i < pages; i++) { + if (next === null) { + break; + } + var [next, tasks] = await query(limit, next).then(test); + results = [...results, ...tasks]; + } + return results + } + + filterLoaded(facets = {}) { + return Tasks.deepCopy(this.loaded.filter(record => { + for (let facet of Object.keys(facets)) { + if (record[facet] !== facets[facet]) { + return false + } + } + return true; + })); + }; +} + +describe("Page", async () => { + const total = 20; + const tasks = new Tasks(TasksModel, {client}); + + before(async () => { + await tasks.load(total); + }) + + it("Should paginate through all records", async () => { + let tests = [ + { + type: "query", + input: { + facets: {project: Tasks.projects[0]}, + index: "projects", + }, + output: { + pageKeys: ["task", "project", "employee"] + }, + }, { + type: "query", + input: { + facets: {employee: Tasks.employees[0]}, + index: "assigned", + }, + output: { + pageKeys: ["points", "project", "employee"] + }, + }, { + type: "scan", + output: { + pageKeys: ["task", "project", "employee"] + }, + } + ] + + for (let test of tests) { + let query; + let loaded; + let testPage = (([page, tasks]) => { + if (page !== null) expect(page).to.have.keys(test.output.pageKeys); + return [page, tasks]; + }); + + if (test.type === "scan") { + query = () => tasks.scan.page(); + loaded = tasks.loaded; + } else { + query = () => tasks.query[test.input.index](test.input.facets).page(); + loaded = tasks.filterLoaded(test.input.facets); + } + + let results = await tasks.paginate(2, total, query, testPage); + expect(() => Tasks.compareTasks(results, loaded)).to.not.throw; + } + }).timeout(10000); + + it("Should not accept incomplete page facets", async () => { + let tests = [ + { + type: "query", + input: { + facets: {project: Tasks.projects[0]}, + index: "projects", + page: {task: "1234", project: undefined} + }, + output: { + error: "Incomplete or invalid key facets supplied. Missing properties: project" + }, + }, { + type: "query", + input: { + facets: {employee: Tasks.employees[0]}, + index: "assigned", + page: {task: "1234", project: "anc"} + }, + output: { + error: "Incomplete or invalid key facets supplied. Missing properties: employee" + }, + }, { + type: "scan", + input: { + page: {task: "1234", project: undefined} + }, + output: { + error: "Incomplete or invalid key facets supplied. Missing properties: project, employee" + }, + } + ]; + + for (let test of tests) { + let query = test.type === "scan" + ? () => tasks.scan.page(test.input.page) + : () => tasks.query[test.input.index](test.input.facets).page(test.input.page); + try { + await query() + } catch(err) { + expect(err.message).to.be.equal(test.output.error); + } + } + }).timeout(10000); + + it("Should paginate and return raw results", async () => { + let results = await tasks.scan.page(null, {raw: true}); + expect(results).to.be.an("array").and.have.length(2); + let [page, items] = results; + expect(items.Items).to.not.be.undefined + expect(items.Items).to.be.an("array"); + expect(page).to.be.an('object').that.has.all.keys('pk', 'sk'); + }).timeout(10000); + + it("Should paginate and return normal results but the real lastEvaluated key as received via lastEvaluatedKeyRaw", async () => { + let results = await tasks.scan.page(null, {lastEvaluatedKeyRaw: true}); + expect(results).to.be.an("array").and.have.length(2); + let [page, items] = results; + expect(items).to.be.an("array"); + if (items[0]) { + expect(items[0]).to.be.an('object').that.has.all.keys(...Object.keys(TasksModel.attributes)); + } + expect(page).to.be.an('object').that.has.all.keys('pk', 'sk'); + }).timeout(10000); + + it("Should require a dynamodb client object to use the page method", () => { + let tasks = new Tasks(TasksModel); + let query = () => tasks.query.task({task: "abc"}).page(); + expect(query).to.throw("No client defined on model") + }) +}); \ No newline at end of file diff --git a/test/connected.where.spec.js b/test/connected.where.spec.js index 1ba691e2..04a328b3 100644 --- a/test/connected.where.spec.js +++ b/test/connected.where.spec.js @@ -32,6 +32,9 @@ describe("General", async () => { }, dangerous: { type: "boolean" + }, + complex: { + type: "any" } }, filters: {}, @@ -236,10 +239,6 @@ describe("General", async () => { ${name(animal)} = ${value(animal, "Pig")} `) .go(); - console.log("WHERE", WhereTests.query - .farm({pen}) - .where(({animal, dangerous}, {value, name, between}) => `${name(animal)} = ${value(animal, "Pig")} AND ${between(dangerous, "2020-09-25", "2020-09-28")}`) - .params()) expect(animals) .to.be.an("array") .and.have.length(1); @@ -339,5 +338,61 @@ describe("General", async () => { ${notReal(dangerous)} `) .params()).to.throw("notReal is not a function") - }) + }); + + it("Should allow for complex types in where clause", () => { + let params = WhereTests.query.farm({pen}) + .where(({complex}, {gte}) => ` + ${gte(complex[0].coordinates.y, -56.0344)} + `) + .params(); + expect(params).to.deep.equal({ + KeyConditionExpression: '#pk = :pk and begins_with(#sk1, :sk1)', + TableName: 'electro', + ExpressionAttributeNames: { + '#complex': 'complex', + '#coordinates': 'coordinates', + '#y': 'y', + '#pk': 'pk', + '#sk1': 'sk' + }, + ExpressionAttributeValues: { + ':complex_w1': -56.0344, + ':pk': `$tests_1#pen_${pen}`, + ':sk1': '$filters#row_' + }, + FilterExpression: '\n\t\t\t\t#complex[0].#coordinates.#y >= :complex_w1\n\t\t\t' + }) + }); + + it("Should not allow random values to passed to where operations", () => { + let query = () => WhereTests.query.farm({pen}).where((attr, op) => op.eq({}, "invalid")).params(); + expect(query).to.throw(`Invalid Attribute in where clause passed to operation 'eq'. Use injected attributes only.`); + }); + + it("Must validate the response of a where clause callback is a string", () => { + let query = () => WhereTests.query.farm({pen}).where((attr, op) => null).params(); + expect(query).to.throw("Invalid response from where clause callback. Expected return result to be of type string"); + }); + + it("Where clause should be able to be used more than once, which will cause an implicit 'and'", () => { + let params = WhereTests.query.farm({pen}).where(({animal}, {eq}) => eq(animal, "Chicken")).where(({dangerous}, {eq}) => eq(dangerous, true)).params(); + expect(params).to.deep.equal({ + KeyConditionExpression: '#pk = :pk and begins_with(#sk1, :sk1)', + TableName: 'electro', + ExpressionAttributeNames: { + '#animal': 'animal', + '#dangerous': 'dangerous', + '#pk': 'pk', + '#sk1': 'sk' + }, + ExpressionAttributeValues: { + ':animal_w1': 'Chicken', + ':dangerous_w1': true, + ':pk': `$tests_1#pen_${pen}`, + ':sk1': '$filters#row_' + }, + FilterExpression: '(#animal = :animal_w1) AND #dangerous = :dangerous_w1' + }); + }); }) diff --git a/test/debug.js b/test/debug.js new file mode 100644 index 00000000..4603e952 --- /dev/null +++ b/test/debug.js @@ -0,0 +1,256 @@ + +process.env.AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1; +const AWS = require("aws-sdk"); +AWS.config.update({ region: "us-west-2", endpoint: "http://localhost:8000"}); +const dynamo = new AWS.DynamoDB(); +const client = new AWS.DynamoDB.DocumentClient(); +const uuid = require("uuid").v4; +const {Entity} = require("../src/entity"); + +function makeTable() { + return dynamo.createTable({ + TableName: 'electro', + KeySchema: [ + { AttributeName: 'pk', KeyType: 'HASH' }, + { AttributeName: 'sk', KeyType: 'RANGE' }, + ], + AttributeDefinitions: [ + { AttributeName: 'pk', AttributeType: 'S' }, + { AttributeName: 'sk', AttributeType: 'S' }, + { AttributeName: 'gsi1pk', AttributeType: 'S' }, + { AttributeName: 'gsi1sk', AttributeType: 'S' }, + { AttributeName: 'gsi2pk', AttributeType: 'S' }, + { AttributeName: 'gsi2sk', AttributeType: 'S' }, + ], + GlobalSecondaryIndexes: [ + { + IndexName: 'gsi1pk-gsi1sk-index', + KeySchema: [ + { AttributeName: 'gsi1pk', KeyType: 'HASH' }, + { AttributeName: 'gsi1sk', KeyType: 'RANGE' }, + ], + Projection: { + ProjectionType: 'ALL', + }, + }, + { + IndexName: 'gsi2pk-gsi2sk-index', + KeySchema: [ + { AttributeName: 'gsi2pk', KeyType: 'HASH' }, + { AttributeName: 'gsi2sk', KeyType: 'RANGE' }, + ], + Projection: { + ProjectionType: 'ALL', + }, + }, + ], + BillingMode: 'PAY_PER_REQUEST', + }).promise(); +} + +const TasksModel = { + entity: "anytype", + version: "1", + service: "taskapp", + table: "electro", + attributes: { + task: { + type: "string", + default: () => uuid(), + }, + project: { + type: "string", + required: true, + }, + employee: { + type: "string" + }, + description: { + type: "string", + }, + points: { + type: "number", + }, + type: { + type: ["story", "defect", "epic"] + }, + comments: { + type: "any" + } + }, + indexes: { + task: { + pk: { + field: "pk", + facets: ["task"], + }, + sk: { + field: "sk", + facets: ["project", "employee"], + }, + }, + projects: { + index: "gsi1pk-gsi1sk-index", + pk: { + field: "gsi1pk", + facets: ["project"], + }, + sk: { + field: "gsi1sk", + facets: ["employee", "task"], + }, + }, + assigned: { + collection: "assignments", + index: "gsi2pk-gsi2sk-index", + pk: { + field: "gsi2pk", + facets: ["employee"], + }, + sk: { + field: "gsi2sk", + facets: ["project", "task"], + }, + }, + }, +}; + +const Tasks = new Entity(TasksModel, {client}); + +const employees = [ + "Jack", + "Tyler", + "David", + "Shane", + "Zack", + "Stephanie", + "Georgina", + "Michele", + "Ronda", + "Paula", + "Fred" +]; + +const projects = [ + "135-53", + "460-63", + "372-55", + "552-77", + "636-33", + "360-56" +]; +const types = ["story", "defect", "epic"]; + +const sentences = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum condimentum eros ut auctor cursus. Vivamus ac malesuada purus. Phasellus scelerisque tellus non nisi tempus, eget sagittis metus tempus. Mauris eu sapien non magna vulputate lobortis. Maecenas posuere enim et dolor ultrices, et tempus ligula scelerisque. Mauris vehicula turpis nec mi blandit convallis. Curabitur lacinia quis eros in blandit. Aliquam sed mauris auctor, tincidunt risus sed, fringilla turpis. Vestibulum molestie nec mauris vel sollicitudin. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Integer vitae orci sem. Vivamus finibus molestie lectus vel tempus. Curabitur rutrum, mauris sit amet blandit imperdiet, nibh purus molestie diam, sit amet maximus odio quam lacinia nisi. Proin laoreet dictum auctor. Mauris placerat commodo nisl in condimentum. Pellentesque non magna diam. Quisque in varius metus. Aenean lorem tellus, gravida nec egestas eu, rutrum id lectus. Ut id lacus leo. Donec dignissim id eros vitae auctor. Nunc tincidunt diam id fermentum placerat. Aliquam efficitur felis metus, id tincidunt lacus tincidunt a. Aliquam erat volutpat. Integer nec quam at metus suscipit viverra. Pellentesque non imperdiet est. Proin suscipit justo ex, eget condimentum leo imperdiet ac. Fusce efficitur purus a convallis euismod. Integer eget nibh erat. Mauris id venenatis urna. Nullam orci lorem, sollicitudin sit amet sapien ornare, euismod lacinia lacus. Aenean vel eros sagittis, dignissim orci et, posuere nunc. Maecenas vel est elit. Nam ac dui nec justo aliquet volutpat vel eget risus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer eu nisl purus. Vestibulum finibus lorem euismod, facilisis ex quis, commodo enim. Nullam placerat lobortis lacus, vitae blandit risus gravida in. Duis quis ultricies orci, non rhoncus ligula. Praesent rhoncus urna sed aliquam sodales. Nunc blandit ut quam id porta. Aliquam erat volutpat. Morbi ultrices ornare ante id dictum. Suspendisse potenti. Quisque interdum imperdiet erat. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vestibulum tincidunt erat quis vestibulum venenatis. Cras vel convallis urna. Proin imperdiet odio nisi, et euismod tortor porttitor at. Pellentesque pharetra mi sed quam cursus viverra. Pellentesque tellus nisi, placerat sed nibh iaculis, viverra rutrum ligula. Fusce condimentum scelerisque lorem non efficitur. Suspendisse ac malesuada lectus. Etiam id cursus orci, ut sollicitudin augue. Morbi et metus dapibus, feugiat risus et, accumsan ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin sodales et nibh at lacinia. Morbi velit tellus, ultricies ac venenatis ac, dignissim eu neque. Quisque placerat magna eget nibh porttitor, a ultricies turpis pellentesque. Phasellus fringilla egestas erat, id interdum sem imperdiet et. Duis diam lorem, feugiat at lacinia sit amet, elementum at libero. Sed vehicula eu velit ut porta. Phasellus ac fermentum urna, a viverra justo. Aliquam vel mi et libero suscipit finibus ut vel purus. Aenean id leo ut felis sollicitudin finibus id sed urna. Donec et purus purus. Morbi euismod condimentum augue, et hendrerit justo elementum et. Nulla dictum et mauris id hendrerit. Aenean non dolor lobortis, convallis metus quis, euismod felis. Curabitur et pretium nunc, a accumsan enim. Donec tempor orci vel pharetra commodo. Mauris sit amet metus porttitor, porttitor ipsum non, aliquet sapien. Proin eu pharetra dolor, sed ultricies arcu. Donec venenatis elementum nisl at varius. Ut sed scelerisque risus. Vestibulum quis leo non sapien eleifend auctor id consequat velit. Maecenas porta felis vitae velit dictum elementum. Vivamus rutrum sodales cursus. Nullam eros ante, fermentum nec nunc non, bibendum iaculis dui. Mauris enim justo, feugiat vitae massa eget, lobortis iaculis purus. Suspendisse tellus nibh, semper vitae purus sed, luctus cursus turpis. Donec id vehicula odio. Nunc non diam ac est vestibulum mattis. Phasellus mattis ipsum eu condimentum convallis. Vivamus tempor massa eu ullamcorper condimentum. Nam ultricies in mauris sit amet pellentesque. Suspendisse dui lectus, pellentesque in volutpat nec, consectetur vitae mi. Duis mi libero, laoreet at turpis vitae, rutrum commodo nulla. Maecenas viverra arcu in elit aliquet malesuada. Integer diam erat, egestas et nulla a, gravida condimentum massa. Nulla malesuada ex ut cursus viverra. In commodo lacinia libero, ac elementum lectus maximus ut. Donec eget lorem quis mi suscipit sagittis. Nunc placerat odio quis mauris iaculis, maximus rutrum est tempor. Integer sollicitudin ipsum urna, at malesuada diam accumsan quis. Praesent eu eleifend urna. Pellentesque molestie diam sit amet tristique condimentum. Aliquam fringilla et nisi vel fermentum. Vivamus elit leo, maximus in dolor ac, vestibulum commodo mi. Ut id nisi nec massa faucibus vulputate sit amet nec ligula. Integer sapien purus, bibendum in sollicitudin a, mollis a purus. Donec quis libero nisl. Mauris blandit ipsum nibh, at ultricies sapien volutpat quis. Integer nec leo efficitur, posuere nisi sed, placerat ante. Nullam malesuada nec tellus eu condimentum. Vestibulum porttitor fermentum dui, vel efficitur neque rhoncus vel. Maecenas sollicitudin gravida leo non mattis. Morbi vehicula mauris eu sagittis ullamcorper. Nam pellentesque molestie consectetur. Vivamus finibus enim justo, eu lacinia arcu iaculis et. Nullam iaculis diam sit amet velit varius, egestas rhoncus nulla venenatis. Duis at eros nibh. Sed sit amet finibus arcu. Aliquam venenatis felis sit amet odio blandit, ut faucibus tellus volutpat. Integer sit amet leo neque. Nullam ullamcorper ullamcorper turpis, vitae feugiat eros varius egestas. Morbi varius luctus augue, quis egestas eros fermentum vel. Nam semper tincidunt ornare. Sed at blandit justo, nec volutpat erat. Vivamus consequat, sapien at laoreet vulputate, urna ante pharetra libero, at faucibus lacus nunc sit amet turpis. Cras luctus diam sit amet bibendum fringilla. Nullam at erat a metus consectetur congue. Nulla accumsan nisl ac lacus vulputate iaculis. Sed interdum aliquet ligula, sit amet rutrum nisi aliquam et. Etiam faucibus at erat nec congue. Integer sed sapien ac lacus finibus pellentesque. Sed convallis erat enim, eget mattis tellus tincidunt ac. Cras nec ultricies purus, sed vestibulum est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vivamus sit amet nulla quis odio feugiat venenatis. Proin quis elit ante. Vestibulum nec magna quis magna bibendum dignissim. Morbi venenatis ligula et enim semper tincidunt. In hac habitasse platea dictumst. Nulla sodales arcu eu ipsum imperdiet suscipit id sed augue. Pellentesque accumsan diam purus, sed elementum orci consequat quis. Sed nec dolor quis turpis finibus fermentum. Donec imperdiet elementum leo varius fermentum. Maecenas vitae euismod leo, a euismod nunc. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque sit amet enim et libero eleifend eleifend. Quisque efficitur neque nulla, quis lobortis orci tempor et. Duis non viverra ligula. Sed elementum, ex sit amet molestie lobortis, sapien diam condimentum tortor, nec mollis mi odio ac lacus. Mauris non augue turpis. Cras venenatis faucibus augue sit amet vulputate. Pellentesque sit amet tempus nisl. Aenean ultrices neque nec ipsum malesuada tincidunt. Pellentesque iaculis mauris magna, ut ornare nulla congue sed. Nullam quis convallis leo. Donec bibendum a arcu pharetra dictum. Sed varius commodo lectus eu bibendum. Vivamus vel condimentum ligula, nec pharetra nunc. Suspendisse malesuada est in eros accumsan, nec tempus diam lacinia. Integer vel dui in ante tempor pharetra in id magna. Aliquam erat volutpat. Cras ligula est, fringilla quis egestas a, ornare at nisi. Integer cursus imperdiet libero ut eleifend. Donec mollis arcu eu lacus euismod egestas. Sed a arcu nec turpis vestibulum porta eu nec elit. Vivamus id tellus vitae lacus euismod vehicula in eget purus. Suspendisse potenti. Praesent vel lectus pulvinar, feugiat urna eu, sodales lacus. Ut sed leo in nisl vestibulum bibendum eget id velit. Donec efficitur lacus ut commodo gravida. Duis placerat, turpis eget placerat pulvinar, nisi est ultricies velit, in ullamcorper lorem odio vel lorem. Cras velit velit, cursus imperdiet suscipit sed, pretium quis purus. Cras neque est, sodales nec congue in, faucibus eget magna. Nulla feugiat, massa at blandit lacinia, tellus dui suscipit velit, id eleifend lacus diam at orci. Donec sit amet ultrices nulla. Maecenas quis sapien a libero laoreet ultrices. Curabitur et ullamcorper elit. Phasellus faucibus volutpat orci. Curabitur suscipit mattis aliquet. Etiam vehicula varius ante, sed blandit leo mattis quis. Donec eu sapien cursus lacus tempor ultricies. Proin fermentum diam id ligula aliquam varius. Vestibulum ac finibus sapien. Donec ultricies eros vel tellus porttitor cursus. Morbi auctor ipsum metus, sed malesuada orci convallis vel. Vestibulum quis leo eget quam malesuada faucibus ac eu erat. Praesent arcu eros, pulvinar non ante id, luctus sodales risus. In non arcu eget justo rutrum tempus vel sit amet nulla. Maecenas eu diam nisl. Phasellus vitae felis vehicula, pulvinar enim nec, pulvinar felis. Nullam ac justo dui. Nam lectus nibh, porttitor in lacus sit amet, hendrerit maximus orci. Integer scelerisque massa porttitor felis ultrices accumsan. Duis rhoncus lectus ut risus suscipit placerat. Morbi tristique egestas mi ac accumsan. Proin vulputate tempor dui quis congue. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam nisl ipsum, lacinia id interdum non, venenatis vel nisl. Maecenas tincidunt urna nisl, id ullamcorper ipsum placerat et. Etiam congue lacus vel justo aliquam, id sollicitudin turpis tempor. Phasellus vehicula arcu sed risus hendrerit interdum. Vivamus sit amet risus lobortis, egestas diam non, placerat tellus. In vitae diam augue. In blandit, metus sed pharetra mattis, mauris nulla consectetur eros, sit amet cursus metus mauris nec lacus. Vivamus suscipit ac elit a ultricies. Aenean accumsan sapien at lectus cursus eleifend. Sed iaculis non metus sit amet lacinia. Aliquam id sapien a sapien vehicula tincidunt. Aenean maximus venenatis nunc et lobortis. Aliquam tristique auctor sapien a sagittis. Curabitur congue tellus turpis, at lacinia ex tincidunt ultrices. Phasellus ornare vel enim non tincidunt. Sed quis odio viverra, scelerisque est a, efficitur quam. Suspendisse dictum ultricies risus, id venenatis dui pulvinar et. Pellentesque gravida ligula nisi, et elementum magna egestas ac. Sed ornare est in interdum pretium. Proin sit amet tincidunt neque. Phasellus ac lacus vitae libero finibus eleifend quis et turpis. Nullam accumsan semper tortor non ultricies. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque semper urna id ligula placerat, in aliquet velit dapibus. Proin in velit velit. Donec mattis felis eros, pharetra facilisis nisl tristique vel. Praesent cursus rhoncus accumsan. Nullam egestas bibendum elit, at finibus metus sodales id. Nulla facilisi. Nam euismod sem arcu, nec vulputate dui blandit et. Integer pellentesque a velit ornare volutpat. In facilisis sodales risus a imperdiet. Vestibulum bibendum, dui et mollis dictum, quam tortor consectetur augue, id accumsan tortor arcu vitae eros. Etiam et libero tortor. Ut at nisl vitae mi iaculis convallis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Mauris eleifend pellentesque fringilla. Mauris vel risus justo. Mauris sed molestie lacus. Mauris sagittis id arcu a fringilla. Cras eget neque sed lorem venenatis mollis non vel mi. Aenean feugiat sem non volutpat malesuada. Sed enim mi, rhoncus a imperdiet eget, feugiat cursus nisl. Donec ut auctor nibh. Mauris vitae tempor ligula, eget sagittis lacus. Sed vitae aliquet nisi. Sed porttitor ullamcorper orci, eget volutpat sem consectetur in. Nam pretium egestas pretium. Etiam eu mattis est. Ut pulvinar lectus sit amet lectus venenatis laoreet. Nullam lectus quam, scelerisque id laoreet ut, elementum et dolor. Etiam imperdiet eu magna id ornare. Ut blandit tristique tellus. Proin feugiat dolor sit amet fermentum dignissim. Vestibulum aliquam vestibulum fermentum. Nulla auctor, dolor porta hendrerit efficitur, ex turpis facilisis diam, a sollicitudin nulla ante eu risus. Nulla iaculis, mi a mattis sollicitudin, nibh lorem rhoncus felis, vel vestibulum lacus leo ut turpis. Nullam eget ipsum id metus elementum tristique non ac mi. Praesent ut pulvinar eros. Aenean id purus ipsum. In auctor tellus quis lacus imperdiet vehicula. Duis volutpat sodales maximus. Suspendisse placerat tellus tortor. Aenean bibendum erat quis enim faucibus finibus. Aenean urna risus, dapibus ac pellentesque vitae, gravida quis nibh. Ut dui augue, suscipit et purus vel, tristique condimentum dolor. Donec vestibulum iaculis posuere. Maecenas congue nulla sed faucibus porta. Proin in feugiat nisl.`.split(".") + +const points = [ + 1, + 2, + 3, + 5, + 8, + 13, + 21, + 50, + 100 +]; + +function getRandomNumber(min, max) { + return Math.floor(Math.random() * (max - min) ) + min; +} + +function generateRandomComments() { + let comments = []; + for (let i = 0; i < getRandomNumber(2, 6); i++) { + let text = []; + for (let j = 0; j < getRandomNumber(4, 10); j++) { + text.push(sentences[getRandomNumber(0, sentences.length)]); + } + comments.push({ + author: employees[getRandomNumber(0, employees.length)], + text: text.join(".") + "." + }) + } + return comments; +} +function generateRandomRecord() { + return { + employee: employees[getRandomNumber(0, employees.length)], + project: projects[getRandomNumber(0, projects.length)], + type: types[getRandomNumber(0, types.length)], + points: points[getRandomNumber(0, points.length + 4)], + description: sentences[getRandomNumber(0, sentences.length)], + comments: generateRandomComments() + } +} + +async function load(total) { + let inserts = []; + for (let i = 0; i < total; i++) { + inserts.push( + Tasks.put(generateRandomRecord()).go() + ) + } + return Promise.all(inserts) +} + +// load(100).then(console.log).catch(console.log); +async function queryPage(Limit = 10, next = null) { + let [page, tasks] = await Tasks.query.projects({project: "135-53"}).page(next, {includeKeys: true, params: {Limit}}); + return {page, tasks, type: "query"}; +} + +async function assignedPage(Limit = 10, next = null) { + let [page, tasks] = await Tasks.query.assigned({employee: employees[0]}).page(next, {includeKeys: true, originalErr: true, params: {Limit}}); + return {page, tasks, type: "query"}; +} + +async function scanPage(Limit = 10, next = null) { + let [page, tasks] = await Tasks.scan.page(next, {includeKeys: true, params: {Limit}}); + return {page, tasks, type: "scan"}; +} + +function compareOrder({page = {}, tasks = [], type} = {}) { + let original = JSON.parse(JSON.stringify(tasks)); + let sorted = tasks.sort((a, z) => (a.pk + a.sk) - (z.pk + z.sk)); + for (let i = 0; i < tasks.length; i++) { + let pkMatch = sorted[i].pk = original[i].pk; + let skMatch = sorted[i].sk = original[i].sk; + let pageMatch = page && sorted[i].project === page.project && sorted[i].employee === page.employee && sorted[i].task === page.task; + + if (!pkMatch) { + console.log(i, tasks.length, "No PK Match", sorted[i].pk, original[i].pk); + } + if (!skMatch) { + console.log(i, tasks.length, "No SK Match", sorted[i].sk, original[i].sk); + } + if (pageMatch) { + console.log(i, tasks.length, "Page in results!"); + } + } + return [page, tasks]; +} + +async function multiPage(fn, op, limit, pages = 1) { + var next; + let results = []; + for (let i = 0; i < pages; i++) { + if (next === null) { + break; + } + var [next, tasks] = await fn(limit, next).then(op); + results = [...results, ...tasks]; + } + return results +} + +// queryPage(10).then(compareOrder).catch(console.log); +// scanPage(10).then(compareOrder).catch(console.log); +// multiPage(assignedPage, 10, 20).then(results => console.log("TOTAL", results.length)).catch(console.log); +// multiPage(scanPage, compareOrder, 10, 1000).then(results => console.log("TOTAL", results.length)).catch(console.log); +// multiPage(queryPage, 10, 1000).then(results => console.log("TOTAL", results.length)).catch(console.log); +// multiPage(scanPage, 10, 20).catch(console.log); +// makeTable().then(console.log).catch(console.log) +// load(2000).then(console.log).catch(console.log); +// Tasks.scan.go({params: {Limit: 10}}).then(console.log).catch(console.log); + diff --git a/test/offline.entity.spec.js b/test/offline.entity.spec.js index 3c6fa43a..33401bfe 100644 --- a/test/offline.entity.spec.js +++ b/test/offline.entity.spec.js @@ -752,14 +752,16 @@ describe("Entity", () => { let scan = MallStores.scan.filter(({store}) => store.eq("Starblix")).params(); expect(scan).to.deep.equal({ "ExpressionAttributeNames": { + "#__edb_e__": "__edb_e__", "#pk": "pk", "#store": "storeId" }, "ExpressionAttributeValues": { + ":__edb_e__": "MallStores", ":pk": "$mallstoredirectory_1$mallstores#id_", ":store1": "Starblix" }, - "FilterExpression": "(begins_with(#pk, :pk) AND #store = :store1", + "FilterExpression": "(begins_with(#pk, :pk) AND #__edb_e__ = :__edb_e__ AND #store = :store1", "TableName": "StoreDirectory" }) }) @@ -1857,12 +1859,13 @@ describe("Entity", () => { let params = MallStores.find({leaseEnd}).params(); expect(params).to.be.deep.equal({ TableName: 'StoreDirectory', - ExpressionAttributeNames: { '#leaseEnd': 'leaseEnd', '#pk': 'pk' }, + ExpressionAttributeNames: { "#__edb_e__": "__edb_e__", '#leaseEnd': 'leaseEnd', '#pk': 'pk' }, ExpressionAttributeValues: { + ":__edb_e__": "MallStores", ':leaseEnd1': '123', ':pk': '$mallstoredirectory_1$mallstores#id_' }, - FilterExpression: '(begins_with(#pk, :pk) AND #leaseEnd = :leaseEnd1' + FilterExpression: "(begins_with(#pk, :pk) AND #__edb_e__ = :__edb_e__ AND #leaseEnd = :leaseEnd1" }); expect(shouldScan).to.be.true; expect(keys).to.be.deep.equal([]); @@ -1879,7 +1882,7 @@ describe("Entity", () => { ':pk': '$mallstoredirectory_1$mallstores#id_123', }, KeyConditionExpression: '#pk = :pk', - FilterExpression: '#id = :id1' + FilterExpression: "#id = :id1" }); expect(keys).to.be.deep.equal([{ name: "id", type: "pk" }]); expect(index).to.be.equal(""); From beacdc0b30388685019be9453dd0b20f552275d2 Mon Sep 17 00:00:00 2001 From: tywalch Date: Tue, 29 Sep 2020 17:04:34 -0400 Subject: [PATCH 4/8] readme updates and version bump --- README.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f7a6fda8..8a3e96c0 100644 --- a/README.md +++ b/README.md @@ -1508,9 +1508,17 @@ let stores = MallStores.query ### Page -The `page` method _ends_ a query chain, and asynchronously queries DynamoDB with the `client` provided in the model. Unlike the `.go()`, the `.page()` method returns a tupple. The first element is the "page", the `ExclusiveStartKey` as returned directly from DynamoDB. The second element is the query results. When calling `.page()` the first argument is reserved for the "page" returned from a previous query, the second parameter is for Query Options. +> As of September 29th 2020 the `.page()` now returns the facets that make up the `ExclusiveStartKey` instead of the `ExclusiveStartKey` itself. To get back only the `ExclusiveStartKey`, add the flag `exclusiveStartKeyRaw` to your query options. If you treated this value opaquely no changes are needed, or if you used the `raw` flag. -> For more information on the options available in the `config` object, check out the section [Query Options](#query-options). +The `page` method _ends_ a query chain, and asynchronously queries DynamoDB with the `client` provided in the model. Unlike the `.go()`, the `.page()` method returns a tupple. + +The first element is the "page", this object contains the facets that make up the `ExclusiveStartKey` that is returned by the DynamoDB client. This is very useful in multi-tenant applications where only some facets are exposed to the client, or there is a need to prevent leaking keys between entities. If there is no `ExclusiveStartKey` this value will be null. On subsequent calls to `.page()`, pass the results returned from the previous call to `.page()` or construct the facets yourself. + +> Note: It is highly recommended to use the `exclusiveStartKeyRaw` flag when using `.page()` in conjunction with scans. This is because when using scan on large tables the docClient may return an `ExclusiveStartKey` for a record that does not belong to entity making the query (regardless of the filters set). In these cases ElectroDB will return null (to avoid leaking the keys of other entities) when further pagination may be needed to find your records. + +The second element is the results of the query, exactly as it would be returned through a `query` operation. + +> Note: When calling `.page()` the first argument is reserved for the "page" returned from a previous query, the second parameter is for Query Options. For more information on the options available in the `config` object, check out the section [Query Options](#query-options). ```javascript let [page, stores] = await MallStores.query @@ -1523,8 +1531,10 @@ let [pageTwo, moreStores] = await MallStores.query // page: // { -// pk: '$bugbeater_1#sector_49a6a7df-a10c-4ad7-9aae-9bed4e2e6cbb', -// sk: '$test_entity#id_55f21028-b2b3-4eae-b248-a2407a2bfdc6' +// storeId: "LatteLarrys", +// mallId: "EastPointe", +// buildingId: "BuildingA1", +// unitId: "B47" // } // stores @@ -1552,8 +1562,6 @@ let [pageTwo, moreStores] = await MallStores.query // }] ``` - - ## Query Examples Below are _all_ chain possibilities available. From aa62ab36f196523dfb66203fc83fb4793c403820 Mon Sep 17 00:00:00 2001 From: tywalch Date: Tue, 29 Sep 2020 17:04:39 -0400 Subject: [PATCH 5/8] readme updates and version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6e5b1440..df3882d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electrodb", - "version": "0.9.13", + "version": "0.9.14", "description": "A library to more easily create and interact with multiple entities and heretical relationships in dynamodb", "main": "index.js", "scripts": { From c3b09739976a65185c87cb2455038b49d28dc8bd Mon Sep 17 00:00:00 2001 From: tywalch Date: Tue, 29 Sep 2020 17:09:29 -0400 Subject: [PATCH 6/8] removing debug file --- test/debug.js | 256 -------------------------------------------------- 1 file changed, 256 deletions(-) delete mode 100644 test/debug.js diff --git a/test/debug.js b/test/debug.js deleted file mode 100644 index 4603e952..00000000 --- a/test/debug.js +++ /dev/null @@ -1,256 +0,0 @@ - -process.env.AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1; -const AWS = require("aws-sdk"); -AWS.config.update({ region: "us-west-2", endpoint: "http://localhost:8000"}); -const dynamo = new AWS.DynamoDB(); -const client = new AWS.DynamoDB.DocumentClient(); -const uuid = require("uuid").v4; -const {Entity} = require("../src/entity"); - -function makeTable() { - return dynamo.createTable({ - TableName: 'electro', - KeySchema: [ - { AttributeName: 'pk', KeyType: 'HASH' }, - { AttributeName: 'sk', KeyType: 'RANGE' }, - ], - AttributeDefinitions: [ - { AttributeName: 'pk', AttributeType: 'S' }, - { AttributeName: 'sk', AttributeType: 'S' }, - { AttributeName: 'gsi1pk', AttributeType: 'S' }, - { AttributeName: 'gsi1sk', AttributeType: 'S' }, - { AttributeName: 'gsi2pk', AttributeType: 'S' }, - { AttributeName: 'gsi2sk', AttributeType: 'S' }, - ], - GlobalSecondaryIndexes: [ - { - IndexName: 'gsi1pk-gsi1sk-index', - KeySchema: [ - { AttributeName: 'gsi1pk', KeyType: 'HASH' }, - { AttributeName: 'gsi1sk', KeyType: 'RANGE' }, - ], - Projection: { - ProjectionType: 'ALL', - }, - }, - { - IndexName: 'gsi2pk-gsi2sk-index', - KeySchema: [ - { AttributeName: 'gsi2pk', KeyType: 'HASH' }, - { AttributeName: 'gsi2sk', KeyType: 'RANGE' }, - ], - Projection: { - ProjectionType: 'ALL', - }, - }, - ], - BillingMode: 'PAY_PER_REQUEST', - }).promise(); -} - -const TasksModel = { - entity: "anytype", - version: "1", - service: "taskapp", - table: "electro", - attributes: { - task: { - type: "string", - default: () => uuid(), - }, - project: { - type: "string", - required: true, - }, - employee: { - type: "string" - }, - description: { - type: "string", - }, - points: { - type: "number", - }, - type: { - type: ["story", "defect", "epic"] - }, - comments: { - type: "any" - } - }, - indexes: { - task: { - pk: { - field: "pk", - facets: ["task"], - }, - sk: { - field: "sk", - facets: ["project", "employee"], - }, - }, - projects: { - index: "gsi1pk-gsi1sk-index", - pk: { - field: "gsi1pk", - facets: ["project"], - }, - sk: { - field: "gsi1sk", - facets: ["employee", "task"], - }, - }, - assigned: { - collection: "assignments", - index: "gsi2pk-gsi2sk-index", - pk: { - field: "gsi2pk", - facets: ["employee"], - }, - sk: { - field: "gsi2sk", - facets: ["project", "task"], - }, - }, - }, -}; - -const Tasks = new Entity(TasksModel, {client}); - -const employees = [ - "Jack", - "Tyler", - "David", - "Shane", - "Zack", - "Stephanie", - "Georgina", - "Michele", - "Ronda", - "Paula", - "Fred" -]; - -const projects = [ - "135-53", - "460-63", - "372-55", - "552-77", - "636-33", - "360-56" -]; -const types = ["story", "defect", "epic"]; - -const sentences = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum condimentum eros ut auctor cursus. Vivamus ac malesuada purus. Phasellus scelerisque tellus non nisi tempus, eget sagittis metus tempus. Mauris eu sapien non magna vulputate lobortis. Maecenas posuere enim et dolor ultrices, et tempus ligula scelerisque. Mauris vehicula turpis nec mi blandit convallis. Curabitur lacinia quis eros in blandit. Aliquam sed mauris auctor, tincidunt risus sed, fringilla turpis. Vestibulum molestie nec mauris vel sollicitudin. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Integer vitae orci sem. Vivamus finibus molestie lectus vel tempus. Curabitur rutrum, mauris sit amet blandit imperdiet, nibh purus molestie diam, sit amet maximus odio quam lacinia nisi. Proin laoreet dictum auctor. Mauris placerat commodo nisl in condimentum. Pellentesque non magna diam. Quisque in varius metus. Aenean lorem tellus, gravida nec egestas eu, rutrum id lectus. Ut id lacus leo. Donec dignissim id eros vitae auctor. Nunc tincidunt diam id fermentum placerat. Aliquam efficitur felis metus, id tincidunt lacus tincidunt a. Aliquam erat volutpat. Integer nec quam at metus suscipit viverra. Pellentesque non imperdiet est. Proin suscipit justo ex, eget condimentum leo imperdiet ac. Fusce efficitur purus a convallis euismod. Integer eget nibh erat. Mauris id venenatis urna. Nullam orci lorem, sollicitudin sit amet sapien ornare, euismod lacinia lacus. Aenean vel eros sagittis, dignissim orci et, posuere nunc. Maecenas vel est elit. Nam ac dui nec justo aliquet volutpat vel eget risus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer eu nisl purus. Vestibulum finibus lorem euismod, facilisis ex quis, commodo enim. Nullam placerat lobortis lacus, vitae blandit risus gravida in. Duis quis ultricies orci, non rhoncus ligula. Praesent rhoncus urna sed aliquam sodales. Nunc blandit ut quam id porta. Aliquam erat volutpat. Morbi ultrices ornare ante id dictum. Suspendisse potenti. Quisque interdum imperdiet erat. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vestibulum tincidunt erat quis vestibulum venenatis. Cras vel convallis urna. Proin imperdiet odio nisi, et euismod tortor porttitor at. Pellentesque pharetra mi sed quam cursus viverra. Pellentesque tellus nisi, placerat sed nibh iaculis, viverra rutrum ligula. Fusce condimentum scelerisque lorem non efficitur. Suspendisse ac malesuada lectus. Etiam id cursus orci, ut sollicitudin augue. Morbi et metus dapibus, feugiat risus et, accumsan ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin sodales et nibh at lacinia. Morbi velit tellus, ultricies ac venenatis ac, dignissim eu neque. Quisque placerat magna eget nibh porttitor, a ultricies turpis pellentesque. Phasellus fringilla egestas erat, id interdum sem imperdiet et. Duis diam lorem, feugiat at lacinia sit amet, elementum at libero. Sed vehicula eu velit ut porta. Phasellus ac fermentum urna, a viverra justo. Aliquam vel mi et libero suscipit finibus ut vel purus. Aenean id leo ut felis sollicitudin finibus id sed urna. Donec et purus purus. Morbi euismod condimentum augue, et hendrerit justo elementum et. Nulla dictum et mauris id hendrerit. Aenean non dolor lobortis, convallis metus quis, euismod felis. Curabitur et pretium nunc, a accumsan enim. Donec tempor orci vel pharetra commodo. Mauris sit amet metus porttitor, porttitor ipsum non, aliquet sapien. Proin eu pharetra dolor, sed ultricies arcu. Donec venenatis elementum nisl at varius. Ut sed scelerisque risus. Vestibulum quis leo non sapien eleifend auctor id consequat velit. Maecenas porta felis vitae velit dictum elementum. Vivamus rutrum sodales cursus. Nullam eros ante, fermentum nec nunc non, bibendum iaculis dui. Mauris enim justo, feugiat vitae massa eget, lobortis iaculis purus. Suspendisse tellus nibh, semper vitae purus sed, luctus cursus turpis. Donec id vehicula odio. Nunc non diam ac est vestibulum mattis. Phasellus mattis ipsum eu condimentum convallis. Vivamus tempor massa eu ullamcorper condimentum. Nam ultricies in mauris sit amet pellentesque. Suspendisse dui lectus, pellentesque in volutpat nec, consectetur vitae mi. Duis mi libero, laoreet at turpis vitae, rutrum commodo nulla. Maecenas viverra arcu in elit aliquet malesuada. Integer diam erat, egestas et nulla a, gravida condimentum massa. Nulla malesuada ex ut cursus viverra. In commodo lacinia libero, ac elementum lectus maximus ut. Donec eget lorem quis mi suscipit sagittis. Nunc placerat odio quis mauris iaculis, maximus rutrum est tempor. Integer sollicitudin ipsum urna, at malesuada diam accumsan quis. Praesent eu eleifend urna. Pellentesque molestie diam sit amet tristique condimentum. Aliquam fringilla et nisi vel fermentum. Vivamus elit leo, maximus in dolor ac, vestibulum commodo mi. Ut id nisi nec massa faucibus vulputate sit amet nec ligula. Integer sapien purus, bibendum in sollicitudin a, mollis a purus. Donec quis libero nisl. Mauris blandit ipsum nibh, at ultricies sapien volutpat quis. Integer nec leo efficitur, posuere nisi sed, placerat ante. Nullam malesuada nec tellus eu condimentum. Vestibulum porttitor fermentum dui, vel efficitur neque rhoncus vel. Maecenas sollicitudin gravida leo non mattis. Morbi vehicula mauris eu sagittis ullamcorper. Nam pellentesque molestie consectetur. Vivamus finibus enim justo, eu lacinia arcu iaculis et. Nullam iaculis diam sit amet velit varius, egestas rhoncus nulla venenatis. Duis at eros nibh. Sed sit amet finibus arcu. Aliquam venenatis felis sit amet odio blandit, ut faucibus tellus volutpat. Integer sit amet leo neque. Nullam ullamcorper ullamcorper turpis, vitae feugiat eros varius egestas. Morbi varius luctus augue, quis egestas eros fermentum vel. Nam semper tincidunt ornare. Sed at blandit justo, nec volutpat erat. Vivamus consequat, sapien at laoreet vulputate, urna ante pharetra libero, at faucibus lacus nunc sit amet turpis. Cras luctus diam sit amet bibendum fringilla. Nullam at erat a metus consectetur congue. Nulla accumsan nisl ac lacus vulputate iaculis. Sed interdum aliquet ligula, sit amet rutrum nisi aliquam et. Etiam faucibus at erat nec congue. Integer sed sapien ac lacus finibus pellentesque. Sed convallis erat enim, eget mattis tellus tincidunt ac. Cras nec ultricies purus, sed vestibulum est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vivamus sit amet nulla quis odio feugiat venenatis. Proin quis elit ante. Vestibulum nec magna quis magna bibendum dignissim. Morbi venenatis ligula et enim semper tincidunt. In hac habitasse platea dictumst. Nulla sodales arcu eu ipsum imperdiet suscipit id sed augue. Pellentesque accumsan diam purus, sed elementum orci consequat quis. Sed nec dolor quis turpis finibus fermentum. Donec imperdiet elementum leo varius fermentum. Maecenas vitae euismod leo, a euismod nunc. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque sit amet enim et libero eleifend eleifend. Quisque efficitur neque nulla, quis lobortis orci tempor et. Duis non viverra ligula. Sed elementum, ex sit amet molestie lobortis, sapien diam condimentum tortor, nec mollis mi odio ac lacus. Mauris non augue turpis. Cras venenatis faucibus augue sit amet vulputate. Pellentesque sit amet tempus nisl. Aenean ultrices neque nec ipsum malesuada tincidunt. Pellentesque iaculis mauris magna, ut ornare nulla congue sed. Nullam quis convallis leo. Donec bibendum a arcu pharetra dictum. Sed varius commodo lectus eu bibendum. Vivamus vel condimentum ligula, nec pharetra nunc. Suspendisse malesuada est in eros accumsan, nec tempus diam lacinia. Integer vel dui in ante tempor pharetra in id magna. Aliquam erat volutpat. Cras ligula est, fringilla quis egestas a, ornare at nisi. Integer cursus imperdiet libero ut eleifend. Donec mollis arcu eu lacus euismod egestas. Sed a arcu nec turpis vestibulum porta eu nec elit. Vivamus id tellus vitae lacus euismod vehicula in eget purus. Suspendisse potenti. Praesent vel lectus pulvinar, feugiat urna eu, sodales lacus. Ut sed leo in nisl vestibulum bibendum eget id velit. Donec efficitur lacus ut commodo gravida. Duis placerat, turpis eget placerat pulvinar, nisi est ultricies velit, in ullamcorper lorem odio vel lorem. Cras velit velit, cursus imperdiet suscipit sed, pretium quis purus. Cras neque est, sodales nec congue in, faucibus eget magna. Nulla feugiat, massa at blandit lacinia, tellus dui suscipit velit, id eleifend lacus diam at orci. Donec sit amet ultrices nulla. Maecenas quis sapien a libero laoreet ultrices. Curabitur et ullamcorper elit. Phasellus faucibus volutpat orci. Curabitur suscipit mattis aliquet. Etiam vehicula varius ante, sed blandit leo mattis quis. Donec eu sapien cursus lacus tempor ultricies. Proin fermentum diam id ligula aliquam varius. Vestibulum ac finibus sapien. Donec ultricies eros vel tellus porttitor cursus. Morbi auctor ipsum metus, sed malesuada orci convallis vel. Vestibulum quis leo eget quam malesuada faucibus ac eu erat. Praesent arcu eros, pulvinar non ante id, luctus sodales risus. In non arcu eget justo rutrum tempus vel sit amet nulla. Maecenas eu diam nisl. Phasellus vitae felis vehicula, pulvinar enim nec, pulvinar felis. Nullam ac justo dui. Nam lectus nibh, porttitor in lacus sit amet, hendrerit maximus orci. Integer scelerisque massa porttitor felis ultrices accumsan. Duis rhoncus lectus ut risus suscipit placerat. Morbi tristique egestas mi ac accumsan. Proin vulputate tempor dui quis congue. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam nisl ipsum, lacinia id interdum non, venenatis vel nisl. Maecenas tincidunt urna nisl, id ullamcorper ipsum placerat et. Etiam congue lacus vel justo aliquam, id sollicitudin turpis tempor. Phasellus vehicula arcu sed risus hendrerit interdum. Vivamus sit amet risus lobortis, egestas diam non, placerat tellus. In vitae diam augue. In blandit, metus sed pharetra mattis, mauris nulla consectetur eros, sit amet cursus metus mauris nec lacus. Vivamus suscipit ac elit a ultricies. Aenean accumsan sapien at lectus cursus eleifend. Sed iaculis non metus sit amet lacinia. Aliquam id sapien a sapien vehicula tincidunt. Aenean maximus venenatis nunc et lobortis. Aliquam tristique auctor sapien a sagittis. Curabitur congue tellus turpis, at lacinia ex tincidunt ultrices. Phasellus ornare vel enim non tincidunt. Sed quis odio viverra, scelerisque est a, efficitur quam. Suspendisse dictum ultricies risus, id venenatis dui pulvinar et. Pellentesque gravida ligula nisi, et elementum magna egestas ac. Sed ornare est in interdum pretium. Proin sit amet tincidunt neque. Phasellus ac lacus vitae libero finibus eleifend quis et turpis. Nullam accumsan semper tortor non ultricies. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque semper urna id ligula placerat, in aliquet velit dapibus. Proin in velit velit. Donec mattis felis eros, pharetra facilisis nisl tristique vel. Praesent cursus rhoncus accumsan. Nullam egestas bibendum elit, at finibus metus sodales id. Nulla facilisi. Nam euismod sem arcu, nec vulputate dui blandit et. Integer pellentesque a velit ornare volutpat. In facilisis sodales risus a imperdiet. Vestibulum bibendum, dui et mollis dictum, quam tortor consectetur augue, id accumsan tortor arcu vitae eros. Etiam et libero tortor. Ut at nisl vitae mi iaculis convallis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Mauris eleifend pellentesque fringilla. Mauris vel risus justo. Mauris sed molestie lacus. Mauris sagittis id arcu a fringilla. Cras eget neque sed lorem venenatis mollis non vel mi. Aenean feugiat sem non volutpat malesuada. Sed enim mi, rhoncus a imperdiet eget, feugiat cursus nisl. Donec ut auctor nibh. Mauris vitae tempor ligula, eget sagittis lacus. Sed vitae aliquet nisi. Sed porttitor ullamcorper orci, eget volutpat sem consectetur in. Nam pretium egestas pretium. Etiam eu mattis est. Ut pulvinar lectus sit amet lectus venenatis laoreet. Nullam lectus quam, scelerisque id laoreet ut, elementum et dolor. Etiam imperdiet eu magna id ornare. Ut blandit tristique tellus. Proin feugiat dolor sit amet fermentum dignissim. Vestibulum aliquam vestibulum fermentum. Nulla auctor, dolor porta hendrerit efficitur, ex turpis facilisis diam, a sollicitudin nulla ante eu risus. Nulla iaculis, mi a mattis sollicitudin, nibh lorem rhoncus felis, vel vestibulum lacus leo ut turpis. Nullam eget ipsum id metus elementum tristique non ac mi. Praesent ut pulvinar eros. Aenean id purus ipsum. In auctor tellus quis lacus imperdiet vehicula. Duis volutpat sodales maximus. Suspendisse placerat tellus tortor. Aenean bibendum erat quis enim faucibus finibus. Aenean urna risus, dapibus ac pellentesque vitae, gravida quis nibh. Ut dui augue, suscipit et purus vel, tristique condimentum dolor. Donec vestibulum iaculis posuere. Maecenas congue nulla sed faucibus porta. Proin in feugiat nisl.`.split(".") - -const points = [ - 1, - 2, - 3, - 5, - 8, - 13, - 21, - 50, - 100 -]; - -function getRandomNumber(min, max) { - return Math.floor(Math.random() * (max - min) ) + min; -} - -function generateRandomComments() { - let comments = []; - for (let i = 0; i < getRandomNumber(2, 6); i++) { - let text = []; - for (let j = 0; j < getRandomNumber(4, 10); j++) { - text.push(sentences[getRandomNumber(0, sentences.length)]); - } - comments.push({ - author: employees[getRandomNumber(0, employees.length)], - text: text.join(".") + "." - }) - } - return comments; -} -function generateRandomRecord() { - return { - employee: employees[getRandomNumber(0, employees.length)], - project: projects[getRandomNumber(0, projects.length)], - type: types[getRandomNumber(0, types.length)], - points: points[getRandomNumber(0, points.length + 4)], - description: sentences[getRandomNumber(0, sentences.length)], - comments: generateRandomComments() - } -} - -async function load(total) { - let inserts = []; - for (let i = 0; i < total; i++) { - inserts.push( - Tasks.put(generateRandomRecord()).go() - ) - } - return Promise.all(inserts) -} - -// load(100).then(console.log).catch(console.log); -async function queryPage(Limit = 10, next = null) { - let [page, tasks] = await Tasks.query.projects({project: "135-53"}).page(next, {includeKeys: true, params: {Limit}}); - return {page, tasks, type: "query"}; -} - -async function assignedPage(Limit = 10, next = null) { - let [page, tasks] = await Tasks.query.assigned({employee: employees[0]}).page(next, {includeKeys: true, originalErr: true, params: {Limit}}); - return {page, tasks, type: "query"}; -} - -async function scanPage(Limit = 10, next = null) { - let [page, tasks] = await Tasks.scan.page(next, {includeKeys: true, params: {Limit}}); - return {page, tasks, type: "scan"}; -} - -function compareOrder({page = {}, tasks = [], type} = {}) { - let original = JSON.parse(JSON.stringify(tasks)); - let sorted = tasks.sort((a, z) => (a.pk + a.sk) - (z.pk + z.sk)); - for (let i = 0; i < tasks.length; i++) { - let pkMatch = sorted[i].pk = original[i].pk; - let skMatch = sorted[i].sk = original[i].sk; - let pageMatch = page && sorted[i].project === page.project && sorted[i].employee === page.employee && sorted[i].task === page.task; - - if (!pkMatch) { - console.log(i, tasks.length, "No PK Match", sorted[i].pk, original[i].pk); - } - if (!skMatch) { - console.log(i, tasks.length, "No SK Match", sorted[i].sk, original[i].sk); - } - if (pageMatch) { - console.log(i, tasks.length, "Page in results!"); - } - } - return [page, tasks]; -} - -async function multiPage(fn, op, limit, pages = 1) { - var next; - let results = []; - for (let i = 0; i < pages; i++) { - if (next === null) { - break; - } - var [next, tasks] = await fn(limit, next).then(op); - results = [...results, ...tasks]; - } - return results -} - -// queryPage(10).then(compareOrder).catch(console.log); -// scanPage(10).then(compareOrder).catch(console.log); -// multiPage(assignedPage, 10, 20).then(results => console.log("TOTAL", results.length)).catch(console.log); -// multiPage(scanPage, compareOrder, 10, 1000).then(results => console.log("TOTAL", results.length)).catch(console.log); -// multiPage(queryPage, 10, 1000).then(results => console.log("TOTAL", results.length)).catch(console.log); -// multiPage(scanPage, 10, 20).catch(console.log); -// makeTable().then(console.log).catch(console.log) -// load(2000).then(console.log).catch(console.log); -// Tasks.scan.go({params: {Limit: 10}}).then(console.log).catch(console.log); - From 747e2c53a7f4c8c5bcd694a05b7b0ab7d21e9949 Mon Sep 17 00:00:00 2001 From: tywalch Date: Tue, 29 Sep 2020 17:19:40 -0400 Subject: [PATCH 7/8] travis-ci cant handle static properties --- test/connected.crud.spec.js | 8 ++--- test/connected.filters.spec.js | 2 ++ test/connected.page.spec.js | 63 ++++++++++++++++++---------------- test/connected.service.spec.js | 2 ++ test/connected.where.spec.js | 2 ++ 5 files changed, 43 insertions(+), 34 deletions(-) diff --git a/test/connected.crud.spec.js b/test/connected.crud.spec.js index 62cc683e..f4d54a63 100644 --- a/test/connected.crud.spec.js +++ b/test/connected.crud.spec.js @@ -1,3 +1,5 @@ +const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + process.env.AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1; const { Entity } = require("../src/entity"); const { expect } = require("chai"); @@ -9,11 +11,6 @@ const client = new DynamoDB.DocumentClient({ }); const SERVICE = "BugBeater"; const ENTITY = "TEST_ENTITY" -function sleep(ms) { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); -} let model = { service: SERVICE, entity: ENTITY, @@ -139,6 +136,7 @@ let model = { }; describe("Entity", async () => { + before(async () => sleep(1000)) let MallStores = new Entity(model, { client }); describe("Simple crud", async () => { let mall = "EastPointe"; diff --git a/test/connected.filters.spec.js b/test/connected.filters.spec.js index b4bfc375..97a8759a 100644 --- a/test/connected.filters.spec.js +++ b/test/connected.filters.spec.js @@ -1,3 +1,4 @@ +const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms)); process.env.AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1; const { Entity, clauses } = require("../src/entity"); const { expect } = require("chai"); @@ -10,6 +11,7 @@ const client = new DynamoDB.DocumentClient({ describe("General", async () => { + before(async () => sleep(1000)) let FilterTests = new Entity({ service: "tests", entity: "filters", diff --git a/test/connected.page.spec.js b/test/connected.page.spec.js index 45c8a4ab..afcc7421 100644 --- a/test/connected.page.spec.js +++ b/test/connected.page.spec.js @@ -1,3 +1,4 @@ +const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms)); process.env.AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1; const AWS = require("aws-sdk"); const { expect } = require("chai"); @@ -79,34 +80,7 @@ class Tasks extends Entity { this.name = model.entity this.loaded = []; } - static employees = [ - "Jack", - "Tyler", - "David", - "Shane", - "Zack", - "Stephanie", - "Georgina", - "Michele", - "Ronda", - "Paula", - "Fred" - ] - - static projects =[ - "135-53", - "460-63", - "372-55", - "552-77", - "636-33", - "360-56" - ] - - static types = ["story", "defect", "epic"] - - static points = [1, 2, 3, 5, 8, 13, 21, 50, 100]; - - static sentences = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum condimentum eros ut auctor cursus. Vivamus ac malesuada purus. Phasellus scelerisque tellus non nisi tempus, eget sagittis metus tempus. Mauris eu sapien non magna vulputate lobortis. Maecenas posuere enim et dolor ultrices, et tempus ligula scelerisque. Mauris vehicula turpis nec mi blandit convallis. Curabitur lacinia quis eros in blandit. Aliquam sed mauris auctor, tincidunt risus sed, fringilla turpis. Vestibulum molestie nec mauris vel sollicitudin. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Integer vitae orci sem. Vivamus finibus molestie lectus vel tempus. Curabitur rutrum, mauris sit amet blandit imperdiet, nibh purus molestie diam, sit amet maximus odio quam lacinia nisi. Proin laoreet dictum auctor. Mauris placerat commodo nisl in condimentum. Pellentesque non magna diam. Quisque in varius metus. Aenean lorem tellus, gravida nec egestas eu, rutrum id lectus. Ut id lacus leo. Donec dignissim id eros vitae auctor. Nunc tincidunt diam id fermentum placerat. Aliquam efficitur felis metus, id tincidunt lacus tincidunt a. Aliquam erat volutpat. Integer nec quam at metus suscipit viverra. Pellentesque non imperdiet est. Proin suscipit justo ex, eget condimentum leo imperdiet ac. Fusce efficitur purus a convallis euismod. Integer eget nibh erat. Mauris id venenatis urna. Nullam orci lorem, sollicitudin sit amet sapien ornare, euismod lacinia lacus. Aenean vel eros sagittis, dignissim orci et, posuere nunc. Maecenas vel est elit. Nam ac dui nec justo aliquet volutpat vel eget risus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer eu nisl purus. Vestibulum finibus lorem euismod, facilisis ex quis, commodo enim. Nullam placerat lobortis lacus, vitae blandit risus gravida in. Duis quis ultricies orci, non rhoncus ligula. Praesent rhoncus urna sed aliquam sodales. Nunc blandit ut quam id porta. Aliquam erat volutpat. Morbi ultrices ornare ante id dictum. Suspendisse potenti. Quisque interdum imperdiet erat. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vestibulum tincidunt erat quis vestibulum venenatis. Cras vel convallis urna. Proin imperdiet odio nisi, et euismod tortor porttitor at. Pellentesque pharetra mi sed quam cursus viverra. Pellentesque tellus nisi, placerat sed nibh iaculis, viverra rutrum ligula. Fusce condimentum scelerisque lorem non efficitur. Suspendisse ac malesuada lectus. Etiam id cursus orci, ut sollicitudin augue. Morbi et metus dapibus, feugiat risus et, accumsan ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin sodales et nibh at lacinia. Morbi velit tellus, ultricies ac venenatis ac, dignissim eu neque. Quisque placerat magna eget nibh porttitor, a ultricies turpis pellentesque. Phasellus fringilla egestas erat, id interdum sem imperdiet et. Duis diam lorem, feugiat at lacinia sit amet, elementum at libero. Sed vehicula eu velit ut porta. Phasellus ac fermentum urna, a viverra justo. Aliquam vel mi et libero suscipit finibus ut vel purus. Aenean id leo ut felis sollicitudin finibus id sed urna. Donec et purus purus. Morbi euismod condimentum augue, et hendrerit justo elementum et. Nulla dictum et mauris id hendrerit. Aenean non dolor lobortis, convallis metus quis, euismod felis. Curabitur et pretium nunc, a accumsan enim. Donec tempor orci vel pharetra commodo. Mauris sit amet metus porttitor, porttitor ipsum non, aliquet sapien. Proin eu pharetra dolor, sed ultricies arcu. Donec venenatis elementum nisl at varius. Ut sed scelerisque risus. Vestibulum quis leo non sapien eleifend auctor id consequat velit. Maecenas porta felis vitae velit dictum elementum. Vivamus rutrum sodales cursus. Nullam eros ante, fermentum nec nunc non, bibendum iaculis dui. Mauris enim justo, feugiat vitae massa eget, lobortis iaculis purus. Suspendisse tellus nibh, semper vitae purus sed, luctus cursus turpis. Donec id vehicula odio. Nunc non diam ac est vestibulum mattis. Phasellus mattis ipsum eu condimentum convallis. Vivamus tempor massa eu ullamcorper condimentum. Nam ultricies in mauris sit amet pellentesque. Suspendisse dui lectus, pellentesque in volutpat nec, consectetur vitae mi. Duis mi libero, laoreet at turpis vitae, rutrum commodo nulla. Maecenas viverra arcu in elit aliquet malesuada. Integer diam erat, egestas et nulla a, gravida condimentum massa. Nulla malesuada ex ut cursus viverra. In commodo lacinia libero, ac elementum lectus maximus ut. Donec eget lorem quis mi suscipit sagittis. Nunc placerat odio quis mauris iaculis, maximus rutrum est tempor. Integer sollicitudin ipsum urna, at malesuada diam accumsan quis. Praesent eu eleifend urna. Pellentesque molestie diam sit amet tristique condimentum. Aliquam fringilla et nisi vel fermentum. Vivamus elit leo, maximus in dolor ac, vestibulum commodo mi. Ut id nisi nec massa faucibus vulputate sit amet nec ligula. Integer sapien purus, bibendum in sollicitudin a, mollis a purus. Donec quis libero nisl. Mauris blandit ipsum nibh, at ultricies sapien volutpat quis. Integer nec leo efficitur, posuere nisi sed, placerat ante. Nullam malesuada nec tellus eu condimentum. Vestibulum porttitor fermentum dui, vel efficitur neque rhoncus vel. Maecenas sollicitudin gravida leo non mattis. Morbi vehicula mauris eu sagittis ullamcorper. Nam pellentesque molestie consectetur. Vivamus finibus enim justo, eu lacinia arcu iaculis et. Nullam iaculis diam sit amet velit varius, egestas rhoncus nulla venenatis. Duis at eros nibh. Sed sit amet finibus arcu. Aliquam venenatis felis sit amet odio blandit, ut faucibus tellus volutpat. Integer sit amet leo neque. Nullam ullamcorper ullamcorper turpis, vitae feugiat eros varius egestas. Morbi varius luctus augue, quis egestas eros fermentum vel. Nam semper tincidunt ornare. Sed at blandit justo, nec volutpat erat. Vivamus consequat, sapien at laoreet vulputate, urna ante pharetra libero, at faucibus lacus nunc sit amet turpis. Cras luctus diam sit amet bibendum fringilla. Nullam at erat a metus consectetur congue. Nulla accumsan nisl ac lacus vulputate iaculis. Sed interdum aliquet ligula, sit amet rutrum nisi aliquam et. Etiam faucibus at erat nec congue. Integer sed sapien ac lacus finibus pellentesque. Sed convallis erat enim, eget mattis tellus tincidunt ac. Cras nec ultricies purus, sed vestibulum est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vivamus sit amet nulla quis odio feugiat venenatis. Proin quis elit ante. Vestibulum nec magna quis magna bibendum dignissim. Morbi venenatis ligula et enim semper tincidunt. In hac habitasse platea dictumst. Nulla sodales arcu eu ipsum imperdiet suscipit id sed augue. Pellentesque accumsan diam purus, sed elementum orci consequat quis. Sed nec dolor quis turpis finibus fermentum. Donec imperdiet elementum leo varius fermentum. Maecenas vitae euismod leo, a euismod nunc. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque sit amet enim et libero eleifend eleifend. Quisque efficitur neque nulla, quis lobortis orci tempor et. Duis non viverra ligula. Sed elementum, ex sit amet molestie lobortis, sapien diam condimentum tortor, nec mollis mi odio ac lacus. Mauris non augue turpis. Cras venenatis faucibus augue sit amet vulputate. Pellentesque sit amet tempus nisl. Aenean ultrices neque nec ipsum malesuada tincidunt. Pellentesque iaculis mauris magna, ut ornare nulla congue sed. Nullam quis convallis leo. Donec bibendum a arcu pharetra dictum. Sed varius commodo lectus eu bibendum. Vivamus vel condimentum ligula, nec pharetra nunc. Suspendisse malesuada est in eros accumsan, nec tempus diam lacinia. Integer vel dui in ante tempor pharetra in id magna. Aliquam erat volutpat. Cras ligula est, fringilla quis egestas a, ornare at nisi. Integer cursus imperdiet libero ut eleifend. Donec mollis arcu eu lacus euismod egestas. Sed a arcu nec turpis vestibulum porta eu nec elit. Vivamus id tellus vitae lacus euismod vehicula in eget purus. Suspendisse potenti. Praesent vel lectus pulvinar, feugiat urna eu, sodales lacus. Ut sed leo in nisl vestibulum bibendum eget id velit. Donec efficitur lacus ut commodo gravida. Duis placerat, turpis eget placerat pulvinar, nisi est ultricies velit, in ullamcorper lorem odio vel lorem. Cras velit velit, cursus imperdiet suscipit sed, pretium quis purus. Cras neque est, sodales nec congue in, faucibus eget magna. Nulla feugiat, massa at blandit lacinia, tellus dui suscipit velit, id eleifend lacus diam at orci. Donec sit amet ultrices nulla. Maecenas quis sapien a libero laoreet ultrices. Curabitur et ullamcorper elit. Phasellus faucibus volutpat orci. Curabitur suscipit mattis aliquet. Etiam vehicula varius ante, sed blandit leo mattis quis. Donec eu sapien cursus lacus tempor ultricies. Proin fermentum diam id ligula aliquam varius. Vestibulum ac finibus sapien. Donec ultricies eros vel tellus porttitor cursus. Morbi auctor ipsum metus, sed malesuada orci convallis vel. Vestibulum quis leo eget quam malesuada faucibus ac eu erat. Praesent arcu eros, pulvinar non ante id, luctus sodales risus. In non arcu eget justo rutrum tempus vel sit amet nulla. Maecenas eu diam nisl. Phasellus vitae felis vehicula, pulvinar enim nec, pulvinar felis. Nullam ac justo dui. Nam lectus nibh, porttitor in lacus sit amet, hendrerit maximus orci. Integer scelerisque massa porttitor felis ultrices accumsan. Duis rhoncus lectus ut risus suscipit placerat. Morbi tristique egestas mi ac accumsan. Proin vulputate tempor dui quis congue. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam nisl ipsum, lacinia id interdum non, venenatis vel nisl. Maecenas tincidunt urna nisl, id ullamcorper ipsum placerat et. Etiam congue lacus vel justo aliquam, id sollicitudin turpis tempor. Phasellus vehicula arcu sed risus hendrerit interdum. Vivamus sit amet risus lobortis, egestas diam non, placerat tellus. In vitae diam augue. In blandit, metus sed pharetra mattis, mauris nulla consectetur eros, sit amet cursus metus mauris nec lacus. Vivamus suscipit ac elit a ultricies. Aenean accumsan sapien at lectus cursus eleifend. Sed iaculis non metus sit amet lacinia. Aliquam id sapien a sapien vehicula tincidunt. Aenean maximus venenatis nunc et lobortis. Aliquam tristique auctor sapien a sagittis. Curabitur congue tellus turpis, at lacinia ex tincidunt ultrices. Phasellus ornare vel enim non tincidunt. Sed quis odio viverra, scelerisque est a, efficitur quam. Suspendisse dictum ultricies risus, id venenatis dui pulvinar et. Pellentesque gravida ligula nisi, et elementum magna egestas ac. Sed ornare est in interdum pretium. Proin sit amet tincidunt neque. Phasellus ac lacus vitae libero finibus eleifend quis et turpis. Nullam accumsan semper tortor non ultricies. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque semper urna id ligula placerat, in aliquet velit dapibus. Proin in velit velit. Donec mattis felis eros, pharetra facilisis nisl tristique vel. Praesent cursus rhoncus accumsan. Nullam egestas bibendum elit, at finibus metus sodales id. Nulla facilisi. Nam euismod sem arcu, nec vulputate dui blandit et. Integer pellentesque a velit ornare volutpat. In facilisis sodales risus a imperdiet. Vestibulum bibendum, dui et mollis dictum, quam tortor consectetur augue, id accumsan tortor arcu vitae eros. Etiam et libero tortor. Ut at nisl vitae mi iaculis convallis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Mauris eleifend pellentesque fringilla. Mauris vel risus justo. Mauris sed molestie lacus. Mauris sagittis id arcu a fringilla. Cras eget neque sed lorem venenatis mollis non vel mi. Aenean feugiat sem non volutpat malesuada. Sed enim mi, rhoncus a imperdiet eget, feugiat cursus nisl. Donec ut auctor nibh. Mauris vitae tempor ligula, eget sagittis lacus. Sed vitae aliquet nisi. Sed porttitor ullamcorper orci, eget volutpat sem consectetur in. Nam pretium egestas pretium. Etiam eu mattis est. Ut pulvinar lectus sit amet lectus venenatis laoreet. Nullam lectus quam, scelerisque id laoreet ut, elementum et dolor. Etiam imperdiet eu magna id ornare. Ut blandit tristique tellus. Proin feugiat dolor sit amet fermentum dignissim. Vestibulum aliquam vestibulum fermentum. Nulla auctor, dolor porta hendrerit efficitur, ex turpis facilisis diam, a sollicitudin nulla ante eu risus. Nulla iaculis, mi a mattis sollicitudin, nibh lorem rhoncus felis, vel vestibulum lacus leo ut turpis. Nullam eget ipsum id metus elementum tristique non ac mi. Praesent ut pulvinar eros. Aenean id purus ipsum. In auctor tellus quis lacus imperdiet vehicula. Duis volutpat sodales maximus. Suspendisse placerat tellus tortor. Aenean bibendum erat quis enim faucibus finibus. Aenean urna risus, dapibus ac pellentesque vitae, gravida quis nibh. Ut dui augue, suscipit et purus vel, tristique condimentum dolor. Donec vestibulum iaculis posuere. Maecenas congue nulla sed faucibus porta. Proin in feugiat nisl.`.split(".") + getRandomNumber(min, max) { return Math.floor(Math.random() * (max - min) ) + min; @@ -195,12 +169,43 @@ class Tasks extends Entity { }; } +Tasks.employees = [ + "Jack", + "Tyler", + "David", + "Shane", + "Zack", + "Stephanie", + "Georgina", + "Michele", + "Ronda", + "Paula", + "Fred" +] + +Tasks.projects =[ + "135-53", + "460-63", + "372-55", + "552-77", + "636-33", + "360-56" +] + +Tasks.types = ["story", "defect", "epic"] + +Tasks.points = [1, 2, 3, 5, 8, 13, 21, 50, 100]; + +Tasks.sentences = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum condimentum eros ut auctor cursus. Vivamus ac malesuada purus. Phasellus scelerisque tellus non nisi tempus, eget sagittis metus tempus. Mauris eu sapien non magna vulputate lobortis. Maecenas posuere enim et dolor ultrices, et tempus ligula scelerisque. Mauris vehicula turpis nec mi blandit convallis. Curabitur lacinia quis eros in blandit. Aliquam sed mauris auctor, tincidunt risus sed, fringilla turpis. Vestibulum molestie nec mauris vel sollicitudin. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Integer vitae orci sem. Vivamus finibus molestie lectus vel tempus. Curabitur rutrum, mauris sit amet blandit imperdiet, nibh purus molestie diam, sit amet maximus odio quam lacinia nisi. Proin laoreet dictum auctor. Mauris placerat commodo nisl in condimentum. Pellentesque non magna diam. Quisque in varius metus. Aenean lorem tellus, gravida nec egestas eu, rutrum id lectus. Ut id lacus leo. Donec dignissim id eros vitae auctor. Nunc tincidunt diam id fermentum placerat. Aliquam efficitur felis metus, id tincidunt lacus tincidunt a. Aliquam erat volutpat. Integer nec quam at metus suscipit viverra. Pellentesque non imperdiet est. Proin suscipit justo ex, eget condimentum leo imperdiet ac. Fusce efficitur purus a convallis euismod. Integer eget nibh erat. Mauris id venenatis urna. Nullam orci lorem, sollicitudin sit amet sapien ornare, euismod lacinia lacus. Aenean vel eros sagittis, dignissim orci et, posuere nunc. Maecenas vel est elit. Nam ac dui nec justo aliquet volutpat vel eget risus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer eu nisl purus. Vestibulum finibus lorem euismod, facilisis ex quis, commodo enim. Nullam placerat lobortis lacus, vitae blandit risus gravida in. Duis quis ultricies orci, non rhoncus ligula. Praesent rhoncus urna sed aliquam sodales. Nunc blandit ut quam id porta. Aliquam erat volutpat. Morbi ultrices ornare ante id dictum. Suspendisse potenti. Quisque interdum imperdiet erat. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vestibulum tincidunt erat quis vestibulum venenatis. Cras vel convallis urna. Proin imperdiet odio nisi, et euismod tortor porttitor at. Pellentesque pharetra mi sed quam cursus viverra. Pellentesque tellus nisi, placerat sed nibh iaculis, viverra rutrum ligula. Fusce condimentum scelerisque lorem non efficitur. Suspendisse ac malesuada lectus. Etiam id cursus orci, ut sollicitudin augue. Morbi et metus dapibus, feugiat risus et, accumsan ligula. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin sodales et nibh at lacinia. Morbi velit tellus, ultricies ac venenatis ac, dignissim eu neque. Quisque placerat magna eget nibh porttitor, a ultricies turpis pellentesque. Phasellus fringilla egestas erat, id interdum sem imperdiet et. Duis diam lorem, feugiat at lacinia sit amet, elementum at libero. Sed vehicula eu velit ut porta. Phasellus ac fermentum urna, a viverra justo. Aliquam vel mi et libero suscipit finibus ut vel purus. Aenean id leo ut felis sollicitudin finibus id sed urna. Donec et purus purus. Morbi euismod condimentum augue, et hendrerit justo elementum et. Nulla dictum et mauris id hendrerit. Aenean non dolor lobortis, convallis metus quis, euismod felis. Curabitur et pretium nunc, a accumsan enim. Donec tempor orci vel pharetra commodo. Mauris sit amet metus porttitor, porttitor ipsum non, aliquet sapien. Proin eu pharetra dolor, sed ultricies arcu. Donec venenatis elementum nisl at varius. Ut sed scelerisque risus. Vestibulum quis leo non sapien eleifend auctor id consequat velit. Maecenas porta felis vitae velit dictum elementum. Vivamus rutrum sodales cursus. Nullam eros ante, fermentum nec nunc non, bibendum iaculis dui. Mauris enim justo, feugiat vitae massa eget, lobortis iaculis purus. Suspendisse tellus nibh, semper vitae purus sed, luctus cursus turpis. Donec id vehicula odio. Nunc non diam ac est vestibulum mattis. Phasellus mattis ipsum eu condimentum convallis. Vivamus tempor massa eu ullamcorper condimentum. Nam ultricies in mauris sit amet pellentesque. Suspendisse dui lectus, pellentesque in volutpat nec, consectetur vitae mi. Duis mi libero, laoreet at turpis vitae, rutrum commodo nulla. Maecenas viverra arcu in elit aliquet malesuada. Integer diam erat, egestas et nulla a, gravida condimentum massa. Nulla malesuada ex ut cursus viverra. In commodo lacinia libero, ac elementum lectus maximus ut. Donec eget lorem quis mi suscipit sagittis. Nunc placerat odio quis mauris iaculis, maximus rutrum est tempor. Integer sollicitudin ipsum urna, at malesuada diam accumsan quis. Praesent eu eleifend urna. Pellentesque molestie diam sit amet tristique condimentum. Aliquam fringilla et nisi vel fermentum. Vivamus elit leo, maximus in dolor ac, vestibulum commodo mi. Ut id nisi nec massa faucibus vulputate sit amet nec ligula. Integer sapien purus, bibendum in sollicitudin a, mollis a purus. Donec quis libero nisl. Mauris blandit ipsum nibh, at ultricies sapien volutpat quis. Integer nec leo efficitur, posuere nisi sed, placerat ante. Nullam malesuada nec tellus eu condimentum. Vestibulum porttitor fermentum dui, vel efficitur neque rhoncus vel. Maecenas sollicitudin gravida leo non mattis. Morbi vehicula mauris eu sagittis ullamcorper. Nam pellentesque molestie consectetur. Vivamus finibus enim justo, eu lacinia arcu iaculis et. Nullam iaculis diam sit amet velit varius, egestas rhoncus nulla venenatis. Duis at eros nibh. Sed sit amet finibus arcu. Aliquam venenatis felis sit amet odio blandit, ut faucibus tellus volutpat. Integer sit amet leo neque. Nullam ullamcorper ullamcorper turpis, vitae feugiat eros varius egestas. Morbi varius luctus augue, quis egestas eros fermentum vel. Nam semper tincidunt ornare. Sed at blandit justo, nec volutpat erat. Vivamus consequat, sapien at laoreet vulputate, urna ante pharetra libero, at faucibus lacus nunc sit amet turpis. Cras luctus diam sit amet bibendum fringilla. Nullam at erat a metus consectetur congue. Nulla accumsan nisl ac lacus vulputate iaculis. Sed interdum aliquet ligula, sit amet rutrum nisi aliquam et. Etiam faucibus at erat nec congue. Integer sed sapien ac lacus finibus pellentesque. Sed convallis erat enim, eget mattis tellus tincidunt ac. Cras nec ultricies purus, sed vestibulum est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vivamus sit amet nulla quis odio feugiat venenatis. Proin quis elit ante. Vestibulum nec magna quis magna bibendum dignissim. Morbi venenatis ligula et enim semper tincidunt. In hac habitasse platea dictumst. Nulla sodales arcu eu ipsum imperdiet suscipit id sed augue. Pellentesque accumsan diam purus, sed elementum orci consequat quis. Sed nec dolor quis turpis finibus fermentum. Donec imperdiet elementum leo varius fermentum. Maecenas vitae euismod leo, a euismod nunc. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque sit amet enim et libero eleifend eleifend. Quisque efficitur neque nulla, quis lobortis orci tempor et. Duis non viverra ligula. Sed elementum, ex sit amet molestie lobortis, sapien diam condimentum tortor, nec mollis mi odio ac lacus. Mauris non augue turpis. Cras venenatis faucibus augue sit amet vulputate. Pellentesque sit amet tempus nisl. Aenean ultrices neque nec ipsum malesuada tincidunt. Pellentesque iaculis mauris magna, ut ornare nulla congue sed. Nullam quis convallis leo. Donec bibendum a arcu pharetra dictum. Sed varius commodo lectus eu bibendum. Vivamus vel condimentum ligula, nec pharetra nunc. Suspendisse malesuada est in eros accumsan, nec tempus diam lacinia. Integer vel dui in ante tempor pharetra in id magna. Aliquam erat volutpat. Cras ligula est, fringilla quis egestas a, ornare at nisi. Integer cursus imperdiet libero ut eleifend. Donec mollis arcu eu lacus euismod egestas. Sed a arcu nec turpis vestibulum porta eu nec elit. Vivamus id tellus vitae lacus euismod vehicula in eget purus. Suspendisse potenti. Praesent vel lectus pulvinar, feugiat urna eu, sodales lacus. Ut sed leo in nisl vestibulum bibendum eget id velit. Donec efficitur lacus ut commodo gravida. Duis placerat, turpis eget placerat pulvinar, nisi est ultricies velit, in ullamcorper lorem odio vel lorem. Cras velit velit, cursus imperdiet suscipit sed, pretium quis purus. Cras neque est, sodales nec congue in, faucibus eget magna. Nulla feugiat, massa at blandit lacinia, tellus dui suscipit velit, id eleifend lacus diam at orci. Donec sit amet ultrices nulla. Maecenas quis sapien a libero laoreet ultrices. Curabitur et ullamcorper elit. Phasellus faucibus volutpat orci. Curabitur suscipit mattis aliquet. Etiam vehicula varius ante, sed blandit leo mattis quis. Donec eu sapien cursus lacus tempor ultricies. Proin fermentum diam id ligula aliquam varius. Vestibulum ac finibus sapien. Donec ultricies eros vel tellus porttitor cursus. Morbi auctor ipsum metus, sed malesuada orci convallis vel. Vestibulum quis leo eget quam malesuada faucibus ac eu erat. Praesent arcu eros, pulvinar non ante id, luctus sodales risus. In non arcu eget justo rutrum tempus vel sit amet nulla. Maecenas eu diam nisl. Phasellus vitae felis vehicula, pulvinar enim nec, pulvinar felis. Nullam ac justo dui. Nam lectus nibh, porttitor in lacus sit amet, hendrerit maximus orci. Integer scelerisque massa porttitor felis ultrices accumsan. Duis rhoncus lectus ut risus suscipit placerat. Morbi tristique egestas mi ac accumsan. Proin vulputate tempor dui quis congue. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam nisl ipsum, lacinia id interdum non, venenatis vel nisl. Maecenas tincidunt urna nisl, id ullamcorper ipsum placerat et. Etiam congue lacus vel justo aliquam, id sollicitudin turpis tempor. Phasellus vehicula arcu sed risus hendrerit interdum. Vivamus sit amet risus lobortis, egestas diam non, placerat tellus. In vitae diam augue. In blandit, metus sed pharetra mattis, mauris nulla consectetur eros, sit amet cursus metus mauris nec lacus. Vivamus suscipit ac elit a ultricies. Aenean accumsan sapien at lectus cursus eleifend. Sed iaculis non metus sit amet lacinia. Aliquam id sapien a sapien vehicula tincidunt. Aenean maximus venenatis nunc et lobortis. Aliquam tristique auctor sapien a sagittis. Curabitur congue tellus turpis, at lacinia ex tincidunt ultrices. Phasellus ornare vel enim non tincidunt. Sed quis odio viverra, scelerisque est a, efficitur quam. Suspendisse dictum ultricies risus, id venenatis dui pulvinar et. Pellentesque gravida ligula nisi, et elementum magna egestas ac. Sed ornare est in interdum pretium. Proin sit amet tincidunt neque. Phasellus ac lacus vitae libero finibus eleifend quis et turpis. Nullam accumsan semper tortor non ultricies. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque semper urna id ligula placerat, in aliquet velit dapibus. Proin in velit velit. Donec mattis felis eros, pharetra facilisis nisl tristique vel. Praesent cursus rhoncus accumsan. Nullam egestas bibendum elit, at finibus metus sodales id. Nulla facilisi. Nam euismod sem arcu, nec vulputate dui blandit et. Integer pellentesque a velit ornare volutpat. In facilisis sodales risus a imperdiet. Vestibulum bibendum, dui et mollis dictum, quam tortor consectetur augue, id accumsan tortor arcu vitae eros. Etiam et libero tortor. Ut at nisl vitae mi iaculis convallis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Mauris eleifend pellentesque fringilla. Mauris vel risus justo. Mauris sed molestie lacus. Mauris sagittis id arcu a fringilla. Cras eget neque sed lorem venenatis mollis non vel mi. Aenean feugiat sem non volutpat malesuada. Sed enim mi, rhoncus a imperdiet eget, feugiat cursus nisl. Donec ut auctor nibh. Mauris vitae tempor ligula, eget sagittis lacus. Sed vitae aliquet nisi. Sed porttitor ullamcorper orci, eget volutpat sem consectetur in. Nam pretium egestas pretium. Etiam eu mattis est. Ut pulvinar lectus sit amet lectus venenatis laoreet. Nullam lectus quam, scelerisque id laoreet ut, elementum et dolor. Etiam imperdiet eu magna id ornare. Ut blandit tristique tellus. Proin feugiat dolor sit amet fermentum dignissim. Vestibulum aliquam vestibulum fermentum. Nulla auctor, dolor porta hendrerit efficitur, ex turpis facilisis diam, a sollicitudin nulla ante eu risus. Nulla iaculis, mi a mattis sollicitudin, nibh lorem rhoncus felis, vel vestibulum lacus leo ut turpis. Nullam eget ipsum id metus elementum tristique non ac mi. Praesent ut pulvinar eros. Aenean id purus ipsum. In auctor tellus quis lacus imperdiet vehicula. Duis volutpat sodales maximus. Suspendisse placerat tellus tortor. Aenean bibendum erat quis enim faucibus finibus. Aenean urna risus, dapibus ac pellentesque vitae, gravida quis nibh. Ut dui augue, suscipit et purus vel, tristique condimentum dolor. Donec vestibulum iaculis posuere. Maecenas congue nulla sed faucibus porta. Proin in feugiat nisl.`.split(".") + describe("Page", async () => { const total = 20; const tasks = new Tasks(TasksModel, {client}); - before(async () => { + before(async function() { + this.timeout(10000); await tasks.load(total); + await sleep(1000) }) it("Should paginate through all records", async () => { diff --git a/test/connected.service.spec.js b/test/connected.service.spec.js index 82244c08..d92c74bc 100644 --- a/test/connected.service.spec.js +++ b/test/connected.service.spec.js @@ -1,3 +1,4 @@ +const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms)); process.env.AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1; const { Entity, clauses } = require("../src/entity"); const { Service } = require("../src/service"); @@ -269,6 +270,7 @@ database.join(modelTwo); database.join(modelThree); describe("Put and query", async () => { + before(async () => sleep(1000)) it("Should add three records and retrieve correct records based on collections", async () => { let recordOne = { prop1: "prop1", diff --git a/test/connected.where.spec.js b/test/connected.where.spec.js index 04a328b3..8d147ea2 100644 --- a/test/connected.where.spec.js +++ b/test/connected.where.spec.js @@ -1,3 +1,4 @@ +const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms)); process.env.AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1; const { Entity, clauses } = require("../src/entity"); const { expect } = require("chai"); @@ -10,6 +11,7 @@ const client = new DynamoDB.DocumentClient({ describe("General", async () => { + before(async () => sleep(1000)) let WhereTests = new Entity({ service: "tests", entity: "filters", From fa1343b5baa70845a32068570980cb40d0c2e3b3 Mon Sep 17 00:00:00 2001 From: tywalch Date: Wed, 30 Sep 2020 10:05:04 -0400 Subject: [PATCH 8/8] improving original error test to be more specific --- test/connected.crud.spec.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/test/connected.crud.spec.js b/test/connected.crud.spec.js index f4d54a63..e7c48875 100644 --- a/test/connected.crud.spec.js +++ b/test/connected.crud.spec.js @@ -342,13 +342,22 @@ describe("Entity", async () => { it("Should pass back the original dynamodb error when originalErr is set to true", async () => { let id = uuidv4(); let sector = "A1"; - let [success, err] = await MallStores.get({sector, id}) - .go({originalErr: true, params: {TableName: "blahblah"}}) - .then(() => [true, null]) - .catch(err => [false, err]); - expect(success).to.be.false; - expect(err.message).to.be.equal("Requested resource not found") - }) + + let [electroSuccess, electroErr] = await MallStores.get({sector, id}) + .go({params: {TableName: "blahblah"}}) + .then(() => [true, null]) + .catch(err => [false, err]); + + let [originalSuccess, originalErr] = await MallStores.get({sector, id}) + .go({originalErr: true, params: {TableName: "blahblah"}}) + .then(() => [true, null]) + .catch(err => [false, err]); + + expect(electroSuccess).to.be.false; + expect(electroErr.stack.split(/\r?\n/)[1].includes("aws-sdk")).to.be.false; + expect(originalSuccess).to.be.false; + expect(originalErr.stack.split(/\r?\n/)[1].includes("aws-sdk")).to.be.true; + }); });