Skip to content

Commit

Permalink
Fix compatibility among types from multiple module instances.
Browse files Browse the repository at this point in the history
  • Loading branch information
autopulated committed Apr 8, 2024
1 parent f100d83 commit 474d123
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 19 deletions.
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### 1.3.2
* Fixes compatibility of Schemas with tables from different dynamodm module instances.

### 1.3.1
* Fixes removal of unversioned documents via a versioned model.

Expand Down
30 changes: 15 additions & 15 deletions lib/shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
const Ajv = require('ajv');

// internal types for AJV
const kExtendedTypeDate = Symbol('extendedType:date');
const kExtendedTypeBuffer = Symbol('extendedType:buffer');
const kExtendedTypeDate = Symbol.for('dynamodm:extendedType:date');
const kExtendedTypeBuffer = Symbol.for('dynamodm:extendedType:buffer');

// this AJV instance is used for general validation, compiled schemas do not modify data
const ajv = new Ajv({
Expand Down Expand Up @@ -118,19 +118,19 @@ unMarshallingAjv.addKeyword({
}
});

const kModelTable = Symbol();
const kModelSchema = Symbol();
const kModelLogger = Symbol();
const kTableIsReady = Symbol();
const kTableDDBClient = Symbol();
const kTableIndices = Symbol();
const kTableGetBackoffDelayMs = Symbol();
const kSchemaCompiled = Symbol('schema:compiled');
const kSchemaMarshall = Symbol('schema:marshall');
const kSchemaUnMarshall = Symbol('schema:unmarshall');
const kSchemaIndices = Symbol('schema:indices');
const kSchemaNewId = Symbol('schema:newId');
const kOptionSkipValidation = Symbol();
const kModelTable = Symbol.for('dynamodm:model:table');
const kModelSchema = Symbol.for('dynamodm:model:schema');
const kModelLogger = Symbol.for('dynamodm:model:logger');
const kTableIsReady = Symbol.for('dynamodm:table:ready');
const kTableDDBClient = Symbol.for('dynamodm:table:ddbc');
const kTableIndices = Symbol.for('dynamodm:table:indices');
const kTableGetBackoffDelayMs = Symbol.for('dynamodm:table:gbdms');
const kSchemaCompiled = Symbol.for('dynamodm:schema:compiled');
const kSchemaMarshall = Symbol.for('dynamodm:schema:marshall');
const kSchemaUnMarshall = Symbol.for('dynamodm:schema:unmarshall');
const kSchemaIndices = Symbol.for('dynamodm:schema:indices');
const kSchemaNewId = Symbol.for('dynamodm:schema:newId');
const kOptionSkipValidation = Symbol.for('dynamodm:option:skipValidate');

// Built-in schema types
const DocId = { type:'string', minLength:1, maxLength:1024 };
Expand Down
9 changes: 5 additions & 4 deletions lib/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
const { DynamoDBClient, CreateTableCommand, DescribeTableCommand, UpdateTableCommand, DeleteTableCommand } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient } = require('@aws-sdk/lib-dynamodb');

const { Schema } = require('./schema');
const { createModel } = require('./model');
const {
kTableIsReady,
Expand Down Expand Up @@ -110,8 +109,8 @@ class Table {
}));
} catch (err) {
// ResourceInUseException is only thrown if the table already exists
/* c8 ignore next 3 */
if (err.name !== 'ResourceInUseException') {
/* c8 ignore next 2 */
throw err;
}
}
Expand All @@ -129,8 +128,8 @@ class Table {
} else if (response.Table.TableStatus === 'ACTIVE' || response.Table.TableStatus === 'UPDATING') {
this.#logger.info('Table %s now %s', this.name, response.Table.TableStatus);
created = true;
/* c8 ignore next 3 */
} else {
/* c8 ignore next 2 */
throw new Error(`Table ${this.name} status is ${response.Table.TableStatus}.`);
}

Expand Down Expand Up @@ -194,7 +193,9 @@ class Table {
}

model(schema){
if (!(schema instanceof Schema)) {
// Not checking using 'instanceof' here, because the Schema might come
// from a different realm, and we want to allow that.
if (!(schema && schema.name && schema.idFieldName && schema.source && schema.methods)) {
throw new Error('The model schema must be a valid DynamoDM.Schema().');
}
// add the schema to the table's schema list and return a corresponding
Expand Down
79 changes: 79 additions & 0 deletions test/multi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const t = require('tap');

const clientOptions = {
endpoint: 'http://localhost:8000'
};

// get two unique instances by clearing the require cache:
const DynamoDMConstructor1 = require('../');
for (const k in require.cache) {
delete require.cache[k];
}
const DynamoDMConstructor2 = require('../');

const DynamoDM1 = DynamoDMConstructor1({clientOptions, logger:{level:'error'}});
const DynamoDM2 = DynamoDMConstructor2({clientOptions, logger:{level:'error'}});

t.test('unique identity', async t => {
t.ok(DynamoDMConstructor1 !== DynamoDMConstructor2, "check that we've created separate module instances for testing");
t.notOk(DynamoDM1.Schema('test') instanceof DynamoDM2.Schema('test').constructor, 'check that Schema types are unique');
});


t.test('crud with schema from separate module', async t => {
const table = DynamoDM1.Table({ name: 'test-table-multi-crud'});
const ThingSchema = DynamoDM2.Schema('namespace.thing', {
properties: {
id: DynamoDM2.DocIdField,
aaaa: {type: 'string'},
bbbb: {type: 'number'},
cccc: {type: 'string'},
blob: DynamoDM2.Binary,
createdAt: DynamoDM2.CreatedAtField,
updatedAt: DynamoDM2.UpdatedAtField
},
required: ['id', 'aaaa', 'bbbb'],
additionalProperties: false
});
const Thing = table.model(ThingSchema);

await table.ready();

let x, y;
await t.test('create', async t => {
x = new Thing({aaaa: 'a', bbbb:1, blob: Buffer.from('hello crud'), });
await x.save();

y = await Thing.getById(x.id);
t.strictSame(y, x);
});

await t.test('update', async t => {
x.bbbb = 2;
await x.save();

y = await Thing.getById(x.id);
t.strictSame(y, x);
});

await t.test('query', async t => {
const aThing = await Thing.queryOne({ type: 'namespace.thing' });
t.equal(aThing.constructor, (new Thing({aaaa:'a',bbbb:2})).constructor, 'should have the correct constructor');
});

await t.test('remove', async t => {
const oldId = x.id;
await x.remove();

t.equal(await Thing.getById(oldId), null);
});

t.teardown(async () => {
await table.deleteTable();
table.destroyConnection();
});

t.end();
});

t.end();

0 comments on commit 474d123

Please sign in to comment.