diff --git a/changelog.md b/changelog.md index cc16542..3bc5108 100644 --- a/changelog.md +++ b/changelog.md @@ -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. diff --git a/lib/shared.js b/lib/shared.js index 2969fa9..76f78dd 100644 --- a/lib/shared.js +++ b/lib/shared.js @@ -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({ @@ -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 }; diff --git a/lib/table.js b/lib/table.js index 1df7d38..483bde4 100644 --- a/lib/table.js +++ b/lib/table.js @@ -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, @@ -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; } } @@ -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}.`); } @@ -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 diff --git a/test/multi.js b/test/multi.js new file mode 100644 index 0000000..f26af1b --- /dev/null +++ b/test/multi.js @@ -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();