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 1.3.2 to 1.4.0

8

CHANGELOG.md

@@ -69,2 +69,8 @@ # Changelog

### Fixed
- Newly added method `parse()` had critical typo. Method now has an improved api, and appropriate tests [[read more]](./README.md#parse)
- Newly added method `parse()` had critical typo. Method now has an improved api, and appropriate tests [[read more]](./README.md#parse)
### [1.4.0] = 2021-08-22
### Added
- Added support for choosing the case ElectroDB will use when modeling a Partition or Sort Key. [[read more]](./README.md#using-electrodb-with-existing-data)
- Added support for indexes to use fields that are shared with attribute fields. This should help users leverage ElectroDB with existing tables. [[read more]](./README.md#using-electrodb-with-existing-data)
- Added Query Option `ignoreOwnership` to bypass ElectroDB checks/interrogations for ownership of an item before returning it. [[read more]](./README.md#query-options)

2

package.json
{
"name": "electrodb",
"version": "1.3.2",
"version": "1.4.0",
"description": "A library to more easily create and interact with multiple entities and heretical relationships in dynamodb",

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

@@ -118,2 +118,14 @@ // # Errors:

},
InvalidIndexWithAttributeName: {
code: 1018,
section: "invalid-index-with-attribute-name",
name: "InvalidIndexWithAttributeName",
sym: ErrorCode,
},
InvalidCollectionOnIndexWithAttributeFieldNames: {
code: 1019,
section: "invalid-collection-on-index-with-attribute-field-names",
name: "InvalidIndexCompositeWithAttributeName",
sym: ErrorCode,
},
MissingAttribute: {

@@ -120,0 +132,0 @@ code: 2001,

@@ -233,3 +233,3 @@ const {AttributeTypes, ItemOperations, AttributeProxySymbol, BuilderTypes} = require("./types");

class ExpressionState {
constructor({prefix} = {}) {
constructor({prefix, singleOccurrence} = {}) {
this.names = {};

@@ -242,5 +242,9 @@ this.values = {};

this.prefix = prefix || "";
this.singleOccurrence = singleOccurrence;
}
incrementName(name) {
if (this.singleOccurrence) {
return `${this.prefix}${0}`
}
if (this.counts[name] === undefined) {

@@ -373,3 +377,4 @@ this.counts[name] = 0;

const attributeValues = [];
for (const value of values) {
for (let value of values) {
value = target.format(value);
// template.length is to see if function takes value argument

@@ -376,0 +381,0 @@ if (template.length > 2) {

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

const { CastTypes, ValueTypes, AttributeTypes, AttributeMutationMethods, AttributeWildCard, PathTypes, TraverserIndexes } = require("./types");
const { CastTypes, ValueTypes, KeyCasing, AttributeTypes, AttributeMutationMethods, AttributeWildCard, PathTypes, TraverserIndexes } = require("./types");
const AttributeTypeNames = Object.keys(AttributeTypes);

@@ -6,2 +6,3 @@ const ValidFacetTypes = [AttributeTypes.string, AttributeTypes.number, AttributeTypes.boolean, AttributeTypes.enum];

const u = require("./util");
const v = require("./validations");
const {DynamoDBSet} = require("./set");

@@ -100,2 +101,5 @@

this.validate = this._makeValidate(definition.validate);
this.isKeyField = !!definition.isKeyField;
this.unformat = this._makeDestructureKey(definition);
this.format = this._makeStructureKey(definition);
this.indexes = [...(definition.indexes || [])];

@@ -220,7 +224,8 @@ let {isWatched, isWatcher, watchedBy, watching, watchAll} = Attribute._destructureWatcher(definition);

const getter = get || ((attr) => attr);
return (values, siblings) => {
return (value, siblings) => {
if (this.hidden) {
return;
}
return getter(values, siblings);
value = this.unformat(value);
return getter(value, siblings);
}

@@ -234,2 +239,28 @@ }

_makeStructureKey({prefix = "", postfix = "", casing= KeyCasing.none} = {}) {
return (key) => {
let value = key;
if (this.type === AttributeTypes.string && v.isStringHasLength(key)) {
value = `${prefix}${key}${postfix}`;
}
return u.formatAttributeCasing(value, casing);
}
}
_makeDestructureKey({prefix = "", postfix = "", casing= KeyCasing.none} = {}) {
return (key) => {
let value = "";
if (![AttributeTypes.string, AttributeTypes.enum].includes(this.type) || typeof key !== "string") {
return key;
} else if (key.length > prefix.length) {
for (let i = prefix.length; i < key.length - postfix.length; i++) {
value += key[i];
}
} else {
value = key;
}
return u.formatAttributeCasing(value, casing);
};
}
getPathType(type, parentType) {

@@ -858,16 +889,74 @@ if (parentType === AttributeTypes.list || parentType === AttributeTypes.set) {

}
if (facets.fields && facets.fields.includes(name)) {
continue;
const field = attribute.field || name;
let isKeyField = false;
let prefix = "";
let postfix = "";
let casing = KeyCasing.none;
if (facets.byField && facets.byField[field] !== undefined) {
for (const indexName of Object.keys(facets.byField[field])) {
let definition = facets.byField[field][indexName];
if (definition.facets.length > 1) {
throw new e.ElectroError(
e.ErrorCodes.InvalidIndexCompositeWithAttributeName,
`Invalid definition for "${definition.type}" field on index "${u.formatIndexNameForDisplay(indexName)}". The ${definition.type} field "${definition.field}" shares a field name with an attribute defined on the Entity, and therefore is not allowed to contain composite references to other attributes. Please either change the field name of the attribute, or redefine the index to use only the single attribute "${definition.field}".`
)
}
if (definition.isCustom) {
const keyFieldLabels = facets.labels[indexName][definition.type].labels;
// I am not sure how more than two would happen but it would mean either
// 1. Code prior has an unknown edge-case.
// 2. Method is being incorrectly used.
if (keyFieldLabels.length > 2) {
throw new e.ElectroError(
e.ErrorCodes.InvalidIndexWithAttributeName,
`Unexpected definition for "${definition.type}" field on index "${u.formatIndexNameForDisplay(indexName)}". The ${definition.type} field "${definition.field}" shares a field name with an attribute defined on the Entity, and therefore is not possible to have more than two labels as part of it's template. Please either change the field name of the attribute, or reformat the key template to reduce all pre-fixing or post-fixing text around the attribute reference to two.`
)
}
isKeyField = true;
casing = definition.casing;
// Walk through the labels, given the above exception handling, I'd expect the first element to
// be the prefix and the second element to be the postfix.
for (const value of keyFieldLabels) {
if (value.name === field) {
prefix = value.label || "";
} else {
postfix = value.label || "";
}
}
if (attribute.type !== AttributeTypes.string && !Array.isArray(attribute.type)) {
if (prefix.length > 0 || postfix.length > 0) {
throw new e.ElectroError(e.ErrorCodes.InvalidIndexWithAttributeName, `definition for "${definition.type}" field on index "${u.formatIndexNameForDisplay(indexName)}". Index templates may only have prefix or postfix values on "string" or "enum" type attributes. The ${definition.type} field "${field}" is type "${attribute.type}", and therefore cannot be used with prefixes or postfixes. Please either remove the prefixed or postfixed values from the template or change the field name of the attribute.`);
}
}
} else {
// Upstream middleware should have taken care of this. An error here would mean:
// 1. Code prior has an unknown edge-case.
// 2. Method is being incorrectly used.
throw new e.ElectroError(
e.ErrorCodes.InvalidIndexCompositeWithAttributeName,
`Unexpected definition for "${definition.type}" field on index "${u.formatIndexNameForDisplay(indexName)}". The ${definition.type} field "${definition.field}" shares a field name with an attribute defined on the Entity, and therefore must be defined with a template. Please either change the field name of the attribute, or add a key template to the "${definition.type}" field on index "${u.formatIndexNameForDisplay(indexName)}" with the value: "\${${definition.field}}"`
)
}
if (definition.inCollection) {
throw new e.ElectroError(
e.ErrorCodes.InvalidCollectionOnIndexWithAttributeFieldNames,
`Invalid use of a collection on index "${u.formatIndexNameForDisplay(indexName)}". The ${definition.type} field "${definition.field}" shares a field name with an attribute defined on the Entity, and therefore the index is not allowed to participate in a Collection. Please either change the field name of the attribute, or remove all collection(s) from the index.`
)
}
}
}
if (attribute.field && facets.fields && facets.fields.includes(attribute.field)) {
continue;
}
let isKey = !!facets.byIndex && facets.byIndex[""].all.find((facet) => facet.name === name);
let definition = {
name,
field,
client,
casing,
prefix,
postfix,
traverser,
client,
isKeyField,
label: attribute.label,
required: !!attribute.required,
field: attribute.field || name,
default: attribute.default,

@@ -874,0 +963,0 @@ validate: attribute.validate,

const { Entity } = require("./entity");
const { clauses } = require("./clauses");
const { ServiceVersions, Pager, ElectroInstance, ElectroInstanceTypes, ModelVersions } = require("./types");
const { KeyCasing, ServiceVersions, Pager, ElectroInstance, ElectroInstanceTypes, ModelVersions } = require("./types");
const { FilterFactory } = require("./filters");

@@ -17,3 +17,3 @@ const { FilterOperations } = require("./operations");

unknown: "unknown"
}
};

@@ -284,3 +284,3 @@ function inferConstructorType(service) {

for (let entity of Object.values(this.collectionSchema[collection].entities)) {
if (entity.ownsPager(this.collectionSchema[collection].index, pager)) {
if (entity.ownsPager(pager, this.collectionSchema[collection].index)) {
matchingEntities.push(entity);

@@ -333,3 +333,3 @@ }

for (let entity of Object.values(this.collectionSchema[collection].entities)) {
if (entity.ownsPager(this.collectionSchema[collection].index, pager)) {
if (entity.ownsPager(pager, this.collectionSchema[collection].index)) {
matchingIdentifiers.push({

@@ -408,4 +408,13 @@ [entity.identifiers.entity]: entity.getName(),

_validateIndexCasingMatch(definition = {}, providedIndex = {}) {
const definitionSk = definition.sk || {};
const providedSk = providedIndex.sk || {};
const pkCasingMatch = v.isMatchingCasing(definition.pk.casing, providedIndex.pk.casing);
const skCasingMatch = v.isMatchingCasing(definitionSk.casing, providedSk.casing);
return {
pk: pkCasingMatch,
sk: skCasingMatch
};
}
_validateCollectionDefinition(definition = {}, providedIndex = {}) {

@@ -417,4 +426,5 @@ let indexMatch = definition.index === providedIndex.index;

let collectionDifferences = [];
let definitionIndexName = definition.index || "(Primary Index)";
let providedIndexName = providedIndex.index || "(Primary Index)";
let definitionIndexName = u.formatIndexNameForDisplay(definition.index);
let providedIndexName = u.formatIndexNameForDisplay(providedIndex.index);
let matchingKeyCasing = this._validateIndexCasingMatch(definition, providedIndex);
if (pkFacetLengthMatch) {

@@ -446,2 +456,14 @@ for (let i = 0; i < definition.pk.labels.length; i++) {

}
if (!matchingKeyCasing.pk) {
collectionDifferences.push(
`The pk property "casing" provided "${providedIndex.pk.casing || KeyCasing.default}" does not match established casing "${definition.pk.casing || KeyCasing.default}" on index "${providedIndexName}". Index casing options must match across all entities participating in a collection`
);
}
if (!matchingKeyCasing.sk) {
const definedSk = definition.sk || {};
const providedSk = providedIndex.sk || {};
collectionDifferences.push(
`The sk property "casing" provided "${definedSk.casing || KeyCasing.default}" does not match established casing "${providedSk.casing || KeyCasing.default}" on index "${providedIndexName}". Index casing options must match across all entities participating in a collection`
);
}
if (!indexMatch) {

@@ -509,9 +531,9 @@ collectionDifferences.push(

_processEntityKeys(definition = {}, providedIndex = {}) {
_processEntityKeys(name, definition = {}, providedIndex = {}) {
if (!Object.keys(definition).length) {
definition = providedIndex;
}
let [invalidDefinition, invalidIndexMessages] = this._validateCollectionDefinition(definition, providedIndex);
const [invalidDefinition, invalidIndexMessages] = this._validateCollectionDefinition(definition, providedIndex);
if (invalidDefinition) {
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, invalidIndexMessages.join(", "));
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, `Validation Error while joining entity, "${name}". ${invalidIndexMessages.join(", ")}`);
}

@@ -618,3 +640,3 @@ return definition;

}
this.collectionSchema[collection].keys = this._processEntityKeys(this.collectionSchema[collection].keys, providedIndex);
this.collectionSchema[collection].keys = this._processEntityKeys(name, this.collectionSchema[collection].keys, providedIndex);
this.collectionSchema[collection].attributes = this._processEntityAttributes(this.collectionSchema[collection].attributes, entity.model.schema.attributes);

@@ -626,2 +648,3 @@ this.collectionSchema[collection].entities[name] = entity;

this.collectionSchema[collection].collection[collectionIndex] = collection;
}

@@ -628,0 +651,0 @@

@@ -177,2 +177,9 @@ const KeyTypes = {

const KeyCasing = {
none: "none",
upper: "upper",
lower: "lower",
default: "default",
}
module.exports = {

@@ -182,2 +189,3 @@ Pager,

CastTypes,
KeyCasing,
PathTypes,

@@ -184,0 +192,0 @@ QueryTypes,

const {AttributeOperationProxy, ExpressionState} = require("./operations");
const {ItemOperations, BuilderTypes} = require("./types");
const u = require("./util");
class UpdateExpression extends ExpressionState {
constructor(props) {
super(props);
constructor(props = {}) {
super({...props, singleOccurrence: true});
this.operations = {

@@ -9,0 +8,0 @@ set: new Set(),

@@ -81,2 +81,41 @@ const v = require("./validations");

function formatStringCasing(str, casing, defaultCase) {
if (typeof str !== "string") {
return str;
}
let strCase = defaultCase;
if (v.isStringHasLength(casing) && typeof t.KeyCasing[casing] === "string") {
strCase = t.KeyCasing.default === casing
? defaultCase
: t.KeyCasing[casing];
}
switch (strCase) {
case t.KeyCasing.upper:
return str.toUpperCase();
case t.KeyCasing.none:
return str;
case t.KeyCasing.lower:
return str.toLowerCase();
case t.KeyCasing.default:
default:
return str;
}
}
function formatKeyCasing(str, casing) {
return formatStringCasing(str, casing, t.KeyCasing.lower);
}
function formatAttributeCasing(str, casing) {
return formatStringCasing(str, casing, t.KeyCasing.none);
}
function formatIndexNameForDisplay(index) {
if (index) {
return index;
} else {
return "(Primary Index)";
}
}
module.exports = {

@@ -87,5 +126,8 @@ batchItems,

getModelVersion,
formatKeyCasing,
genericizeJSONPath,
commaSeparatedString,
applyBetaModelOverrides
formatAttributeCasing,
applyBetaModelOverrides,
formatIndexNameForDisplay
};
const e = require("./errors");
const {KeyCasing} = require("./types")

@@ -101,2 +102,7 @@ const Validator = require("jsonschema").Validator;

},
casing: {
type: "string",
enum: ["upper", "lower", "none", "default"],
required: false,
}
},

@@ -130,2 +136,7 @@ },

},
casing: {
type: "string",
enum: ["upper", "lower", "none", "default"],
required: false,
}
},

@@ -328,13 +339,32 @@ },

function isMatchingCasing(casing1, casing2) {
const equivalentCasings = [KeyCasing.default, KeyCasing.lower];
if (isStringHasLength(casing1) && isStringHasLength(casing2)) {
let isRealCase = KeyCasing[casing1.toLowerCase()] !== undefined;
let casingsMatch = casing1 === casing2;
let casingsAreEquivalent = [casing1, casing2].every(casing => {
return casing === KeyCasing.lower || casing === KeyCasing.default;
});
return isRealCase && (casingsMatch || casingsAreEquivalent);
} else if (isStringHasLength(casing1)) {
return equivalentCasings.includes(casing1.toLowerCase());
} else if (isStringHasLength(casing2)) {
return equivalentCasings.includes(casing2.toLowerCase());
} else {
return casing1 === undefined && casing2 === undefined;
}
}
module.exports = {
model: validateModel,
testModel,
isFunction,
stringArrayMatch,
isMatchingCasing,
isArrayHasLength,
isNameModelRecordType,
isStringHasLength,
isObjectHasLength,
stringArrayMatch,
isFunction,
isBetaServiceConfig,
isNameEntityRecordType
isNameModelRecordType,
isNameEntityRecordType,
model: validateModel
};

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

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

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

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