diff --git a/integration/test/ParseACLTest.js b/integration/test/ParseACLTest.js
index d1340cfe7..32f8e9442 100644
--- a/integration/test/ParseACLTest.js
+++ b/integration/test/ParseACLTest.js
@@ -10,7 +10,9 @@ describe('Parse.ACL', () => {
it('acl must be valid', () => {
const user = new Parse.User();
- assert.equal(user.setACL(`Ceci n'est pas un ACL.`), false);
+ expect(() => user.setACL(`Ceci n'est pas un ACL.`)).toThrow(
+ new Parse.Error(Parse.Error.OTHER_CAUSE, 'ACL must be a Parse ACL.')
+ );
});
it('can refresh object with acl', async () => {
diff --git a/integration/test/ParseObjectTest.js b/integration/test/ParseObjectTest.js
index e289923ad..05977687c 100644
--- a/integration/test/ParseObjectTest.js
+++ b/integration/test/ParseObjectTest.js
@@ -546,24 +546,20 @@ describe('Parse Object', () => {
});
});
- it('cannot create invalid key names', done => {
+ it('cannot create invalid key names', async () => {
+ const error = new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid key name: "foo^bar"`);
const item = new Parse.Object('Item');
- assert(!item.set({ 'foo^bar': 'baz' }));
- item.save({ 'foo^bar': 'baz' }).catch(e => {
- assert.equal(e.code, Parse.Error.INVALID_KEY_NAME);
- done();
- });
+ expect(() => {
+ item.set({ 'foo^bar': 'baz' });
+ }).toThrow(error);
+ await expectAsync(item.save({ 'foo^bar': 'baz' })).toBeRejectedWith(error);
});
it('cannot use invalid key names in multiple sets', () => {
const item = new Parse.Object('Item');
- assert(
- !item.set({
- foobar: 'baz',
- 'foo^bar': 'baz',
- })
- );
- assert(!item.get('foobar'));
+ expect(() => {
+ item.set({ foobar: 'baz', 'foo^bar': 'baz' });
+ }).toThrow(new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid key name: "foo^bar"`));
});
it('can unset fields', done => {
@@ -1135,12 +1131,43 @@ describe('Parse Object', () => {
parent.set('children', [child1, child2]);
parent.set('bastard', child3);
- expect(parent.save).toThrow();
- let results = await new Parse.Query(Child).find();
- assert.equal(results.length, 0);
-
await parent.save(null, { cascadeSave: true });
- results = await new Parse.Query(Child).find();
+ const results = await new Parse.Query(Child).find();
+ assert.equal(results.length, 3);
+
+ parent.set('dead', true);
+ child1.set('dead', true);
+ await parent.save(null);
+ const rob = await new Parse.Query(Child).equalTo('name', 'rob').first();
+ expect(rob.get('dead')).toBe(true);
+
+ parent.set('lastname', 'stark');
+ child3.set('lastname', 'stark');
+ await parent.save(null, { cascadeSave: false });
+ const john = await new Parse.Query(Child).doesNotExist('lastname').first();
+ expect(john.get('lastname')).toBeUndefined();
+ });
+
+ it('can skip cascade (default true) saving as per request', async () => {
+ const Parent = Parse.Object.extend('Parent');
+ const Child = Parse.Object.extend('Child');
+
+ const parent = new Parent();
+ const child1 = new Child();
+ const child2 = new Child();
+ const child3 = new Child();
+
+ child1.set('name', 'rob');
+ child2.set('name', 'sansa');
+ child3.set('name', 'john');
+ parent.set('children', [child1, child2]);
+ parent.set('bastard', child3);
+
+ // cascadeSave option default true
+ await parent.save(null, {
+ /* cascadeSave: true */
+ });
+ const results = await new Parse.Query(Child).find();
assert.equal(results.length, 3);
parent.set('dead', true);
diff --git a/src/ParseInstallation.ts b/src/ParseInstallation.ts
index fa69e2929..465d59175 100644
--- a/src/ParseInstallation.ts
+++ b/src/ParseInstallation.ts
@@ -40,7 +40,9 @@ class ParseInstallation extends ParseObject {
constructor(attributes?: AttributeMap) {
super('_Installation');
if (attributes && typeof attributes === 'object') {
- if (!this.set(attributes)) {
+ try {
+ this.set(attributes || {});
+ } catch (_) {
throw new Error("Can't create an invalid Installation");
}
}
diff --git a/src/ParseObject.ts b/src/ParseObject.ts
index bd3d6f81e..c53930757 100644
--- a/src/ParseObject.ts
+++ b/src/ParseObject.ts
@@ -136,8 +136,12 @@ class ParseObject {
options = attributes as any;
}
}
- if (toSet && !this.set(toSet, options)) {
- throw new Error("Can't create an invalid Parse Object");
+ if (toSet) {
+ try {
+ this.set(toSet, options);
+ } catch (_) {
+ throw new Error("Can't create an invalid Parse Object");
+ }
}
}
@@ -733,9 +737,9 @@ class ParseObject {
* @param {(string|object)} value The value to give it.
* @param {object} options A set of options for the set.
* The only supported option is error
.
- * @returns {(ParseObject|boolean)} true if the set succeeded.
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- set(key: any, value?: any, options?: any): ParseObject | boolean {
+ set(key: any, value?: any, options?: any): this {
let changes = {};
const newOps = {};
if (key && typeof key === 'object') {
@@ -804,12 +808,9 @@ class ParseObject {
// Validate changes
if (!options.ignoreValidation) {
- const validation = this.validate(newValues);
- if (validation) {
- if (typeof options.error === 'function') {
- options.error(this, validation);
- }
- return false;
+ const validationError = this.validate(newValues);
+ if (validationError) {
+ throw validationError;
}
}
@@ -831,9 +832,9 @@ class ParseObject {
*
* @param {string} attr The string name of an attribute.
* @param options
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- unset(attr: string, options?: { [opt: string]: any }): ParseObject | boolean {
+ unset(attr: string, options?: { [opt: string]: any }): this {
options = options || {};
options.unset = true;
return this.set(attr, null, options);
@@ -845,9 +846,9 @@ class ParseObject {
*
* @param attr {String} The key.
* @param amount {Number} The amount to increment by (optional).
- * @returns {(ParseObject|boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- increment(attr: string, amount?: number): ParseObject | boolean {
+ increment(attr: string, amount?: number): this {
if (typeof amount === 'undefined') {
amount = 1;
}
@@ -863,9 +864,9 @@ class ParseObject {
*
* @param attr {String} The key.
* @param amount {Number} The amount to decrement by (optional).
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- decrement(attr: string, amount?: number): ParseObject | boolean {
+ decrement(attr: string, amount?: number): this {
if (typeof amount === 'undefined') {
amount = 1;
}
@@ -881,9 +882,9 @@ class ParseObject {
*
* @param attr {String} The key.
* @param item {} The item to add.
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- add(attr: string, item: any): ParseObject | boolean {
+ add(attr: string, item: any): this {
return this.set(attr, new AddOp([item]));
}
@@ -893,9 +894,9 @@ class ParseObject {
*
* @param attr {String} The key.
* @param items {Object[]} The items to add.
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- addAll(attr: string, items: Array): ParseObject | boolean {
+ addAll(attr: string, items: Array): this {
return this.set(attr, new AddOp(items));
}
@@ -906,9 +907,9 @@ class ParseObject {
*
* @param attr {String} The key.
* @param item {} The object to add.
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- addUnique(attr: string, item: any): ParseObject | boolean {
+ addUnique(attr: string, item: any): this {
return this.set(attr, new AddUniqueOp([item]));
}
@@ -919,9 +920,9 @@ class ParseObject {
*
* @param attr {String} The key.
* @param items {Object[]} The objects to add.
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- addAllUnique(attr: string, items: Array): ParseObject | boolean {
+ addAllUnique(attr: string, items: Array): this {
return this.set(attr, new AddUniqueOp(items));
}
@@ -931,9 +932,9 @@ class ParseObject {
*
* @param attr {String} The key.
* @param item {} The object to remove.
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- remove(attr: string, item: any): ParseObject | boolean {
+ remove(attr: string, item: any): this {
return this.set(attr, new RemoveOp([item]));
}
@@ -943,9 +944,9 @@ class ParseObject {
*
* @param attr {String} The key.
* @param items {Object[]} The object to remove.
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- removeAll(attr: string, items: Array): ParseObject | boolean {
+ removeAll(attr: string, items: Array): this {
return this.set(attr, new RemoveOp(items));
}
@@ -1099,7 +1100,7 @@ class ParseObject {
}
for (const key in attrs) {
if (!/^[A-Za-z][0-9A-Za-z_.]*$/.test(key)) {
- return new ParseError(ParseError.INVALID_KEY_NAME);
+ return new ParseError(ParseError.INVALID_KEY_NAME, `Invalid key name: "${key}"`);
}
}
return false;
@@ -1124,10 +1125,10 @@ class ParseObject {
*
* @param {Parse.ACL} acl An instance of Parse.ACL.
* @param {object} options
- * @returns {(ParseObject | boolean)} Whether the set passed validation.
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
* @see Parse.Object#set
*/
- setACL(acl: ParseACL, options?: any): ParseObject | boolean {
+ setACL(acl: ParseACL, options?: any): this {
return this.set('ACL', acl, options);
}
@@ -1154,9 +1155,9 @@ class ParseObject {
/**
* Clears all attributes on a model
*
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- clear(): ParseObject | boolean {
+ clear(): this {
const attributes = this.attributes;
const erasable = {};
let readonly = ['createdAt', 'updatedAt'];
@@ -1320,7 +1321,7 @@ class ParseObject {
* @returns {Promise} A promise that is fulfilled when the save
* completes.
*/
- save(
+ async save(
arg1: undefined | string | { [attr: string]: any } | null,
arg2: SaveOptions | any,
arg3?: SaveOptions
@@ -1337,17 +1338,9 @@ class ParseObject {
attrs[arg1] = arg2;
options = arg3;
}
-
options = options || {};
if (attrs) {
- let validationError;
- options.error = (_, validation) => {
- validationError = validation;
- };
- const success = this.set(attrs, options);
- if (!success) {
- return Promise.reject(validationError);
- }
+ this.set(attrs, options);
}
const saveOptions = ParseObject._getRequestOptions(options);
const controller = CoreManager.getObjectController();
@@ -1985,7 +1978,9 @@ class ParseObject {
}
if (attributes && typeof attributes === 'object') {
- if (!this.set(attributes || {}, options)) {
+ try {
+ this.set(attributes || {}, options);
+ } catch (_) {
throw new Error("Can't create an invalid Parse Object");
}
}
diff --git a/src/ParseSession.ts b/src/ParseSession.ts
index b20cb4f12..e783a305b 100644
--- a/src/ParseSession.ts
+++ b/src/ParseSession.ts
@@ -20,7 +20,9 @@ class ParseSession extends ParseObject {
constructor(attributes?: any) {
super('_Session');
if (attributes && typeof attributes === 'object') {
- if (!this.set(attributes || {})) {
+ try {
+ this.set(attributes || {});
+ } catch (_) {
throw new Error("Can't create an invalid Session");
}
}
diff --git a/src/ParseUser.ts b/src/ParseUser.ts
index 2cc4287a0..47be73de2 100644
--- a/src/ParseUser.ts
+++ b/src/ParseUser.ts
@@ -41,7 +41,9 @@ class ParseUser extends ParseObject {
constructor(attributes?: AttributeMap) {
super('_User');
if (attributes && typeof attributes === 'object') {
- if (!this.set(attributes || {})) {
+ try {
+ this.set(attributes || {});
+ } catch (_) {
throw new Error("Can't create an invalid Parse User");
}
}
diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js
index 36453bb1e..565b57c96 100644
--- a/src/__tests__/ParseObject-test.js
+++ b/src/__tests__/ParseObject-test.js
@@ -965,7 +965,7 @@ describe('ParseObject', () => {
o.validate({
'invalid!key': 12,
})
- ).toEqual(new ParseError(ParseError.INVALID_KEY_NAME));
+ ).toEqual(new ParseError(ParseError.INVALID_KEY_NAME, `Invalid key name: "invalid!key"`));
expect(
o.validate({
@@ -982,16 +982,13 @@ describe('ParseObject', () => {
it('validates attributes on set()', () => {
const o = new ParseObject('Listing');
- expect(o.set('ACL', 'not an acl')).toBe(false);
+ expect(() => {
+ o.set('ACL', 'not an acl');
+ }).toThrow(new ParseError(ParseError.OTHER_CAUSE, 'ACL must be a Parse ACL.'));
expect(o.set('ACL', { '*': { read: true, write: false } })).toBe(o);
- expect(o.set('$$$', 'o_O')).toBe(false);
-
- o.set('$$$', 'o_O', {
- error: function (obj, err) {
- expect(obj).toBe(o);
- expect(err.code).toBe(105);
- },
- });
+ expect(() => {
+ o.set('$$$', 'o_O');
+ }).toThrow(new ParseError(ParseError.INVALID_KEY_NAME, `Invalid key name: "$$$"`));
});
it('ignores validation if ignoreValidation option is passed to set()', () => {
@@ -1947,22 +1944,25 @@ describe('ParseObject', () => {
await result;
});
- it('will fail for a circular dependency of non-existing objects', () => {
+ it('will fail for a circular dependency of non-existing objects', async () => {
const parent = new ParseObject('Item');
const child = new ParseObject('Item');
parent.set('child', child);
child.set('parent', parent);
- expect(parent.save.bind(parent)).toThrow('Cannot create a pointer to an unsaved Object.');
+ await expect(parent.save()).rejects.toThrowError(
+ 'Cannot create a pointer to an unsaved Object.'
+ );
});
- it('will fail for deeper unsaved objects', () => {
+ it('will fail for deeper unsaved objects', async () => {
const parent = new ParseObject('Item');
const child = new ParseObject('Item');
const grandchild = new ParseObject('Item');
parent.set('child', child);
child.set('child', grandchild);
-
- expect(parent.save.bind(parent)).toThrow('Cannot create a pointer to an unsaved Object.');
+ await expect(parent.save()).rejects.toThrowError(
+ 'Cannot create a pointer to an unsaved Object.'
+ );
});
it('does not mark shallow objects as dirty', () => {
diff --git a/types/ParseObject.d.ts b/types/ParseObject.d.ts
index e51b18858..7b49bfce2 100644
--- a/types/ParseObject.d.ts
+++ b/types/ParseObject.d.ts
@@ -241,59 +241,59 @@ declare class ParseObject {
* @param {(string|object)} value The value to give it.
* @param {object} options A set of options for the set.
* The only supported option is error
.
- * @returns {(ParseObject|boolean)} true if the set succeeded.
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- set(key: any, value?: any, options?: any): ParseObject | boolean;
+ set(key: any, value?: any, options?: any): this;
/**
* Remove an attribute from the model. This is a noop if the attribute doesn't
* exist.
*
* @param {string} attr The string name of an attribute.
* @param options
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
unset(
attr: string,
options?: {
[opt: string]: any;
}
- ): ParseObject | boolean;
+ ): this;
/**
* Atomically increments the value of the given attribute the next time the
* object is saved. If no amount is specified, 1 is used by default.
*
* @param attr {String} The key.
* @param amount {Number} The amount to increment by (optional).
- * @returns {(ParseObject|boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- increment(attr: string, amount?: number): ParseObject | boolean;
+ increment(attr: string, amount?: number): this;
/**
* Atomically decrements the value of the given attribute the next time the
* object is saved. If no amount is specified, 1 is used by default.
*
* @param attr {String} The key.
* @param amount {Number} The amount to decrement by (optional).
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- decrement(attr: string, amount?: number): ParseObject | boolean;
+ decrement(attr: string, amount?: number): this;
/**
* Atomically add an object to the end of the array associated with a given
* key.
*
* @param attr {String} The key.
* @param item {} The item to add.
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- add(attr: string, item: any): ParseObject | boolean;
+ add(attr: string, item: any): this;
/**
* Atomically add the objects to the end of the array associated with a given
* key.
*
* @param attr {String} The key.
* @param items {Object[]} The items to add.
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- addAll(attr: string, items: Array): ParseObject | boolean;
+ addAll(attr: string, items: Array): this;
/**
* Atomically add an object to the array associated with a given key, only
* if it is not already present in the array. The position of the insert is
@@ -301,9 +301,9 @@ declare class ParseObject {
*
* @param attr {String} The key.
* @param item {} The object to add.
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- addUnique(attr: string, item: any): ParseObject | boolean;
+ addUnique(attr: string, item: any): this;
/**
* Atomically add the objects to the array associated with a given key, only
* if it is not already present in the array. The position of the insert is
@@ -311,27 +311,27 @@ declare class ParseObject {
*
* @param attr {String} The key.
* @param items {Object[]} The objects to add.
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- addAllUnique(attr: string, items: Array): ParseObject | boolean;
+ addAllUnique(attr: string, items: Array): this;
/**
* Atomically remove all instances of an object from the array associated
* with a given key.
*
* @param attr {String} The key.
* @param item {} The object to remove.
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- remove(attr: string, item: any): ParseObject | boolean;
+ remove(attr: string, item: any): this;
/**
* Atomically remove all instances of the objects from the array associated
* with a given key.
*
* @param attr {String} The key.
* @param items {Object[]} The object to remove.
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- removeAll(attr: string, items: Array): ParseObject | boolean;
+ removeAll(attr: string, items: Array): this;
/**
* Returns an instance of a subclass of Parse.Op describing what kind of
* modification has been performed on this field since the last time it was
@@ -410,10 +410,10 @@ declare class ParseObject {
*
* @param {Parse.ACL} acl An instance of Parse.ACL.
* @param {object} options
- * @returns {(ParseObject | boolean)} Whether the set passed validation.
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
* @see Parse.Object#set
*/
- setACL(acl: ParseACL, options?: any): ParseObject | boolean;
+ setACL(acl: ParseACL, options?: any): this;
/**
* Clears any (or specific) changes to this object made since the last call to save()
*
@@ -423,9 +423,9 @@ declare class ParseObject {
/**
* Clears all attributes on a model
*
- * @returns {(ParseObject | boolean)}
+ * @returns {Parse.Object} Returns the object, so you can chain this call.
*/
- clear(): ParseObject | boolean;
+ clear(): this;
/**
* Fetch the model from the server. If the server's representation of the
* model differs from its current attributes, they will be overriden.
diff --git a/types/ParseUser.d.ts b/types/ParseUser.d.ts
index 9a88c2131..98295d105 100644
--- a/types/ParseUser.d.ts
+++ b/types/ParseUser.d.ts
@@ -171,7 +171,7 @@ declare class ParseUser extends ParseObject {
* @param {string} email
* @returns {boolean}
*/
- setEmail(email: string): boolean | ParseObject;
+ setEmail(email: string): this;
/**
* Returns the session token for this user, if the user has been logged in,
* or if it is the result of a query with the master key. Otherwise, returns