From d0f48aa0dc4eeab1be54aafc501b1ba1cbeaface Mon Sep 17 00:00:00 2001 From: "Raschid J.F. Rafaelly" Date: Sun, 15 Sep 2019 16:24:43 -0500 Subject: [PATCH 1/4] test(ParseCloudeTest): add case 'retrieve subclass objects' * Test failing with error "Expected 'default' to be 'foo'." * `jasmine.DEFAULT_TIMEOUT_INTERVAL` was increased to `10000` because the first cases often failed when starting integration testing. Related to issue parse-community/parse-server#5521 --- integration/test/ParseCloudTest.js | 33 ++++++++++++++++++++++++++++++ integration/test/helper.js | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/integration/test/ParseCloudTest.js b/integration/test/ParseCloudTest.js index 01332c8c8..2c78a1f46 100644 --- a/integration/test/ParseCloudTest.js +++ b/integration/test/ParseCloudTest.js @@ -133,4 +133,37 @@ describe('Parse Cloud', () => { done(); }); }); + + it('retrieve subclass objects', async (done) => { + try { + + // Register custom class with initial value prop + class MySubclass extends Parse.Object { + constructor(attr) { + super('MySubclass', attr); + if(!attr || !attr.defaultProp) { + this.defaultProp = 'default'; + } + } + // get defaultProp() { return this.get('defaultProp'); } + // set defaultProp(val) { this.set('defaultProp', val); } + } + Parse.Object.registerSubclass('MySubclass', MySubclass); + + const o = await new MySubclass().save(); + let results = await new Parse.Query(MySubclass).find(); + expect(results.length).toBe(1); + expect(results[0].defaultProp).toBe('default'); + await o.destroy(); + + await new MySubclass({defaultProp: 'foo'}).save(); + results = await new Parse.Query(MySubclass).find(); + expect(results.length).toBe(1); + expect(results[0].defaultProp).toBe('foo'); + + done(); + } catch(e) { + done.fail(e); + } + }); }); diff --git a/integration/test/helper.js b/integration/test/helper.js index df4355669..6cadddf7d 100644 --- a/integration/test/helper.js +++ b/integration/test/helper.js @@ -1,6 +1,6 @@ const ParseServer = require('parse-server').ParseServer; -jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; +jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; beforeAll((done) => { const { app } = require('../server'); const httpServer = require('http').createServer(app); From d961397bcfd7d56c1d25651b9c51cdebe3679ff4 Mon Sep 17 00:00:00 2001 From: "Raschid J.F. Rafaelly" Date: Mon, 16 Sep 2019 00:03:20 -0500 Subject: [PATCH 2/4] test(ParseCloudTed): moved test 'retrieve subclass objects' I found that the problem is in the `ParseObject` class --- integration/test/ParseCloudTest.js | 33 ----------------------------- integration/test/ParseObjectTest.js | 22 +++++++++++++++++++ 2 files changed, 22 insertions(+), 33 deletions(-) diff --git a/integration/test/ParseCloudTest.js b/integration/test/ParseCloudTest.js index 2c78a1f46..01332c8c8 100644 --- a/integration/test/ParseCloudTest.js +++ b/integration/test/ParseCloudTest.js @@ -133,37 +133,4 @@ describe('Parse Cloud', () => { done(); }); }); - - it('retrieve subclass objects', async (done) => { - try { - - // Register custom class with initial value prop - class MySubclass extends Parse.Object { - constructor(attr) { - super('MySubclass', attr); - if(!attr || !attr.defaultProp) { - this.defaultProp = 'default'; - } - } - // get defaultProp() { return this.get('defaultProp'); } - // set defaultProp(val) { this.set('defaultProp', val); } - } - Parse.Object.registerSubclass('MySubclass', MySubclass); - - const o = await new MySubclass().save(); - let results = await new Parse.Query(MySubclass).find(); - expect(results.length).toBe(1); - expect(results[0].defaultProp).toBe('default'); - await o.destroy(); - - await new MySubclass({defaultProp: 'foo'}).save(); - results = await new Parse.Query(MySubclass).find(); - expect(results.length).toBe(1); - expect(results[0].defaultProp).toBe('foo'); - - done(); - } catch(e) { - done.fail(e); - } - }); }); diff --git a/integration/test/ParseObjectTest.js b/integration/test/ParseObjectTest.js index 24285663c..d59bfa69e 100644 --- a/integration/test/ParseObjectTest.js +++ b/integration/test/ParseObjectTest.js @@ -1774,4 +1774,26 @@ describe('Parse Object', () => { const fetched = await query.get(user.id); assert.equal(fetched.isDataAvailable(), true); }); + + it('retrieve subclass objects', async (done) => { + try { + class MySubclass extends Parse.Object { + constructor(attr) { + super('MySubclass', attr); + this.defaultProp = this.defaultProp || 'default'; + } + get defaultProp() { return this.get('defaultProp'); } + set defaultProp(val) { this.set('defaultProp', val); } + } + Parse.Object.registerSubclass('MySubclass', MySubclass); + + await new MySubclass({defaultProp: 'foo'}).save(); + const result = await new Parse.Query(MySubclass).first(); + expect(result.defaultProp).toBe('foo'); + + done(); + } catch(e) { + done.fail(e); + } + }); }); From a6d613d01da4d20d55365f6ad89284c0aabad2c0 Mon Sep 17 00:00:00 2001 From: "Raschid J.F. Rafaelly" Date: Mon, 16 Sep 2019 01:59:15 -0500 Subject: [PATCH 3/4] feat(ParseObject): enable subclasses to set initial values for props Sobclasses of `ParseObject` where unable to set initial values for their props and maintain them after fetching from the server. The function `ParseObject.fromJSON()` has been modified to allow this feature. Two test cases have been added. It may close parse-community/parse-server/#5521 --- integration/test/ParseObjectTest.js | 29 +++++++++++++++++++++++++++++ src/ParseObject.js | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/integration/test/ParseObjectTest.js b/integration/test/ParseObjectTest.js index d59bfa69e..f7ee25bdb 100644 --- a/integration/test/ParseObjectTest.js +++ b/integration/test/ParseObjectTest.js @@ -1796,4 +1796,33 @@ describe('Parse Object', () => { done.fail(e); } }); + + it('conver from JSON', async (done) => { + try { + + // Object without constructor + const o = Parse.Object.fromJSON({ + className: 'FooObject', + name: 'foo' + }, true); + + await o.save(); + let result = await new Parse.Query('FooObject').first(); + expect(result.get('name')).toBe('foo'); + + // Object with constructor `TestObject` + const to = Parse.Object.fromJSON({ + className: 'TestObject', + name: 'foo' + }, true); + + await to.save(); + result = await new Parse.Query('TestObject').first(); + expect(result.get('name')).toBe('foo'); + + done(); + } catch(e) { + done.fail(e); + } + }); }); diff --git a/src/ParseObject.js b/src/ParseObject.js index 61f9ad83d..de1f6f79e 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -1702,7 +1702,7 @@ class ParseObject { throw new Error('Cannot create an object without a className'); } const constructor = classMap[json.className]; - const o = constructor ? new constructor() : new ParseObject(json.className); + const o = constructor ? new constructor(json) : new ParseObject(json.className, Object.assign(json, { className: undefined })); const otherAttributes = {}; for (const attr in json) { if (attr !== 'className' && attr !== '__type') { From 8c9bf87537129a8300a075edd42c36ecc451193a Mon Sep 17 00:00:00 2001 From: "Raschid J.F. Rafaelly" Date: Wed, 6 Nov 2019 10:36:43 -0600 Subject: [PATCH 4/4] fix(ParseObject): remove reserved fields in `fromJSON()` * reserved field prevented the creation of some classes such as ParseUser * fix test 'retrieve subclass objects' in ParseObjectTest.js --- integration/test/ParseObjectTest.js | 2 +- src/ParseObject.js | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/integration/test/ParseObjectTest.js b/integration/test/ParseObjectTest.js index f7ee25bdb..e04c0ffdb 100644 --- a/integration/test/ParseObjectTest.js +++ b/integration/test/ParseObjectTest.js @@ -1780,7 +1780,7 @@ describe('Parse Object', () => { class MySubclass extends Parse.Object { constructor(attr) { super('MySubclass', attr); - this.defaultProp = this.defaultProp || 'default'; + this.defaultProp = attr && attr.defaultProp || 'default'; } get defaultProp() { return this.get('defaultProp'); } set defaultProp(val) { this.set('defaultProp', val); } diff --git a/src/ParseObject.js b/src/ParseObject.js index 8e052a913..887225e83 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -1700,8 +1700,23 @@ class ParseObject { if (!json.className) { throw new Error('Cannot create an object without a className'); } + const constructor = classMap[json.className]; - const o = constructor ? new constructor(json) : new ParseObject(json.className, Object.assign(json, { className: undefined })); + + // Remove reserved fields + const attributes = {} + Object.assign({}, json); + if(constructor && constructor.readOnlyAttributes) { + constructor.readOnlyAttributes().forEach(attr => { + try { delete attributes[attr]; } + catch { 0; } + }); + } + if(attributes && attributes.__type) delete attributes.__type; + if(attributes && attributes.className) delete attributes.className; + + const o = constructor ? new constructor(attributes) : new ParseObject(json.className, attributes); + const otherAttributes = {}; for (const attr in json) { if (attr !== 'className' && attr !== '__type') {