From b1a0098c033c33b4f0fffc961408bc29b691168a Mon Sep 17 00:00:00 2001 From: Alan Plum Date: Mon, 15 Sep 2014 23:34:41 +0200 Subject: [PATCH 1/9] Allow passing joi params directly. --- .../org/arangodb/foxx/request_context.js | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/js/server/modules/org/arangodb/foxx/request_context.js b/js/server/modules/org/arangodb/foxx/request_context.js index abd2c8cfe0..dbbfe3fc9c 100644 --- a/js/server/modules/org/arangodb/foxx/request_context.js +++ b/js/server/modules/org/arangodb/foxx/request_context.js @@ -239,9 +239,16 @@ extend(RequestContext.prototype, { type = attributes.type, required = attributes.required, description = attributes.description, - constraint = type, - regexType = type, - cfg; + constraint, regexType, cfg; + + if (attributes.isJoi) { + type = attributes; + required = undefined; + description = undefined; + } + + constraint = type; + regexType = type; // deprecated: assume type.describe is always a function if (type && typeof type.describe === 'function') { @@ -319,8 +326,15 @@ extend(RequestContext.prototype, { var type = attributes.type, required = attributes.required, description = attributes.description, - constraint = type, - cfg; + constraint, cfg; + + if (attributes.isJoi) { + type = attributes; + required = undefined; + description = undefined; + } + + constraint = type; // deprecated: assume type.describe is always a function if (type && typeof type.describe === 'function') { From 6e3e5d47fcdb2a464a8dd7618dea2d7dafed7669 Mon Sep 17 00:00:00 2001 From: Alan Plum Date: Tue, 16 Sep 2014 00:17:46 +0200 Subject: [PATCH 2/9] Allow ctrl.before to prevent further processing of the request. --- js/server/modules/org/arangodb/foxx/controller.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/js/server/modules/org/arangodb/foxx/controller.js b/js/server/modules/org/arangodb/foxx/controller.js index 9fc5fa2cec..5ca83305c2 100644 --- a/js/server/modules/org/arangodb/foxx/controller.js +++ b/js/server/modules/org/arangodb/foxx/controller.js @@ -371,8 +371,10 @@ extend(Controller.prototype, { url: {match: path}, action: { callback: function (req, res, opts, next) { - func(req, res, opts); - next(); + var result = func(req, res, opts); + if (result !== false) { + next(); + } } } }); From cf3c219f22787fa046e247d00243cedbe6ed6c7d Mon Sep 17 00:00:00 2001 From: Alan Plum Date: Tue, 16 Sep 2014 10:27:08 +0200 Subject: [PATCH 3/9] Revert "Allow specifying "_" as collection prefix." This reverts commit ebc5c1490d14471b04b8398b44a06896a9460ea8. --- js/server/modules/org/arangodb/foxx/manager.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/js/server/modules/org/arangodb/foxx/manager.js b/js/server/modules/org/arangodb/foxx/manager.js index 58b8bb4eb0..90d943a3db 100644 --- a/js/server/modules/org/arangodb/foxx/manager.js +++ b/js/server/modules/org/arangodb/foxx/manager.js @@ -192,13 +192,14 @@ function extendContext (context, app, root) { "use strict"; var cp = context.collectionPrefix; + var cname = ""; - if (cp !== "" && cp !== "_") { - cp += "_"; + if (cp !== "") { + cname = cp + "_"; } context.collectionName = function (name) { - var replaced = ((cp + name).replace(/[^a-zA-Z0-9]/g, '_').replace(/(^_+|_+$)/g, '')).substr(0, 64); + var replaced = (cname + name).replace(/[^a-zA-Z0-9]/g, '_').replace(/(^_+|_+$)/g, '').substr(0, 64); if (replaced.length === 0) { throw new Error("Cannot derive collection name from '" + name + "'"); From b12d7f99cdc4d4dce5af4bae3d1ee6dd67414ccf Mon Sep 17 00:00:00 2001 From: scottashton Date: Tue, 16 Sep 2014 10:36:02 +0200 Subject: [PATCH 4/9] added resolving from example strings to objects in GRAPH_NEIGHBORS --- js/server/modules/org/arangodb/ahuacatl.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/js/server/modules/org/arangodb/ahuacatl.js b/js/server/modules/org/arangodb/ahuacatl.js index 63db469ace..3fd967447d 100644 --- a/js/server/modules/org/arangodb/ahuacatl.js +++ b/js/server/modules/org/arangodb/ahuacatl.js @@ -191,7 +191,6 @@ function FILTER (list, examples) { for (i = 0; i < list.length; ++i) { var element = list[i]; - if (MATCHES(element, examples, false)) { result.push(element); } @@ -3860,7 +3859,6 @@ function MATCHES (element, examples, returnIndex) { if (! Array.isArray(examples)) { examples = [ examples ]; } - if (examples.length === 0) { THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, "MATCHES"); } @@ -3870,7 +3868,6 @@ function MATCHES (element, examples, returnIndex) { for (i = 0; i < examples.length; ++i) { var example = examples[i]; var result = true; - if (TYPEWEIGHT(example) !== TYPEWEIGHT_DOCUMENT) { THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, "MATCHES"); } @@ -5997,6 +5994,11 @@ function GENERAL_GRAPH_NEIGHBORS (graphName, options.startVertexCollectionRestriction = options.vertexCollectionRestriction; } } + if (options.neighborExamples) { + if (typeof options.neighborExamples === "string") { + options.neighborExamples = {_id : options.neighborExamples}; + } + } var neighbors = [], params = TRAVERSAL_PARAMS(), factory = TRAVERSAL.generalGraphDatasourceFactory(graphName); @@ -6005,14 +6007,12 @@ function GENERAL_GRAPH_NEIGHBORS (graphName, params.paths = true; params.visitor = TRAVERSAL_NEIGHBOR_VISITOR; var fromVertices = RESOLVE_GRAPH_TO_FROM_VERTICES(graphName, options); - if (options.edgeExamples) { params.followEdges = options.edgeExamples; } if (options.edgeCollectionRestriction) { params.edgeCollectionRestriction = options.edgeCollectionRestriction; } - fromVertices.forEach(function (v) { var e = TRAVERSAL_FUNC("GRAPH_NEIGHBORS", factory, From e04cfebfe2266a8a48cfcadbc2bddd9f2e251112 Mon Sep 17 00:00:00 2001 From: Alan Plum Date: Tue, 16 Sep 2014 11:03:38 +0200 Subject: [PATCH 5/9] Added tests and docs for param shorthand syntax, allow passing 'allowMultiple' via joi.meta, fixed joi.required/optional being ignored. --- .../org/arangodb/foxx/request_context.js | 46 +++++++++++-- js/server/tests/shell-foxx.js | 65 ++++++++++++++++++- 2 files changed, 105 insertions(+), 6 deletions(-) diff --git a/js/server/modules/org/arangodb/foxx/request_context.js b/js/server/modules/org/arangodb/foxx/request_context.js index dbbfe3fc9c..4af90b3ff9 100644 --- a/js/server/modules/org/arangodb/foxx/request_context.js +++ b/js/server/modules/org/arangodb/foxx/request_context.js @@ -220,13 +220,23 @@ extend(RequestContext.prototype, { /// /// You can also provide a description of this parameter. /// -/// @EXAMPLES +/// *Examples* +/// +/// ```js +/// app.get("/foxx/:id", function { +/// // Do something +/// }).pathParam("id", type: joi.number().integer().required().description("Id of the Foxx")); +/// ``` +/// +/// You can also pass in a configuration object instead: /// /// ```js /// app.get("/foxx/:id", function { /// // Do something /// }).pathParam("id", { -/// type: joi.number().integer().required().description("Id of the Foxx") +/// type: joi.number().integer(), +/// required: true, +/// description: "Id of the Foxx" /// }); /// ``` /// @endDocuBlock @@ -313,6 +323,19 @@ extend(RequestContext.prototype, { /// ```js /// app.get("/foxx", function { /// // Do something +/// }).queryParam("id", +/// joi.number().integer() +/// .required() +/// .description("Id of the Foxx") +/// .meta({allowMultiple: false}) +/// }); +/// ``` +/// +/// You can also pass in a configuration object instead: +/// +/// ```js +/// app.get("/foxx", function { +/// // Do something /// }).queryParam("id", { /// type: joi.number().integer().required().description("Id of the Foxx"), /// allowMultiple: false @@ -326,12 +349,14 @@ extend(RequestContext.prototype, { var type = attributes.type, required = attributes.required, description = attributes.description, + allowMultiple = attributes.allowMultiple, constraint, cfg; if (attributes.isJoi) { type = attributes; required = undefined; description = undefined; + allowMultiple = undefined; } constraint = type; @@ -344,6 +369,9 @@ extend(RequestContext.prototype, { if (typeof description === 'string') { constraint = constraint.description(description); } + if (typeof allowMultiple === 'boolean') { + constraint = constraint.meta({allowMultiple: allowMultiple}); + } this.constraints.queryParams[paramName] = constraint; cfg = constraint.describe(); if (Array.isArray(cfg)) { @@ -352,8 +380,18 @@ extend(RequestContext.prototype, { } else { type = cfg.type; } - required = Boolean(cfg.flags && cfg.flags.presense === 'required'); + required = Boolean(cfg.flags && cfg.flags.presence === 'required'); description = cfg.description; + if (cfg.meta) { + if (!Array.isArray(cfg.meta)) { + cfg.meta = [cfg.meta]; + } + _.each(cfg.meta, function (meta) { + if (meta && typeof meta.allowMultiple === 'boolean') { + allowMultiple = meta.allowMultiple; + } + }); + } if ( type === 'number' && _.isArray(cfg.rules) && @@ -370,7 +408,7 @@ extend(RequestContext.prototype, { description, type, required, - attributes.allowMultiple + Boolean(allowMultiple) ); return this; }, diff --git a/js/server/tests/shell-foxx.js b/js/server/tests/shell-foxx.js index feb926e3a6..6abd9dc2fb 100644 --- a/js/server/tests/shell-foxx.js +++ b/js/server/tests/shell-foxx.js @@ -514,6 +514,21 @@ function DocumentationAndConstraintsSpec () { assertEqual(context.constraints.urlParams, {id: constraint}); }, + testDefinePathParamShorthand: function () { + var constraint = joi.number().integer().description("Id of the Foxx"), + context = app.get('/foxx/:id', function () { + //nothing + }).pathParam("id", constraint); + + assertEqual(routes.length, 1); + assertEqual(routes[0].url.constraint.id, "/[0-9]+/"); + assertEqual(routes[0].docs.parameters[0].paramType, "path"); + assertEqual(routes[0].docs.parameters[0].name, "id"); + assertEqual(routes[0].docs.parameters[0].description, "Id of the Foxx"); + assertEqual(routes[0].docs.parameters[0].dataType, "integer"); + assertEqual(context.constraints.urlParams, {id: constraint}); + }, + testDefinePathCaseParam: function () { var constraint = joi.number().integer().description("Id of the Foxx"), context = app.get('/foxx/:idParam', function () { @@ -592,10 +607,56 @@ function DocumentationAndConstraintsSpec () { context = app.get('/foxx', function () { //nothing }).queryParam("a", { - type: constraint, - allowMultiple: true + type: constraint }); + assertEqual(routes.length, 1); + assertEqual(routes[0].docs.parameters[0].paramType, "query"); + assertEqual(routes[0].docs.parameters[0].name, "a"); + assertEqual(routes[0].docs.parameters[0].description, "The value of an a"); + assertEqual(routes[0].docs.parameters[0].dataType, "integer"); + assertEqual(routes[0].docs.parameters[0].required, false); + assertEqual(routes[0].docs.parameters[0].allowMultiple, false); + assertEqual(context.constraints.queryParams, {a: constraint}); + }, + + testDefineQueryParamWithOverrides: function () { + var constraint = joi.number().integer(), + context = app.get('/foxx', function () { + //nothing + }).queryParam("a", { + type: constraint, + description: "The value of an a", + allowMultiple: true, + required: true + }); + + assertEqual(routes.length, 1); + assertEqual(routes[0].docs.parameters[0].paramType, "query"); + assertEqual(routes[0].docs.parameters[0].name, "a"); + assertEqual(routes[0].docs.parameters[0].description, "The value of an a"); + assertEqual(routes[0].docs.parameters[0].dataType, "integer"); + print(0) + assertEqual(routes[0].docs.parameters[0].required, true); + print(1) + assertEqual(routes[0].docs.parameters[0].allowMultiple, true); + print(2) + assertEqual(context.constraints.queryParams, { + a: constraint + .description("The value of an a") + .meta({allowMultiple: true}) + .required() + }); + }, + + testDefineQueryParamShorthand: function () { + var constraint = joi.number().integer() + .description("The value of an a") + .meta({allowMultiple: true}), + context = app.get('/foxx', function () { + //nothing + }).queryParam("a", constraint); + assertEqual(routes.length, 1); assertEqual(routes[0].docs.parameters[0].paramType, "query"); assertEqual(routes[0].docs.parameters[0].name, "a"); From cd8af52859fcc3875c8c31ed0a3aaf3ee8fd6d6a Mon Sep 17 00:00:00 2001 From: Alan Plum Date: Tue, 16 Sep 2014 11:39:45 +0200 Subject: [PATCH 6/9] Added documentation for ctrl.before interrupts. --- js/server/modules/org/arangodb/foxx/controller.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/server/modules/org/arangodb/foxx/controller.js b/js/server/modules/org/arangodb/foxx/controller.js index 5ca83305c2..56123dfaed 100644 --- a/js/server/modules/org/arangodb/foxx/controller.js +++ b/js/server/modules/org/arangodb/foxx/controller.js @@ -346,7 +346,11 @@ extend(Controller.prototype, { /// The before function takes a *path* on which it should watch and a /// function that it should execute before the routing takes place. If you do /// omit the path, the function will be executed before each request, no matter -/// the path. Your function gets a Request and a Response object. +/// the path. Your function gets a Request and a Response object. +/// +/// If your callback returns the Boolean value *false*, the route handling +/// will not proceed. You can use this to intercept invalid or unauthorized +/// requests and prevent them from being passed to the matching routes. /// /// @EXAMPLES /// From 54e5333590f2b987abec1acd89131455a4aab338 Mon Sep 17 00:00:00 2001 From: Alan Plum Date: Tue, 16 Sep 2014 11:44:48 +0200 Subject: [PATCH 7/9] Provided examples for ctrl.allRoutes. --- .../Books/Users/Foxx/FoxxController.mdpp | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Documentation/Books/Users/Foxx/FoxxController.mdpp b/Documentation/Books/Users/Foxx/FoxxController.mdpp index 5ea81dfd28..93b45e4e84 100644 --- a/Documentation/Books/Users/Foxx/FoxxController.mdpp +++ b/Documentation/Books/Users/Foxx/FoxxController.mdpp @@ -85,6 +85,29 @@ do the same for all routes of a controller. For this purpose use the *allRoutes* object of the according controller. The following methods are available. +*Examples* + +Provide an error response for all routes handled by this controller: + +```js +ctrl.allRoutes +.errorResponse(Unauthorized, 401, 'Not authenticated.') +.errorResponse(NotFound, 404, 'Document not found.') +.errorResponse(ImATeapot, 418, 'I\'m a teapot.'); + +ctrl.get('/some/route', function (req, res) { + // ... + throw new NotFound('The document does not exist'); + // ... +}); // no errorResponse needed here + +ctrl.get('/another/route', function (req, res) { + // ... + throw new NotFound('I made you a cookie but I ated it'); + // ... +}); // no errorResponse needed here either +``` + !SUBSECTION Buffer Error Response @startDocuBlock JSF_foxx_RequestContextBuffer_errorResponse From 6f357edb671c712123ea788845ffc54382447e43 Mon Sep 17 00:00:00 2001 From: Alan Plum Date: Tue, 16 Sep 2014 14:04:20 +0200 Subject: [PATCH 8/9] Made Foxx.Repository methods behave more consistently. Fixes #975. --- .../modules/org/arangodb/foxx/repository.js | 92 ++++++++++++------- 1 file changed, 61 insertions(+), 31 deletions(-) diff --git a/js/server/modules/org/arangodb/foxx/repository.js b/js/server/modules/org/arangodb/foxx/repository.js index 7d6042a344..ae095a9c70 100644 --- a/js/server/modules/org/arangodb/foxx/repository.js +++ b/js/server/modules/org/arangodb/foxx/repository.js @@ -28,6 +28,7 @@ //////////////////////////////////////////////////////////////////////////////// var Repository, + Model = require("org/arangodb/foxx/model").Model, _ = require("underscore"), extend = require('org/arangodb/extend').extend; @@ -82,7 +83,7 @@ Repository = function (collection, opts) { /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// - this.modelPrototype = this.options.model || require("org/arangodb/foxx/model").Model; + this.modelPrototype = this.options.model || Model; //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_foxx_repository_prefix @@ -262,7 +263,7 @@ _.extend(Repository.prototype, { remove: function (model) { 'use strict'; var id = model.get('_id'); - this.collection.remove(id); + return this.collection.remove(id); }, //////////////////////////////////////////////////////////////////////////////// @@ -281,7 +282,7 @@ _.extend(Repository.prototype, { //////////////////////////////////////////////////////////////////////////////// removeById: function (id) { 'use strict'; - this.collection.remove(id); + return this.collection.remove(id); }, //////////////////////////////////////////////////////////////////////////////// @@ -299,7 +300,7 @@ _.extend(Repository.prototype, { //////////////////////////////////////////////////////////////////////////////// removeByExample: function (example) { 'use strict'; - this.collection.removeByExample(example); + return this.collection.removeByExample(example); }, // ----------------------------------------------------------------------------- @@ -311,7 +312,7 @@ _.extend(Repository.prototype, { /// `FoxxRepository#replace(model)` /// /// Find the model in the database by its *_id* and replace it with this version. -/// Expects a model. Sets the Revision of the model. +/// Expects a model. Sets the revision of the model. /// Returns the model. /// /// @EXAMPLES @@ -324,7 +325,7 @@ _.extend(Repository.prototype, { //////////////////////////////////////////////////////////////////////////////// replace: function (model) { 'use strict'; - var id = model.get("_id"), + var id = model.get("_id") || model.get("_key"), data = model.forDB(), id_and_rev = this.collection.replace(id, data); model.set(id_and_rev); @@ -333,11 +334,12 @@ _.extend(Repository.prototype, { //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_foxx_repository_replaceById -/// `FoxxRepository#replaceById(id, model)` +/// `FoxxRepository#replaceById(id, object)` /// -/// Find the model in the database by the given ID and replace it with the given. -/// model. -/// Sets the ID and Revision of the model and also returns it. +/// Find the item in the database by the given ID and replace it with the +/// given object's attributes. +/// +/// If the object is a model, updates the model's revision and returns the model. /// /// @EXAMPLES /// @@ -346,21 +348,22 @@ _.extend(Repository.prototype, { /// ``` /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// - replaceById: function (id, model) { + replaceById: function (id, data) { 'use strict'; - var data = model.forDB(), - id_and_rev = this.collection.replace(id, data); - model.set(id_and_rev); - return model; + if (data instanceof Model) { + var id_and_rev = this.collection.replace(id, data.forDB()); + data.set(id_and_rev); + return data; + } + return this.collection.replace(id, data); }, //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_foxx_repository_replaceByExample -/// `FoxxRepository#replaceByExample(example, model)` +/// `FoxxRepository#replaceByExample(example, object)` /// -/// Find the model in the database by the given example and replace it with the given. -/// model. -/// Sets the ID and Revision of the model and also returns it. +/// Find every matching item by example and replace it with the attributes in +/// the provided object. /// /// @EXAMPLES /// @@ -369,24 +372,46 @@ _.extend(Repository.prototype, { /// ``` /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// - replaceByExample: function (example, model) { + replaceByExample: function (example, data) { 'use strict'; - var data = model.forDB(), - idAndRev = this.collection.replaceByExample(example, data); - model.set(idAndRev); - return model; + return this.collection.replaceByExample(example, data); }, // ----------------------------------------------------------------------------- // --SUBSECTION-- Updating Entries // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_foxx_repository_update +/// `FoxxRepository#update(model, object)` +/// +/// Find the model in the database by its *_id* and update it with the given object. +/// Expects a model. Sets the revision of the model and updates its properties. +/// Returns the model. +/// +/// @EXAMPLES +/// +/// ```javascript +/// repository.update(myModel, {name: 'Jan Steeman'}); +/// ``` +/// @endDocuBlock +//////////////////////////////////////////////////////////////////////////////// + update: function (model, data) { + 'use strict'; + var id = model.get("_id") || model.get("_key"), + id_and_rev = this.collection.update(id, data); + model.set(data); + model.set(id_and_rev); + return model; + }, + //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_foxx_repository_updateById /// `FoxxRepository#updateById(id, object)` /// /// Find an item by ID and update it with the attributes in the provided object. -/// Returns the updated model. +/// +/// If the object is a model, updates the model's revision and returns the model. /// /// @EXAMPLES /// @@ -395,17 +420,22 @@ _.extend(Repository.prototype, { /// ``` /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// - updateById: function (id, updates) { + updateById: function (id, data) { 'use strict'; - this.collection.update(id, updates); + if (data instanceof Model) { + var id_and_rev = this.collection.update(id, data.forDB()); + data.set(id_and_rev); + return data; + } + return this.collection.update(id, data); }, //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_foxx_repository_updateByExample /// `FoxxRepository#updateByExample(example, object)` /// -/// Find an item by example and update it with the attributes in the provided object. -/// Returns the updated model. +/// Find every matching item by example and update it with the attributes in +/// the provided object. /// /// @EXAMPLES /// @@ -414,9 +444,9 @@ _.extend(Repository.prototype, { /// ``` /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// - updateByExample: function (example, updates) { + updateByExample: function (example, data) { 'use strict'; - this.collection.updateByExample(example, updates); + return this.collection.updateByExample(example, data); }, // ----------------------------------------------------------------------------- From 0de1da4cc80e8e570a24c08d4c89703286120f40 Mon Sep 17 00:00:00 2001 From: Alan Plum Date: Tue, 16 Sep 2014 14:56:29 +0200 Subject: [PATCH 9/9] Adjusted tests to reflect corrected behaviour. --- js/server/tests/shell-foxx-repository-spec.js | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/js/server/tests/shell-foxx-repository-spec.js b/js/server/tests/shell-foxx-repository-spec.js index 5a2d9b67fd..0ff25fa6ed 100644 --- a/js/server/tests/shell-foxx-repository-spec.js +++ b/js/server/tests/shell-foxx-repository-spec.js @@ -288,25 +288,39 @@ describe('Repository Methods', function () { }); it('should replace by example', function () { - var model = new Model({}), - idAndRev = createSpy('idAndRev'), - example = createSpy('example'), - data = createSpy('data'), - result; + var example = createSpy('example'), + data = createSpy('data'); - spyOn(model, 'forDB').and.returnValue(data); - spyOn(model, 'set'); - collection.replaceByExample.and.returnValue(idAndRev); + instance.replaceByExample(example, data); - result = instance.replaceByExample(example, model); - - expect(result).toBe(model); - expect(model.set.calls.argsFor(0)).toEqual([idAndRev]); expect(collection.replaceByExample.calls.argsFor(0)).toEqual([example, data]); }); }); describe('for updating entries', function () { + it('should allow to update by model', function () { + var model = new Model({}), + idAndRev = createSpy('idAndRev'), + id = createSpy('id'), + data = createSpy('data'), + updates = createSpy('updates'), + result; + + spyOn(model, 'get').and.returnValue(id); + spyOn(model, 'forDB').and.returnValue(data); + spyOn(model, 'set'); + collection.update.and.returnValue(idAndRev); + + result = instance.update(model, updates); + + expect(result).toBe(model); + expect(model.set.calls.allArgs().length).toEqual(2); + expect(model.set.calls.allArgs()).toContain([idAndRev]); + expect(model.set.calls.allArgs()).toContain([updates]); + expect(collection.update.calls.argsFor(0)).toEqual([id, updates]); + expect(model.get.calls.argsFor(0)).toEqual(['_id']); + }); + it('should update by id', function () { var id = createSpy('id'), updates = createSpy('updates'), @@ -320,13 +334,11 @@ describe('Repository Methods', function () { it('should update by example', function () { var example = createSpy('example'), - updates = createSpy('updates'), - idAndRev = createSpy('idAndRev'); + data = createSpy('data'); - collection.updateByExample.and.returnValue(idAndRev); - instance.updateByExample(example, updates); + instance.updateByExample(example, data); - expect(collection.updateByExample.calls.argsFor(0)).toEqual([example, updates]); + expect(collection.updateByExample.calls.argsFor(0)).toEqual([example, data]); }); });