Skip to content

Commit

Permalink
Add add/remove methods for working with collections
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle Morse committed Dec 17, 2014
1 parent f48b641 commit 62301b8
Show file tree
Hide file tree
Showing 2 changed files with 254 additions and 0 deletions.
54 changes: 54 additions & 0 deletions lib/restapi.js
Expand Up @@ -267,4 +267,58 @@ RestApi.prototype.query = function(options, callback) {
return deferred.promise.nodeify(callback);
};

function collectionPost(options, operation, callback) {
var deferred = Q.defer();
this.request.post(_.merge({
url: refUtils.getRelative(options.ref) + '/' + options.collection + '/' + operation,
json: {CollectionItems: options.data}
}, options.requestOptions,
optionsToRequestOptions(options)), function(error, result) {
if (error) {
deferred.reject(error);
} else {
deferred.resolve(result);
}
});
return deferred.promise.nodeify(callback);
}

/**
Adds items to a collection
@param {object} options - The add options (required)
- @member {string} ref - The ref of the collection to update, e.g. /user/12345 (required)
- @member {string} collection - The name of the collection to update, e.g. 'TeamMemberships (required)
- @member {object} data - [{_ref: objectRef}, {Name:"Joe"}], things to be added to the collection (required)
- @member {string/string[]} fetch - the fields to include on the returned records
- @member {object} scope - the default scoping to use. if not specified server default will be used.
- @member {ref} scope.workspace - the workspace
- @member {object} requestOptions - Additional options to be applied to the request: https://github.com/mikeal/request (optional)
@param {function} callback - A callback to be called when the operation completes
- @param {string[]} errors - Any errors which occurred
- @param {object} result - the operation result
@return {promise}
*/
RestApi.prototype.add = function(options, callback) {
return collectionPost.call(this, options, 'add', callback);
};

/**
Remove items from a collection
@param {object} options - The remove options (required)
- @member {string} ref - The ref of the collection to update, e.g. /user/12345 (required)
- @member {string} collection - The name of the collection to update, e.g. 'TeamMemberships (required)
- @member {object} data - [{_ref: objectRef}], where the objectRefs are to be removed from the collection (required)
- @member {string/string[]} fetch - the fields to include on the returned records
- @member {object} scope - the default scoping to use. if not specified server default will be used.
- @member {ref} scope.workspace - the workspace
- @member {object} requestOptions - Additional options to be applied to the request: https://github.com/mikeal/request (optional)
@param {function} callback - A callback to be called when the operation completes
- @param {string[]} errors - Any errors which occurred
- @param {object} result - the operation result
@return {promise}
*/
RestApi.prototype.remove = function(options, callback) {
return collectionPost.call(this, options, 'remove', callback);
};

module.exports = RestApi;
200 changes: 200 additions & 0 deletions spec/restapi.spec.js
Expand Up @@ -573,5 +573,205 @@ describe('RestApi', function() {
}).done();
});
});

describe('add', function() {
it('translates request options', function() {
var restApi = new RestApi();
restApi.add({
ref: '/defect/1234',
data: [{_ref: '/defect/2345'}],
collection: 'Duplicates',
scope: {workspace: '/workspace/1234'},
fetch: ['FormattedID'],
requestOptions: {
qs: {foo: 'bar'}
}
});

this.post.callCount.should.eql(1);
var args = this.post.firstCall.args[0];
args.qs.workspace.should.eql('/workspace/1234');
args.qs.fetch.should.eql('FormattedID');
args.qs.foo.should.eql('bar');
});

it('generates correct post request', function() {
var restApi = new RestApi();
var callback = sinon.stub();
restApi.add({
ref: '/defect/1234',
data: [{_ref: '/defect/2345'}],
collection: 'Duplicates'
}, callback);

this.post.callCount.should.eql(1);
var args = this.post.firstCall.args;
args[0].url.should.eql('/defect/1234/Duplicates/add');
args[0].json.should.eql({CollectionItems: [{_ref: '/defect/2345'}]});
});

it('calls back with result', function(done) {
this.post.yieldsAsync(null, {Errors: [], Warnings: [], Results: [{_ref: '/defect/2345'}]});
var restApi = new RestApi();
restApi.add({
ref: '/defect/1234',
data: [{_ref: '/defect/2345'}],
collection: 'Duplicates'
}, function(error, result) {
should.not.exist(error);
result.Errors.should.eql([]);
result.Warnings.should.eql([]);
result.Results.should.eql([{_ref: '/defect/2345'}]);
done();
});
});

it('resolves promise with result', function(done) {
this.post.yieldsAsync(null, {Errors: [], Warnings: [], Results: [{_ref: '/defect/2345'}]});
var restApi = new RestApi();
var onError = sinon.stub();
restApi.add({
ref: '/defect/1234',
data: [{_ref: '/defect/2345'}],
collection: 'Duplicates'
}).then(function(result) {
onError.callCount.should.eql(0);
result.Errors.should.eql([]);
result.Warnings.should.eql([]);
result.Results.should.eql([{_ref: '/defect/2345'}]);
done();
}, onError).done();
});

it('calls back with error', function(done) {
var error = 'Error!';
this.post.yieldsAsync([error], null);
var restApi = new RestApi();
restApi.add({
ref: '/defect/1234',
data: [{_ref: '/defect/2345'}],
collection: 'Duplicates'
}, function(err, result) {
err.should.eql([error]);
should.not.exist(result);
done();
});
});

it('rejects promise with error', function(done) {
var error = 'Error!';
this.post.yieldsAsync([error], null);
var restApi = new RestApi();
var onSuccess = sinon.stub();
restApi.add({
ref: '/defect/1234',
data: [{_ref: '/defect/2345'}],
collection: 'Duplicates'
}).then(onSuccess, function(err) {
onSuccess.callCount.should.eql(0);
err.should.eql([error]);
done();
}).done();
});
});

describe('remove', function() {
it('translates request options', function() {
var restApi = new RestApi();
restApi.remove({
ref: '/defect/1234',
data: [{_ref: '/defect/2345'}],
collection: 'Duplicates',
scope: {workspace: '/workspace/1234'},
fetch: ['FormattedID'],
requestOptions: {
qs: {foo: 'bar'}
}
});

this.post.callCount.should.eql(1);
var args = this.post.firstCall.args[0];
args.qs.workspace.should.eql('/workspace/1234');
args.qs.fetch.should.eql('FormattedID');
args.qs.foo.should.eql('bar');
});

it('generates correct post request', function() {
var restApi = new RestApi();
var callback = sinon.stub();
restApi.remove({
ref: '/defect/1234',
data: [{_ref: '/defect/2345'}],
collection: 'Duplicates'
}, callback);

this.post.callCount.should.eql(1);
var args = this.post.firstCall.args;
args[0].url.should.eql('/defect/1234/Duplicates/remove');
args[0].json.should.eql({CollectionItems: [{_ref: '/defect/2345'}]});
});

it('calls back with result', function(done) {
this.post.yieldsAsync(null, {Errors: [], Warnings: []});
var restApi = new RestApi();
restApi.remove({
ref: '/defect/1234',
data: [{_ref: '/defect/2345'}],
collection: 'Duplicates'
}, function(error, result) {
should.not.exist(error);
result.Errors.should.eql([]);
result.Warnings.should.eql([]);
done();
});
});

it('resolves promise with result', function(done) {
this.post.yieldsAsync(null, {Errors: [], Warnings: []});
var restApi = new RestApi();
var onError = sinon.stub();
restApi.remove({
ref: '/defect/1234',
data: [{_ref: '/defect/2345'}],
collection: 'Duplicates'
}).then(function(result) {
onError.callCount.should.eql(0);
result.Errors.should.eql([]);
result.Warnings.should.eql([]);
done();
}, onError).done();
});

it('calls back with error', function(done) {
var error = 'Error!';
this.post.yieldsAsync([error], null);
var restApi = new RestApi();
restApi.remove({
ref: '/defect/1234',
data: [{_ref: '/defect/2345'}],
collection: 'Duplicates'
}, function(err, result) {
err.should.eql([error]);
should.not.exist(result);
done();
});
});

it('rejects promise with error', function(done) {
var error = 'Error!';
this.post.yieldsAsync([error], null);
var restApi = new RestApi();
var onSuccess = sinon.stub();
restApi.remove({
ref: '/defect/1234',
data: [{_ref: '/defect/2345'}],
collection: 'Duplicates'
}).then(onSuccess, function(err) {
onSuccess.callCount.should.eql(0);
err.should.eql([error]);
done();
}).done();
});
});
});
});

0 comments on commit 62301b8

Please sign in to comment.