diff --git a/integration/test/ParseObjectTest.js b/integration/test/ParseObjectTest.js index 64e4a42e0..8efda1c3e 100644 --- a/integration/test/ParseObjectTest.js +++ b/integration/test/ParseObjectTest.js @@ -2060,59 +2060,135 @@ describe('Parse Object', () => { expect(obj.get('string')).toBeInstanceOf(String); }); - it('allowCustomObjectId', async () => { - await reconfigureServer({ allowCustomObjectId: true }); - Parse.allowCustomObjectId = true; - const customId = `${Date.now()}`; - const object = new Parse.Object('TestObject'); - try { + describe('allowCustomObjectId', () => { + it('can save without setting an objectId', async () => { + await reconfigureServer({ allowCustomObjectId: true }); + Parse.allowCustomObjectId = true; + + const object = new Parse.Object('TestObject'); await object.save(); - fail(); - } catch (error) { - expect(error.message).toBe('objectId must not be empty, null or undefined'); - } - object.id = customId; - object.set('foo', 'bar'); - await object.save(); - expect(object.id).toBe(customId); + expect(object.id).toBeDefined(); + + Parse.allowCustomObjectId = false; + }); + + it('fails to save when objectId is empty', async () => { + await reconfigureServer({ allowCustomObjectId: true }); + Parse.allowCustomObjectId = true; + + const object = new Parse.Object('TestObject'); + object.id = ''; + await expectAsync(object.save()).toBeRejectedWith( + new Parse.Error(Parse.Error.MISSING_OBJECT_ID, 'objectId must not be empty or null') + ); + + Parse.allowCustomObjectId = false; + }); + + it('fails to save when objectId is null', async () => { + await reconfigureServer({ allowCustomObjectId: true }); + Parse.allowCustomObjectId = true; + + const object = new Parse.Object('TestObject'); + object.id = null; + await expectAsync(object.save()).toBeRejectedWith( + new Parse.Error(Parse.Error.MISSING_OBJECT_ID, 'objectId must not be empty or null') + ); + + Parse.allowCustomObjectId = false; + }); + + it('can save with custom objectId', async () => { + await reconfigureServer({ allowCustomObjectId: true }); + Parse.allowCustomObjectId = true; + + const customId = `${Date.now()}`; + const object = new Parse.Object('TestObject'); + object.id = customId; + object.set('foo', 'bar'); + await object.save(); + expect(object.id).toBe(customId); + + const query = new Parse.Query('TestObject'); + const result = await query.get(customId); + expect(result.get('foo')).toBe('bar'); + expect(result.id).toBe(customId); - const query = new Parse.Query('TestObject'); - const result = await query.get(customId); - expect(result.get('foo')).toBe('bar'); - expect(result.id).toBe(customId); + result.set('foo', 'baz'); + await result.save(); - result.set('foo', 'baz'); - await result.save(); + const afterSave = await query.get(customId); + expect(afterSave.get('foo')).toBe('baz'); - const afterSave = await query.get(customId); - expect(afterSave.get('foo')).toBe('baz'); - Parse.allowCustomObjectId = false; + Parse.allowCustomObjectId = false; + }); }); - it('allowCustomObjectId saveAll', async () => { - await reconfigureServer({ allowCustomObjectId: true }); - Parse.allowCustomObjectId = true; - const customId1 = `${Date.now()}`; - const customId2 = `${Date.now()}`; - const obj1 = new TestObject({ foo: 'bar' }); - const obj2 = new TestObject({ foo: 'baz' }); - try { + describe('allowCustomObjectId saveAll', () => { + it('can save without setting an objectId', async () => { + await reconfigureServer({ allowCustomObjectId: true }); + Parse.allowCustomObjectId = true; + + const obj1 = new TestObject({ foo: 'bar' }); + const obj2 = new TestObject({ foo: 'baz' }); await Parse.Object.saveAll([obj1, obj2]); - fail(); - } catch (error) { - expect(error.message).toBe('objectId must not be empty, null or undefined'); - } - obj1.id = customId1; - obj2.id = customId2; - await Parse.Object.saveAll([obj1, obj2]); - expect(obj1.id).toBe(customId1); - expect(obj2.id).toBe(customId2); + expect(obj1.id).toBeDefined(); + expect(obj2.id).toBeDefined(); - const query = new Parse.Query(TestObject); - const results = await query.find(); - results.forEach(result => { - expect([customId1, customId2].includes(result.id)); + Parse.allowCustomObjectId = false; + }); + + it('fails to save when objectId is empty', async () => { + await reconfigureServer({ allowCustomObjectId: true }); + Parse.allowCustomObjectId = true; + + const obj1 = new TestObject({ foo: 'bar' }); + obj1.id = ''; + const obj2 = new TestObject({ foo: 'baz' }); + obj2.id = ''; + await expectAsync(Parse.Object.saveAll([obj1, obj2])).toBeRejectedWith( + new Parse.Error(Parse.Error.MISSING_OBJECT_ID, 'objectId must not be empty or null') + ); + + Parse.allowCustomObjectId = false; + }); + + it('fails to save when objectId is null', async () => { + await reconfigureServer({ allowCustomObjectId: true }); + Parse.allowCustomObjectId = true; + + const obj1 = new TestObject({ foo: 'bar' }); + obj1.id = null; + const obj2 = new TestObject({ foo: 'baz' }); + obj2.id = null; + await expectAsync(Parse.Object.saveAll([obj1, obj2])).toBeRejectedWith( + new Parse.Error(Parse.Error.MISSING_OBJECT_ID, 'objectId must not be empty or null') + ); + + Parse.allowCustomObjectId = false; + }); + + it('can save with custom objectId', async () => { + await reconfigureServer({ allowCustomObjectId: true }); + Parse.allowCustomObjectId = true; + + const obj1 = new TestObject({ foo: 'bar' }); + const customId1 = `${Date.now()}`; + obj1.id = customId1; + const obj2 = new TestObject({ foo: 'baz' }); + const customId2 = `${Date.now()}`; + obj1.id = customId2; + await Parse.Object.saveAll([obj1, obj2]); + expect(obj1.id).toBeDefined(); + expect(obj2.id).toBeDefined(); + + const query = new Parse.Query(TestObject); + const results = await query.find(); + results.forEach(result => { + expect([customId1, customId2].includes(result.id)); + }); + + Parse.allowCustomObjectId = false; }); - Parse.allowCustomObjectId = false; }); }); diff --git a/integration/test/helper.js b/integration/test/helper.js index dbd0df18c..836953f3e 100644 --- a/integration/test/helper.js +++ b/integration/test/helper.js @@ -72,6 +72,7 @@ const defaultConfiguration = { enableForAuthenticatedUser: true, }, revokeSessionOnPasswordReset: false, + allowCustomObjectId: false, }; const openConnections = {}; diff --git a/src/ParseObject.js b/src/ParseObject.js index 87738d56f..6e838b72a 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -2399,12 +2399,6 @@ const DefaultController = { if (el instanceof ParseFile) { filesSaved.push(el.save(options)); } else if (el instanceof ParseObject) { - if (allowCustomObjectId && !el.id) { - throw new ParseError( - ParseError.MISSING_OBJECT_ID, - 'objectId must not be empty, null or undefined' - ); - } pending.push(el); } }); @@ -2419,6 +2413,13 @@ const DefaultController = { const batch = []; const nextPending = []; pending.forEach(el => { + if (allowCustomObjectId && Object.prototype.hasOwnProperty.call(el, 'id') && !el.id) { + throw new ParseError( + ParseError.MISSING_OBJECT_ID, + 'objectId must not be empty or null' + ); + } + if (batch.length < batchSize && canBeSerialized(el)) { batch.push(el); } else { @@ -2498,11 +2499,8 @@ const DefaultController = { }); }); } else if (target instanceof ParseObject) { - if (allowCustomObjectId && !target.id) { - throw new ParseError( - ParseError.MISSING_OBJECT_ID, - 'objectId must not be empty, null or undefined' - ); + if (allowCustomObjectId && Object.prototype.hasOwnProperty.call(target, 'id') && !target.id) { + throw new ParseError(ParseError.MISSING_OBJECT_ID, 'objectId must not be empty or null'); } // generate _localId in case if cascadeSave=false target._getId(); diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js index e8294aef0..0e35f56be 100644 --- a/src/__tests__/ParseObject-test.js +++ b/src/__tests__/ParseObject-test.js @@ -3807,27 +3807,22 @@ describe('ParseObject pin', () => { }); }); - it('can allowCustomObjectId', async done => { + it('can allowCustomObjectId', async () => { CoreManager.set('ALLOW_CUSTOM_OBJECT_ID', true); const o = new ParseObject('Person'); + o.id = ''; let params = o._getSaveParams(); expect(params).toEqual({ method: 'POST', - body: { objectId: undefined }, + body: { objectId: '' }, path: 'classes/Person', }); - try { - await o.save(); - done.fail(); - } catch (error) { - expect(error.message).toBe('objectId must not be empty, null or undefined'); - } - try { - await ParseObject.saveAll([o]); - done.fail(); - } catch (error) { - expect(error.message).toBe('objectId must not be empty, null or undefined'); - } + await expect(o.save()).rejects.toEqual( + new ParseError(ParseError.MISSING_OBJECT_ID, 'objectId must not be empty or null') + ); + await expect(ParseObject.saveAll([o])).rejects.toEqual( + new ParseError(ParseError.MISSING_OBJECT_ID, 'objectId must not be empty or null') + ); o._finishFetch({ objectId: 'CUSTOM_ID', createdAt: { __type: 'Date', iso: new Date().toISOString() }, @@ -3840,6 +3835,5 @@ describe('ParseObject pin', () => { path: 'classes/Person/CUSTOM_ID', }); CoreManager.set('ALLOW_CUSTOM_OBJECT_ID', false); - done(); }); });