Skip to content

Commit 9e87b04

Browse files
authored
feat(Object): add batch error.results (#516)
1 parent 1ee8e68 commit 9e87b04

File tree

3 files changed

+95
-40
lines changed

3 files changed

+95
-40
lines changed

src/object.js

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ const checkReservedKey = key => {
1616
}
1717
};
1818

19+
const handleBatchResults = (results) => {
20+
const firstError = _.find(results, result => result instanceof Error);
21+
if (!firstError) {
22+
return results;
23+
}
24+
const error = new AVError(firstError.code, firstError.message);
25+
error.results = results;
26+
throw error;
27+
};
28+
1929
// Helper function to get a value from a Backbone object as a property
2030
// or as a function.
2131
function getValue(object, prop) {
@@ -139,17 +149,17 @@ module.exports = function(AV) {
139149
}),
140150
}, options)
141151
).then(function(response) {
142-
_.forEach(objects, function(object, i) {
152+
const results = _.map(objects, function(object, i) {
143153
if (response[i].success) {
144-
object._finishFetch(
145-
object.parse(response[i].success));
146-
} else {
147-
const error = new Error(response[i].error.error);
148-
error.code = response[i].error.code;
149-
throw error;
154+
object._finishFetch(object.parse(response[i].success));
155+
return object;
156+
}
157+
if (response[i].success === null) {
158+
return new AVError(AVError.OBJECT_NOT_FOUND, 'Object not found.');
150159
}
160+
return new AVError(response[i].error.code, response[i].error.error);
151161
});
152-
return objects;
162+
return handleBatchResults(results);
153163
});
154164

155165
// Attach all inheritable methods to the AV.Object prototype.
@@ -1309,21 +1319,26 @@ module.exports = function(AV) {
13091319
if (!objects || objects.length === 0){
13101320
return AV.Promise.resolve();
13111321
}
1312-
const objectsByClassNameAndFlags = _.groupBy(objects, object => JSON.stringify({
1313-
className: object.className,
1314-
flags: object._flags
1315-
}));
13161322
const body = {
1317-
requests: _.map(objectsByClassNameAndFlags, objects => {
1318-
const ids = _.map(objects, 'id').join(',');
1323+
requests: _.map(objects, object => {
1324+
if (!object.className) throw new Error('object must have className to destroy');
1325+
if (!object.id) throw new Error('object must have id to destroy');
13191326
return {
13201327
method: 'DELETE',
1321-
path: `/1.1/classes/${objects[0].className}/${ids}`,
1322-
body: objects[0]._flags
1323-
}
1324-
})
1328+
path: `/1.1/classes/${object.className}/${object.id}`,
1329+
body: object._flags
1330+
};
1331+
}),
13251332
};
1326-
return _request('batch', null, null, 'POST', body, options);
1333+
return _request('batch', null, null, 'POST', body, options).then(function(response) {
1334+
const results = _.map(objects, function(object, i) {
1335+
if (response[i].success) {
1336+
return null;
1337+
}
1338+
return new AVError(response[i].error.code, response[i].error.error);
1339+
});
1340+
return handleBatchResults(results);
1341+
});
13271342
};
13281343

13291344
/**
@@ -1627,21 +1642,15 @@ module.exports = function(AV) {
16271642
})
16281643

16291644
}, options).then(function(response) {
1630-
var error;
1631-
AV._arrayEach(batch, function(object, i) {
1645+
const results = _.map(batch, function(object, i) {
16321646
if (response[i].success) {
1633-
object._finishSave(
1634-
object.parse(response[i].success));
1635-
} else {
1636-
error = error || response[i].error;
1637-
object._cancelSave();
1647+
object._finishSave(object.parse(response[i].success));
1648+
return object;
16381649
}
1650+
object._cancelSave();
1651+
return new AVError(response[i].error.code, response[i].error.error);
16391652
});
1640-
if (error) {
1641-
return AV.Promise.reject(
1642-
new AVError(error.code, error.error));
1643-
}
1644-
1653+
return handleBatchResults(results);
16451654
})
16461655
);
16471656
AV._arrayEach(batch, function(object) {

test/hooks.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,11 @@ describe('hooks', function() {
187187
return object.save();
188188
})).then(function() {
189189
return AV.Object.destroyAll([object, objectIgnoreBefore]);
190-
}).then(function(result) {
191-
expect(result[0].error).to.be.ok();
192-
expect(result[1].error).to.not.be.ok();
193-
});
190+
}).catch(error => {
191+
error.results.should.be.length(2);
192+
error.results[0].should.be.instanceof(Error);
193+
expect(error.results[1]).to.equal(null);
194+
throw new Error('handled error');
195+
}).should.be.rejectedWith('handled error');
194196
});
195197
});

test/object.js

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -389,11 +389,19 @@ describe('Objects', function(){
389389
expect(score2.id).to.be.eql(gameScore.id);
390390
})
391391
);
392-
it('fetchAll with non-existed Class should fail', () =>
392+
it('fetchAll with non-existed Class/object should fail', () =>
393393
AV.Object.fetchAll([
394394
AV.Object.createWithoutData('GameScore', gameScore.id),
395+
AV.Object.createWithoutData('GameScore', 'fakeId'),
395396
AV.Object.createWithoutData('FakeClass', gameScore.id),
396-
]).should.be.rejected()
397+
]).catch(error => {
398+
error.message.should.eql('Object not found.');
399+
error.results.should.be.length(3);
400+
error.results[0].should.be.instanceof(GameScore);
401+
error.results[1].should.be.instanceof(Error);
402+
error.results[2].should.be.instanceof(Error);
403+
throw new Error('handled error');
404+
}).should.be.rejectedWith('handled error')
397405
);
398406
it('fetchAll with dirty objet should fail', () =>
399407
AV.Object.fetchAll([
@@ -404,9 +412,28 @@ describe('Objects', function(){
404412
});
405413

406414
describe("Deleting Objects",function(){
407-
it("should delete cheatMode",function(){
408-
gameScore.unset("cheatMode");
409-
return gameScore.save();
415+
it("should delete object",function(){
416+
return new GameScore().save().then(gameScore =>
417+
gameScore.destroy().then(() =>
418+
GameScore.query.get(gameScore.id)
419+
.should.be.rejectedWith('Object not found.')
420+
)
421+
)
422+
});
423+
it("batch delete",function(){
424+
const acl = new AV.ACL();
425+
acl.setPublicWriteAccess(false);
426+
return new GameScore({ACL: acl}).save().then(gameScore =>
427+
AV.Object.destroyAll([
428+
AV.Object.createWithoutData('GameScore', 'fakeId'),
429+
gameScore,
430+
]).catch(error => {
431+
error.results.should.be.length(2);
432+
const [_, err] = error.results;
433+
err.should.be.instanceof(Error);
434+
throw new Error('handled error');
435+
}).should.be.rejectedWith('handled error')
436+
);
410437
});
411438
});
412439

@@ -525,6 +552,23 @@ describe('Objects', function(){
525552
});
526553
});
527554

555+
it("should save all partially", function(){
556+
var Person = AV.Object.extend("Person");
557+
return AV.Object.saveAll([
558+
AV.Object.createWithoutData('Person', 'fakeid').set('age', 30),
559+
new Person({
560+
age: 40,
561+
}),
562+
]).catch(error => {
563+
error.results.should.be.length(2);
564+
const [err, person] = error.results;
565+
err.should.be.instanceof(Error);
566+
person.should.be.instanceof(Person);
567+
person.id.should.be.ok();
568+
person.get('age').should.eql(40);
569+
});
570+
});
571+
528572
it("should query relational data",function(){
529573
var Person=AV.Object.extend("Person");
530574
var query=new AV.Query(Person);

0 commit comments

Comments
 (0)