Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

electrodb

Package Overview
Dependencies
Maintainers
1
Versions
163
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

electrodb - npm Package Compare versions

Comparing version 0.9.13 to 0.9.14

test/connected.page.spec.js

2

package.json
{
"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",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -9,24 +9,2 @@ "use strict";

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 {

@@ -61,2 +39,3 @@ constructor(model, config = {}) {

}
this.modelAttributeIdentifier = "__edb_e__"
}

@@ -210,3 +189,3 @@

formatResponse(response, config = {}) {
formatResponse(index, response, config = {}) {
let stackTrace = new Error();

@@ -242,5 +221,9 @@ try {

}
if (config.pager) {
let nextPage = response.LastEvaluatedKey || null;
if (!config.raw && !config.lastEvaluatedKeyRaw) {
nextPage = this._formatReturnPager(index, nextPage, results[results.length - 1]);
}
results = [nextPage, results];

@@ -261,2 +244,101 @@ }

_regexpEscape(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
_deconstructKeys(index, keyType, key, backupFacets = {}) {
if (typeof key !== "string" || key.length === 0) {
return null;
}
// 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];
let names = [];
let pattern = `^${this._regexpEscape(prefix)}`;
for (let facet of facets) {
let { label, name } = this.model.schema.attributes[facet];
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) {
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];
}
}
return results;
}
_deconstructIndex(index = "", lastEvaluated, lastReturned) {
let pkName = this.model.translations.keys[index].pk;
let skName = this.model.translations.keys[index].sk;
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) {
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;
}
_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 tableIndex = "";
let pager = this._constructPagerIndex(index, item);
if (index !== tableIndex) {
pager = {...pager, ...this._constructPagerIndex(tableIndex, item)}
}
return pager
}
_applyParameterOptions(params, ...options) {

@@ -277,2 +359,3 @@ let config = {

if (option.pager) config.pager = true;
if (option.lastEvaluatedKeyRaw === true) config.lastEvaluatedKeyRaw = true;
config.page = Object.assign({}, config.page, option.page);

@@ -292,3 +375,7 @@ config.params = Object.assign({}, config.params, option.params);

if (Object.keys(config.page || {}).length) {
parameters.ExclusiveStartKey = config.page;
if (config.raw || config.lastEvaluatedKeyRaw) {
parameters.ExclusiveStartKey = config.page;
} else {
parameters.ExclusiveStartKey = this._formatSuppliedPager(params.IndexName, config.page);
}
}

@@ -306,3 +393,4 @@

page: options.page,
pager: !!options.pager
pager: !!options.pager,
lastEvaluatedKeyRaw: !!options.lastEvaluatedKeyRaw
};

@@ -315,5 +403,5 @@ let parameters = Object.assign({}, params);

// 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);
}

@@ -414,3 +502,3 @@ } catch (err) {

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);

@@ -428,3 +516,3 @@ let keys = this._makeParameterKey(

filter.ExpressionAttributeNames,
keyExpressions.ExpressionAttributeNames,
keyExpressions.ExpressionAttributeNames
),

@@ -437,2 +525,5 @@ ExpressionAttributeValues: this._mergeExpressionsAttributes(

};
params.ExpressionAttributeNames["#" + this.modelAttributeIdentifier] = this.modelAttributeIdentifier;
params.ExpressionAttributeValues[":" + this.modelAttributeIdentifier] = this.model.entity;
params.FilterExpression = `${params.FilterExpression} AND #${this.modelAttributeIdentifier} = :${this.modelAttributeIdentifier}`
if (hasSortKey) {

@@ -490,3 +581,3 @@ params.FilterExpression = `${params.FilterExpression} AND begins_with(#sk, :sk))`;

...updatedKeys,
__edb_e__: this.model.entity,
[this.modelAttributeIdentifier]: this.model.entity,
},

@@ -751,7 +842,7 @@ TableName: this.model.table,

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)

@@ -930,7 +1021,46 @@ .join(", ")}cannot be updated.`,

_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;
}

@@ -1005,4 +1135,6 @@

let facets = this.model.facets.byIndex[index];
let prefixes = this._getPrefixes(facets);
// let sk = [];
let prefixes = this.model.prefixes[index];
if (!prefixes) {
throw new Error(`Invalid index: ${index}`);
}
let pk = this._makeKey(

@@ -1032,5 +1164,5 @@ prefixes.pk.prefix,

if (isCustom) {
key = `${key}${label}`;
key = `${key}${label === undefined ? "" : label}`;
} else {
key = `${key}#${label || name}_`;
key = `${key}#${label === undefined ? name : label}_`;
}

@@ -1188,3 +1320,5 @@ if (supplied[name] !== undefined) {

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 = {

@@ -1197,9 +1331,10 @@ accessPattern,

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);

@@ -1213,2 +1348,3 @@ sk = {

facets: [...facetArray],
isCustom: parsedSKFacets.isCustom
};

@@ -1280,6 +1416,17 @@ facets.fields.push(sk.field);

};
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;
});
}

@@ -1290,3 +1437,3 @@

}
return {

@@ -1320,5 +1467,14 @@ facets,

_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 {

@@ -1335,2 +1491,3 @@ facets,

let filters = this._normalizeFilters(model.filters);
let prefixes = this._normalizePrefixes(service, entity, version, indexes);
return {

@@ -1337,0 +1494,0 @@ service,

@@ -8,3 +8,3 @@ const { KeyTypes, CastTypes, AttributeTypes } = require("./types");

this.field = definition.field || definition.name;
this.label = definition.label || "";
this.label = definition.label;
this.readOnly = !!definition.readOnly;

@@ -11,0 +11,0 @@ this.required = !!definition.required;

@@ -62,7 +62,7 @@ const __is_clause__ = Symbol("IsWhereClause");

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.`);
}

@@ -121,3 +121,3 @@ }

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");
}

@@ -124,0 +124,0 @@ state.query.filter[expressionType] = this._concatFilterExpression(

@@ -0,1 +1,3 @@

const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
process.env.AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1;

@@ -12,7 +14,2 @@ const { Entity } = require("../src/entity");

const ENTITY = "TEST_ENTITY"
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
let model = {

@@ -143,2 +140,3 @@ service: SERVICE,

describe("Entity", async () => {
before(async () => sleep(1000))
let MallStores = new Entity(model, { client });

@@ -344,3 +342,23 @@ describe("Simple crud", async () => {

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 [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;
});
});

@@ -392,47 +410,2 @@

// 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 () => {

@@ -794,12 +767,14 @@ let db = new Entity(

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');
let [nextIndex, nextStores] = await MallStores.scan.page(index);
expect(stores).to.be.an("array").and.have.length(2);
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, {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').and.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');
}

@@ -806,0 +781,0 @@ }

@@ -0,1 +1,2 @@

const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
process.env.AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1;

@@ -13,2 +14,3 @@ const { Entity, clauses } = require("../src/entity");

describe("General", async () => {
before(async () => sleep(1000))
let FilterTests = new Entity({

@@ -15,0 +17,0 @@ service: "tests",

@@ -0,1 +1,2 @@

const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
process.env.AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1;

@@ -272,2 +273,3 @@ const { Entity, clauses } = require("../src/entity");

describe("Put and query", async () => {
before(async () => sleep(1000))
it("Should add three records and retrieve correct records based on collections", async () => {

@@ -274,0 +276,0 @@ let recordOne = {

@@ -0,1 +1,2 @@

const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
process.env.AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1;

@@ -13,2 +14,3 @@ const { Entity, clauses } = require("../src/entity");

describe("General", async () => {
before(async () => sleep(1000))
let WhereTests = new Entity({

@@ -36,2 +38,5 @@ service: "tests",

type: "boolean"
},
complex: {
type: "any"
}

@@ -241,6 +246,2 @@ },

.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)

@@ -345,3 +346,59 @@ .to.be.an("array")

.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'
});
});
})

@@ -755,2 +755,3 @@ const { Entity, clauses } = require("../src/entity");

"ExpressionAttributeNames": {
"#__edb_e__": "__edb_e__",
"#pk": "pk",

@@ -760,6 +761,7 @@ "#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"

@@ -1862,8 +1864,9 @@ })

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"
});

@@ -1885,3 +1888,3 @@ expect(shouldScan).to.be.true;

KeyConditionExpression: '#pk = :pk',
FilterExpression: '#id = :id1'
FilterExpression: "#id = :id1"
});

@@ -1888,0 +1891,0 @@ expect(keys).to.be.deep.equal([{ name: "id", type: "pk" }]);

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc