diff --git a/integration/cloud/main.js b/integration/cloud/main.js index 6b632de2f..45378793b 100644 --- a/integration/cloud/main.js +++ b/integration/cloud/main.js @@ -49,3 +49,22 @@ Parse.Cloud.job('CloudJob2', function() { Parse.Cloud.job('CloudJobFailing', function() { throw 'cloud job failed'; }); + +//ugly patch. Should not stay here, but I don't know how to test changes in Cloud Code otherwise +//parse-server uses the published parse SDK version, but we want to test the patched version via cloud code +const PatchedParse = require('../../node').Parse; +//FunctionsRouter.js 104 calls Parse._encode directly, so it must be patched. +Parse._encode = PatchedParse._encode; +//Parse.Object calls encode, so it must be patched. +Parse.Object = PatchedParse.Object; + +Parse.Cloud.define('getUnsavedObject', function(){ + const parent = new Parse.Object("Unsaved"); + const child = new Parse.Object("secondUnsaved"); + const childOfChild = new Parse.Object("thirdUnsaved"); + child.set("foz", "baz"); + child.set("child", childOfChild); + parent.set("foo", "bar"); + parent.set("child", child); + return parent; +}); \ No newline at end of file diff --git a/integration/test/ParseCloudTest.js b/integration/test/ParseCloudTest.js index 01332c8c8..c1ab98761 100644 --- a/integration/test/ParseCloudTest.js +++ b/integration/test/ParseCloudTest.js @@ -133,4 +133,15 @@ describe('Parse Cloud', () => { done(); }); }); + + it('should transfer unsaved object as full JSON', (done) => { + Parse.Cloud.run('getUnsavedObject').then((result) => { + expect(result).toBeInstanceOf(Parse.Object); + expect(result.get('foo')).toEqual('bar'); + expect(result.get('child')).toBeInstanceOf(Parse.Object); + expect(result.get('child').get('child')).toBeInstanceOf(Parse.Object); + expect(result.get('child').get('foz')).toEqual('baz'); + done(); + }).catch(done.fail); + }); }); diff --git a/src/__tests__/encode-test.js b/src/__tests__/encode-test.js index 665036996..430d7432a 100644 --- a/src/__tests__/encode-test.js +++ b/src/__tests__/encode-test.js @@ -263,4 +263,15 @@ describe('encode', () => { str: 'abc' }); }); + + it('should throw an error when recursion is detected in unsaved objects', (done) => { + const parent = new ParseObject('Parent'); + const child = new ParseObject('Child'); + parent._serverData = {}; + child._serverData = {}; + parent.attributes = { child : child}; + child.attributes = {parent : parent}; + expect(function() {encode(parent)}).toThrow("Circular recursion is not allowed on temporary Objects."); + done(); + }); }); diff --git a/src/encode.js b/src/encode.js index f1913328f..6addc7287 100644 --- a/src/encode.js +++ b/src/encode.js @@ -32,9 +32,18 @@ function encode(value: mixed, disallowObjects: boolean, forcePointers: boolean, if (offline && value._getId().startsWith('local')) { return value.toOfflinePointer(); } - if (value.id === undefined){ - return value._toFullJSON(); + + if (value.id === undefined) { + if (value._temp) { + throw new Error("Circular recursion is not allowed on temporary Objects."); + } + if (seen.includes(value)) { + value._temp = true; + } + seen = seen.concat(seenEntry); + return value._toFullJSON(seen, offline); } + return value.toPointer(); } seen = seen.concat(seenEntry);