Skip to content

Commit 52ddaee

Browse files
authored
fix: Remove validation error handler option error from various methods of Parse.Object (#2445)
BREAKING CHANGE: Removes the error handler option `error` from `Parse.Object.set`, `Parse.Object.setACL`, `Parse.Object.unset`, `Parse.Role.setName` and instead throws on validation error. Previously, if the `error` option was set, the handler was invoked if a validation error occurred on `Parse.Object.set`, and if no handler was set, an error was thrown on `Parse.Object.save`. The new behavior is that an error is thrown at `Parse.Object.set`. For example, instead of using `Parse.Object.set(key, value, { error: ... })` wrap `Parse.Object.set(key, value)` into a `try/catch` block.
1 parent fa34827 commit 52ddaee

File tree

9 files changed

+136
-106
lines changed

9 files changed

+136
-106
lines changed

integration/test/ParseACLTest.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ describe('Parse.ACL', () => {
1010

1111
it('acl must be valid', () => {
1212
const user = new Parse.User();
13-
assert.equal(user.setACL(`Ceci n'est pas un ACL.`), false);
13+
expect(() => user.setACL(`Ceci n'est pas un ACL.`)).toThrow(
14+
new Parse.Error(Parse.Error.OTHER_CAUSE, 'ACL must be a Parse ACL.')
15+
);
1416
});
1517

1618
it('can refresh object with acl', async () => {

integration/test/ParseObjectTest.js

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -546,24 +546,20 @@ describe('Parse Object', () => {
546546
});
547547
});
548548

549-
it('cannot create invalid key names', done => {
549+
it('cannot create invalid key names', async () => {
550+
const error = new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid key name: "foo^bar"`);
550551
const item = new Parse.Object('Item');
551-
assert(!item.set({ 'foo^bar': 'baz' }));
552-
item.save({ 'foo^bar': 'baz' }).catch(e => {
553-
assert.equal(e.code, Parse.Error.INVALID_KEY_NAME);
554-
done();
555-
});
552+
expect(() => {
553+
item.set({ 'foo^bar': 'baz' });
554+
}).toThrow(error);
555+
await expectAsync(item.save({ 'foo^bar': 'baz' })).toBeRejectedWith(error);
556556
});
557557

558558
it('cannot use invalid key names in multiple sets', () => {
559559
const item = new Parse.Object('Item');
560-
assert(
561-
!item.set({
562-
foobar: 'baz',
563-
'foo^bar': 'baz',
564-
})
565-
);
566-
assert(!item.get('foobar'));
560+
expect(() => {
561+
item.set({ foobar: 'baz', 'foo^bar': 'baz' });
562+
}).toThrow(new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid key name: "foo^bar"`));
567563
});
568564

569565
it('can unset fields', done => {
@@ -1135,12 +1131,43 @@ describe('Parse Object', () => {
11351131
parent.set('children', [child1, child2]);
11361132
parent.set('bastard', child3);
11371133

1138-
expect(parent.save).toThrow();
1139-
let results = await new Parse.Query(Child).find();
1140-
assert.equal(results.length, 0);
1141-
11421134
await parent.save(null, { cascadeSave: true });
1143-
results = await new Parse.Query(Child).find();
1135+
const results = await new Parse.Query(Child).find();
1136+
assert.equal(results.length, 3);
1137+
1138+
parent.set('dead', true);
1139+
child1.set('dead', true);
1140+
await parent.save(null);
1141+
const rob = await new Parse.Query(Child).equalTo('name', 'rob').first();
1142+
expect(rob.get('dead')).toBe(true);
1143+
1144+
parent.set('lastname', 'stark');
1145+
child3.set('lastname', 'stark');
1146+
await parent.save(null, { cascadeSave: false });
1147+
const john = await new Parse.Query(Child).doesNotExist('lastname').first();
1148+
expect(john.get('lastname')).toBeUndefined();
1149+
});
1150+
1151+
it('can skip cascade (default true) saving as per request', async () => {
1152+
const Parent = Parse.Object.extend('Parent');
1153+
const Child = Parse.Object.extend('Child');
1154+
1155+
const parent = new Parent();
1156+
const child1 = new Child();
1157+
const child2 = new Child();
1158+
const child3 = new Child();
1159+
1160+
child1.set('name', 'rob');
1161+
child2.set('name', 'sansa');
1162+
child3.set('name', 'john');
1163+
parent.set('children', [child1, child2]);
1164+
parent.set('bastard', child3);
1165+
1166+
// cascadeSave option default true
1167+
await parent.save(null, {
1168+
/* cascadeSave: true */
1169+
});
1170+
const results = await new Parse.Query(Child).find();
11441171
assert.equal(results.length, 3);
11451172

11461173
parent.set('dead', true);

src/ParseInstallation.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ class ParseInstallation extends ParseObject {
4040
constructor(attributes?: AttributeMap) {
4141
super('_Installation');
4242
if (attributes && typeof attributes === 'object') {
43-
if (!this.set(attributes)) {
43+
try {
44+
this.set(attributes || {});
45+
} catch (_) {
4446
throw new Error("Can't create an invalid Installation");
4547
}
4648
}

src/ParseObject.ts

Lines changed: 39 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,12 @@ class ParseObject {
136136
options = attributes as any;
137137
}
138138
}
139-
if (toSet && !this.set(toSet, options)) {
140-
throw new Error("Can't create an invalid Parse Object");
139+
if (toSet) {
140+
try {
141+
this.set(toSet, options);
142+
} catch (_) {
143+
throw new Error("Can't create an invalid Parse Object");
144+
}
141145
}
142146
}
143147

@@ -733,9 +737,9 @@ class ParseObject {
733737
* @param {(string|object)} value The value to give it.
734738
* @param {object} options A set of options for the set.
735739
* The only supported option is <code>error</code>.
736-
* @returns {(ParseObject|boolean)} true if the set succeeded.
740+
* @returns {Parse.Object} Returns the object, so you can chain this call.
737741
*/
738-
set(key: any, value?: any, options?: any): ParseObject | boolean {
742+
set(key: any, value?: any, options?: any): this {
739743
let changes = {};
740744
const newOps = {};
741745
if (key && typeof key === 'object') {
@@ -804,12 +808,9 @@ class ParseObject {
804808

805809
// Validate changes
806810
if (!options.ignoreValidation) {
807-
const validation = this.validate(newValues);
808-
if (validation) {
809-
if (typeof options.error === 'function') {
810-
options.error(this, validation);
811-
}
812-
return false;
811+
const validationError = this.validate(newValues);
812+
if (validationError) {
813+
throw validationError;
813814
}
814815
}
815816

@@ -831,9 +832,9 @@ class ParseObject {
831832
*
832833
* @param {string} attr The string name of an attribute.
833834
* @param options
834-
* @returns {(ParseObject | boolean)}
835+
* @returns {Parse.Object} Returns the object, so you can chain this call.
835836
*/
836-
unset(attr: string, options?: { [opt: string]: any }): ParseObject | boolean {
837+
unset(attr: string, options?: { [opt: string]: any }): this {
837838
options = options || {};
838839
options.unset = true;
839840
return this.set(attr, null, options);
@@ -845,9 +846,9 @@ class ParseObject {
845846
*
846847
* @param attr {String} The key.
847848
* @param amount {Number} The amount to increment by (optional).
848-
* @returns {(ParseObject|boolean)}
849+
* @returns {Parse.Object} Returns the object, so you can chain this call.
849850
*/
850-
increment(attr: string, amount?: number): ParseObject | boolean {
851+
increment(attr: string, amount?: number): this {
851852
if (typeof amount === 'undefined') {
852853
amount = 1;
853854
}
@@ -863,9 +864,9 @@ class ParseObject {
863864
*
864865
* @param attr {String} The key.
865866
* @param amount {Number} The amount to decrement by (optional).
866-
* @returns {(ParseObject | boolean)}
867+
* @returns {Parse.Object} Returns the object, so you can chain this call.
867868
*/
868-
decrement(attr: string, amount?: number): ParseObject | boolean {
869+
decrement(attr: string, amount?: number): this {
869870
if (typeof amount === 'undefined') {
870871
amount = 1;
871872
}
@@ -881,9 +882,9 @@ class ParseObject {
881882
*
882883
* @param attr {String} The key.
883884
* @param item {} The item to add.
884-
* @returns {(ParseObject | boolean)}
885+
* @returns {Parse.Object} Returns the object, so you can chain this call.
885886
*/
886-
add(attr: string, item: any): ParseObject | boolean {
887+
add(attr: string, item: any): this {
887888
return this.set(attr, new AddOp([item]));
888889
}
889890

@@ -893,9 +894,9 @@ class ParseObject {
893894
*
894895
* @param attr {String} The key.
895896
* @param items {Object[]} The items to add.
896-
* @returns {(ParseObject | boolean)}
897+
* @returns {Parse.Object} Returns the object, so you can chain this call.
897898
*/
898-
addAll(attr: string, items: Array<any>): ParseObject | boolean {
899+
addAll(attr: string, items: Array<any>): this {
899900
return this.set(attr, new AddOp(items));
900901
}
901902

@@ -906,9 +907,9 @@ class ParseObject {
906907
*
907908
* @param attr {String} The key.
908909
* @param item {} The object to add.
909-
* @returns {(ParseObject | boolean)}
910+
* @returns {Parse.Object} Returns the object, so you can chain this call.
910911
*/
911-
addUnique(attr: string, item: any): ParseObject | boolean {
912+
addUnique(attr: string, item: any): this {
912913
return this.set(attr, new AddUniqueOp([item]));
913914
}
914915

@@ -919,9 +920,9 @@ class ParseObject {
919920
*
920921
* @param attr {String} The key.
921922
* @param items {Object[]} The objects to add.
922-
* @returns {(ParseObject | boolean)}
923+
* @returns {Parse.Object} Returns the object, so you can chain this call.
923924
*/
924-
addAllUnique(attr: string, items: Array<any>): ParseObject | boolean {
925+
addAllUnique(attr: string, items: Array<any>): this {
925926
return this.set(attr, new AddUniqueOp(items));
926927
}
927928

@@ -931,9 +932,9 @@ class ParseObject {
931932
*
932933
* @param attr {String} The key.
933934
* @param item {} The object to remove.
934-
* @returns {(ParseObject | boolean)}
935+
* @returns {Parse.Object} Returns the object, so you can chain this call.
935936
*/
936-
remove(attr: string, item: any): ParseObject | boolean {
937+
remove(attr: string, item: any): this {
937938
return this.set(attr, new RemoveOp([item]));
938939
}
939940

@@ -943,9 +944,9 @@ class ParseObject {
943944
*
944945
* @param attr {String} The key.
945946
* @param items {Object[]} The object to remove.
946-
* @returns {(ParseObject | boolean)}
947+
* @returns {Parse.Object} Returns the object, so you can chain this call.
947948
*/
948-
removeAll(attr: string, items: Array<any>): ParseObject | boolean {
949+
removeAll(attr: string, items: Array<any>): this {
949950
return this.set(attr, new RemoveOp(items));
950951
}
951952

@@ -1099,7 +1100,7 @@ class ParseObject {
10991100
}
11001101
for (const key in attrs) {
11011102
if (!/^[A-Za-z][0-9A-Za-z_.]*$/.test(key)) {
1102-
return new ParseError(ParseError.INVALID_KEY_NAME);
1103+
return new ParseError(ParseError.INVALID_KEY_NAME, `Invalid key name: "${key}"`);
11031104
}
11041105
}
11051106
return false;
@@ -1124,10 +1125,10 @@ class ParseObject {
11241125
*
11251126
* @param {Parse.ACL} acl An instance of Parse.ACL.
11261127
* @param {object} options
1127-
* @returns {(ParseObject | boolean)} Whether the set passed validation.
1128+
* @returns {Parse.Object} Returns the object, so you can chain this call.
11281129
* @see Parse.Object#set
11291130
*/
1130-
setACL(acl: ParseACL, options?: any): ParseObject | boolean {
1131+
setACL(acl: ParseACL, options?: any): this {
11311132
return this.set('ACL', acl, options);
11321133
}
11331134

@@ -1154,9 +1155,9 @@ class ParseObject {
11541155
/**
11551156
* Clears all attributes on a model
11561157
*
1157-
* @returns {(ParseObject | boolean)}
1158+
* @returns {Parse.Object} Returns the object, so you can chain this call.
11581159
*/
1159-
clear(): ParseObject | boolean {
1160+
clear(): this {
11601161
const attributes = this.attributes;
11611162
const erasable = {};
11621163
let readonly = ['createdAt', 'updatedAt'];
@@ -1320,7 +1321,7 @@ class ParseObject {
13201321
* @returns {Promise} A promise that is fulfilled when the save
13211322
* completes.
13221323
*/
1323-
save(
1324+
async save(
13241325
arg1: undefined | string | { [attr: string]: any } | null,
13251326
arg2: SaveOptions | any,
13261327
arg3?: SaveOptions
@@ -1337,17 +1338,9 @@ class ParseObject {
13371338
attrs[arg1] = arg2;
13381339
options = arg3;
13391340
}
1340-
13411341
options = options || {};
13421342
if (attrs) {
1343-
let validationError;
1344-
options.error = (_, validation) => {
1345-
validationError = validation;
1346-
};
1347-
const success = this.set(attrs, options);
1348-
if (!success) {
1349-
return Promise.reject(validationError);
1350-
}
1343+
this.set(attrs, options);
13511344
}
13521345
const saveOptions = ParseObject._getRequestOptions(options);
13531346
const controller = CoreManager.getObjectController();
@@ -1985,7 +1978,9 @@ class ParseObject {
19851978
}
19861979

19871980
if (attributes && typeof attributes === 'object') {
1988-
if (!this.set(attributes || {}, options)) {
1981+
try {
1982+
this.set(attributes || {}, options);
1983+
} catch (_) {
19891984
throw new Error("Can't create an invalid Parse Object");
19901985
}
19911986
}

src/ParseSession.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ class ParseSession extends ParseObject {
2020
constructor(attributes?: any) {
2121
super('_Session');
2222
if (attributes && typeof attributes === 'object') {
23-
if (!this.set(attributes || {})) {
23+
try {
24+
this.set(attributes || {});
25+
} catch (_) {
2426
throw new Error("Can't create an invalid Session");
2527
}
2628
}

src/ParseUser.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ class ParseUser extends ParseObject {
4141
constructor(attributes?: AttributeMap) {
4242
super('_User');
4343
if (attributes && typeof attributes === 'object') {
44-
if (!this.set(attributes || {})) {
44+
try {
45+
this.set(attributes || {});
46+
} catch (_) {
4547
throw new Error("Can't create an invalid Parse User");
4648
}
4749
}

0 commit comments

Comments
 (0)