diff --git a/src/plugins/error-messages.js b/src/plugins/error-messages.js index 04340ae09ce..88994b4239e 100644 --- a/src/plugins/error-messages.js +++ b/src/plugins/error-messages.js @@ -51,7 +51,7 @@ const CODES = { // rx-collection COL1: 'RxDocument.insert() You cannot insert an existing document', - COL2: 'RxCollection.insert() do not provide ._id, it will be generated', + COL2: 'RxCollection.insert() do not provide ._id when it is not the primary key', COL3: 'RxCollection.upsert() does not work without primary', COL4: 'RxCollection.atomicUpsert() does not work without primary', COL5: 'RxCollection.find() if you want to search by _id, use .findOne(_id)', @@ -130,7 +130,6 @@ const CODES = { SC6: 'SchemaCheck: primary can only be defined at top-level', SC7: 'SchemaCheck: default-values can only be defined at top-level', SC8: 'SchemaCheck: first level-fields cannot start with underscore _', - SC9: 'SchemaCheck: schema defines ._id, this will be done automatically', SC10: 'SchemaCheck: schema defines ._rev, this will be done automatically', SC11: 'SchemaCheck: schema need an number>=0 as version', SC12: 'SchemaCheck: primary can only be defined once', diff --git a/src/plugins/schema-check.js b/src/plugins/schema-check.js index bc0cf7bd29e..69fe1a3f785 100644 --- a/src/plugins/schema-check.js +++ b/src/plugins/schema-check.js @@ -20,6 +20,7 @@ import { */ export function checkFieldNameRegex(fieldName) { if (fieldName === '') return; + if (fieldName === '_id') return; if (['properties', 'language'].includes(fieldName)) { throw RxError.newRxError('SC23', { @@ -117,6 +118,9 @@ export function validateFieldsDeep(jsonSchema) { if (!isNested) { // check underscore fields if (fieldName.charAt(0) === '_') { + if (fieldName === '_id' && schemaObj.primary) { + return; + } throw RxError.newRxError('SC8', { fieldName }); @@ -150,13 +154,6 @@ export function validateFieldsDeep(jsonSchema) { * @throws {Error} if something is not ok */ export function checkSchema(jsonID) { - // check _id - if (jsonID.properties._id) { - throw RxError.newRxError('SC9', { - schema: jsonID - }); - } - // check _rev if (jsonID.properties._rev) { throw RxError.newRxError('SC10', { diff --git a/src/rx-collection.js b/src/rx-collection.js index 571b86a2edf..43225052a6c 100644 --- a/src/rx-collection.js +++ b/src/rx-collection.js @@ -368,7 +368,7 @@ export class RxCollection { json = clone(json); json = this.schema.fillObjectWithDefaults(json); - if (json._id) { + if (json._id && this.schema.primaryPath !== '_id') { throw RxError.newRxError('COL2', { data: json }); diff --git a/src/rx-schema.js b/src/rx-schema.js old mode 100755 new mode 100644 diff --git a/test/helper/schema-objects.js b/test/helper/schema-objects.js index 3d8c2443b5e..fe2a2585519 100644 --- a/test/helper/schema-objects.js +++ b/test/helper/schema-objects.js @@ -124,3 +124,10 @@ export function point() { y: faker.random.number() }; } + +export function _idPrimary() { + return { + _id: randomToken(12), + firstName: faker.name.firstName() + }; +} diff --git a/test/helper/schemas.js b/test/helper/schemas.js index d352b0d41fa..0ad65c1c07f 100644 --- a/test/helper/schemas.js +++ b/test/helper/schemas.js @@ -270,10 +270,24 @@ export const nostringIndex = { } }, required: ['firstName', 'lastName'] - }; +export const _idPrimary = { + description: 'the primary is \'_id\'', + version: 0, + type: 'object', + properties: { + _id: { + type: 'string', + primary: true + }, + firstName: { + type: 'string' + } + }, + required: ['firstName'] +}; export const bigHuman = { title: 'human schema', diff --git a/test/unit/key-compression.test.js b/test/unit/key-compression.test.js old mode 100755 new mode 100644 diff --git a/test/unit/primary.test.js b/test/unit/primary.test.js index 931bb73e9f2..9d9519a52bc 100644 --- a/test/unit/primary.test.js +++ b/test/unit/primary.test.js @@ -69,6 +69,15 @@ config.parallel('primary.test.js', () => { assert.ok(schema.validate(obj)); }); }); + + describe('positive', () => { + it('should validate when primary key is _id', () => { + const schema = RxSchema.create(schemas._idPrimary); + const obj = schemaObjects._idPrimary(); + assert.ok(schema.validate(obj)); + }); + }); + describe('negative', () => { it('should not validate the human without primary', () => { const schema = RxSchema.create(schemas.primaryHuman); diff --git a/test/unit/rx-collection.test.js b/test/unit/rx-collection.test.js index bf37cecd36e..9209cdd111a 100644 --- a/test/unit/rx-collection.test.js +++ b/test/unit/rx-collection.test.js @@ -282,6 +282,32 @@ config.parallel('rx-collection.test.js', () => { await collection.insert(schemaObjects.human()); db.destroy(); }); + it('should insert an object with _id set', async () => { + const db = await RxDatabase.create({ + name: util.randomCouchString(10), + adapter: 'memory' + }); + const collection = await db.collection({ + name: 'idprimary', + schema: schemas._idPrimary + }); + await collection.insert(schemaObjects._idPrimary()); + db.destroy(); + }); + it('should insert human (_id given)', async () => { + const db = await RxDatabase.create({ + name: util.randomCouchString(10), + adapter: 'memory' + }); + const collection = await db.collection({ + name: 'human', + schema: schemas.human + }); + const human = schemaObjects.human(); + human._id = util.randomCouchString(20); + await collection.insert(human); + db.destroy(); + }); it('should insert nested human', async () => { const db = await RxDatabase.create({ name: util.randomCouchString(10), @@ -346,14 +372,14 @@ config.parallel('rx-collection.test.js', () => { ); db.destroy(); }); - it('should not insert broken human (_id given)', async () => { + it('should not insert when _id given but _id is not primary', async () => { const db = await RxDatabase.create({ name: util.randomCouchString(10), adapter: 'memory' }); const collection = await db.collection({ - name: 'human', - schema: schemas.human + name: 'humanfinal', + schema: schemas.humanFinal }); const human = schemaObjects.human(); human._id = util.randomCouchString(20); diff --git a/test/unit/rx-schema.test.js b/test/unit/rx-schema.test.js old mode 100755 new mode 100644 index 87bf427bdb0..171563f4060 --- a/test/unit/rx-schema.test.js +++ b/test/unit/rx-schema.test.js @@ -64,6 +64,22 @@ config.parallel('rx-schema.test.js', () => { it('validate point', () => { SchemaCheck.checkSchema(schemas.point); }); + it('validate _id when primary', async () => { + SchemaCheck.checkSchema({ + title: 'schema', + version: 0, + properties: { + _id: { + type: 'string', + primary: true + }, + firstName: { + type: 'string' + } + }, + required: ['firstName'] + }); + }); }); describe('negative', () => { it('break when index is no string', () => { @@ -266,6 +282,26 @@ config.parallel('rx-schema.test.js', () => { } }), Error); }); + it('throw when _id is not primary', async () => { + assert.throws(() => SchemaCheck.checkSchema({ + title: 'schema', + version: 0, + description: 'save as fieldname', + properties: { + userId: { + type: 'string', + primary: true + }, + _id: { + type: 'string', + }, + firstName: { + type: 'string' + } + }, + required: ['firstName'] + }), Error); + }); }); }); describe('.normalize()', () => {