-
Notifications
You must be signed in to change notification settings - Fork 363
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SEMVER-MAJOR] Disallow bulk updateOrCreate. #889
Conversation
Also, let me know if you want me to break this up into separate commits for some of that test functionality. |
if (Array.isArray(data)) { | ||
var isBulkCreate = data.every(function(item){ | ||
var id = getIdValue(self, item); | ||
return id === undefined || id === null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can probably be simplified to:
return !getIdValue(self, item);
@richardpringle Reviewed with some feedback for you. |
fix for #2123 |
if (isBulkCreate) { | ||
return self.create(data, options, cb); | ||
} else { | ||
return cb(new Error('updateOrCreate does not support bulk mode or any array input')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... or any array input
- that's not correct, is it? We still support array input when items don't contain ids.
@richardpringle ^^^ that did not create cross-link, did you mean strongloop/loopback#2123? It's also best to put cross-links into PR description at the top (you can edit the description after the PR was created). @superkhau @raymondfeng This change modifies the outcome of certain edge cases, e.g. when calling OTOH, if you think we should be more conservative and do not back-port this change, then I would vote for a more aggressive cleanup and always Or perhaps we can always return an error for array input in 3.x, and in 2.x branch, we can do what's implemented here, i.e. fail only when the request is not a clear bulk create. Thoughts? |
Cross-posting from strongloop/loopback#2123
In that case, I am leaning towards disabling array input for Thoughts? |
@superkhau suggested that we could also just make a change in the rest-adapter error handler to enable handling an array of errors. I really do think it would be better to clean up both POST (create) and PUT (replaceOrCreate) and PATCH (updateOrCreate) and release the changes all at the same time to stay consistent, assuming that replaceOrCreate will run into the same issues. So for now, I would suggest making the changes in strong-remoting, making them available in LoopBack 2.x and only landing some of my test cases here once I clean them up. |
Another argument for adjusting strong-remoting is that I would expect the error you get via REST would be the same as the error on the back-end. |
76e2490
to
43c9972
Compare
@bajtos, @superkhau, ready for another round of review |
LGTM |
Todo = db.define('Todo', { | ||
content: String, | ||
}); | ||
done(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should still call db.automigrate(done)
to ensure the tables are created? Note that these tests are run by all connectors, and especially SQL connectors need to create tables before you can store any data.
The way how the code is written now, you are relying on some other tests running earlier to create a table for Todo
model, possibly with a different set of properties than your Todo model has.
43c9972
to
af1d364
Compare
LGTM, please wait to see dependant build of connectors to pass, just to be sure. |
@slnode test please |
dependant loopback-connector-remote is failing on all instances of node 0.10 I believe it's because of the following PR: strongloop/loopback-connector-remote#44 and more particularly https://github.com/strongloop/loopback-connector-remote/pull/44/files#r61875179 |
Need to re-trigger tests again when strongloop/loopback-connector-remote#47 lands |
@slnode test please |
@bajtos, it looks like the expected behavior has now changed in some dependent modules (loopback-sdk-angular for example). What's the next step, is it to go through and re-write and/or remove unit tests from the dependants? |
Ha, that just confirms that this patch is a breaking change.
Could you please point me to the failing tests? Ideally, we should fix the tests to work with both LB 2.x and 3.x, but this really depends. |
Here are the failing tests: And below are the logs from Jenkins (link to see console on Jenkins): Tue, 24 May 2016 12:10:35 GMT engine:ws received "42["info",{"log":"'methods', ['get', 'save', 'query', 'remove', 'delete', 'create', 'upsert', 'exists', 'findById', 'find', 'findOne', 'updateAll', 'deleteById', 'count', 'prototype$updateAttributes', 'createChangeStream', 'bind', 'updateOrCreate', 'update', 'destroyById', 'removeById', 'modelName']","type":"log"}]"
Tue, 24 May 2016 12:10:35 GMT express:router dispatching POST /setup
Tue, 24 May 2016 12:10:35 GMT engine:socket packet
Tue, 24 May 2016 12:10:35 GMT express:router query : /setup
Tue, 24 May 2016 12:10:35 GMT express:router expressInit : /setup
Tue, 24 May 2016 12:10:35 GMT socket.io-parser decoded 2["info",{"log":"'methods', ['get', 'save', 'query', 'remove', 'delete', 'create', 'upsert', 'exists', 'findById', 'find', 'findOne', 'updateAll', 'deleteById', 'count', 'prototype$updateAttributes', 'createChangeStream', 'bind', 'updateOrCreate', 'update', 'destroyById', 'removeById', 'modelName']","type":"log"}] as {"type":2,"nsp":"/","data":["info",{"log":"'methods', ['get', 'save', 'query', 'remove', 'delete', 'create', 'upsert', 'exists', 'findById', 'find', 'findOne', 'updateAll', 'deleteById', 'count', 'prototype$updateAttributes', 'createChangeStream', 'bind', 'updateOrCreate', 'update', 'destroyById', 'removeById', 'modelName']","type":"log"}]}
Tue, 24 May 2016 12:10:35 GMT express:router <anonymous> : /setup
Tue, 24 May 2016 12:10:35 GMT express:router jsonParser : /setup
Tue, 24 May 2016 12:10:35 GMT socket.io:socket got packet {"type":2,"nsp":"/","data":["info",{"log":"'methods', ['get', 'save', 'query', 'remove', 'delete', 'create', 'upsert', 'exists', 'findById', 'find', 'findOne', 'updateAll', 'deleteById', 'count', 'prototype$updateAttributes', 'createChangeStream', 'bind', 'updateOrCreate', 'update', 'destroyById', 'removeById', 'modelName']","type":"log"}]}
Tue, 24 May 2016 12:10:35 GMT body-parser:json content-type "application/json"
Tue, 24 May 2016 12:10:35 GMT socket.io:socket emitting event ["info",{"log":"'methods', ['get', 'save', 'query', 'remove', 'delete', 'create', 'upsert', 'exists', 'findById', 'find', 'findOne', 'updateAll', 'deleteById', 'count', 'prototype$updateAttributes', 'createChangeStream', 'bind', 'updateOrCreate', 'update', 'destroyById', 'removeById', 'modelName']","type":"log"}]
Tue, 24 May 2016 12:10:35 GMT body-parser:json content-encoding "identity"
Tue, 24 May 2016 12:10:35 GMT body-parser:json read body
LOG: 'methods', ['get', 'save', 'query', 'remove', 'delete', 'create', 'upsert', 'exists', 'findById', 'find', 'findOne', 'updateAll', 'deleteById', 'count', 'prototype$updateAttributes', 'createChangeStream', 'bind', 'updateOrCreate', 'update', 'destroyById', 'removeById', 'modelName']
Tue, 24 May 2016 12:10:35 GMT engine:ws received "42["result",{"id":"","description":"has all methods including aliases","suite":["services","MyModel $resource"],"success":false,"skipped":false,"time":5,"log":["expected [ Array(22) ] to be a superset of [ Array(13) ]\nassert@http://localhost:9876/base/node_modules/chai/chai.js?80a81da918cc1a355fd23c104379920817ef5b78:873:67\nhttp://localhost:9876/base/node_modules/chai/chai.js?80a81da918cc1a355fd23c104379920817ef5b78:2293:25\nhttp://localhost:9876/base/node_modules/chai/chai.js?80a81da918cc1a355fd23c104379920817ef5b78:3627:30\nhttp://localhost:9876/base/test.e2e/spec/services.spec.js?e850318c0e078721d0c01502db8db3ccb18edce7:399:47"],"assertionErrors":[{"name":"AssertionError","message":"expected [ Array(22) ] to be a superset of [ Array(13) ]","showDiff":false}]}]"
Tue, 24 May 2016 12:10:35 GMT engine:socket packet
Tue, 24 May 2016 12:10:35 GMT socket.io-parser decoded 2["result",{"id":"","description":"has all methods including aliases","suite":["services","MyModel $resource"],"success":false,"skipped":false,"time":5,"log":["expected [ Array(22) ] to be a superset of [ Array(13) ]\nassert@http://localhost:9876/base/node_modules/chai/chai.js?80a81da918cc1a355fd23c104379920817ef5b78:873:67\nhttp://localhost:9876/base/node_modules/chai/chai.js?80a81da918cc1a355fd23c104379920817ef5b78:2293:25\nhttp://localhost:9876/base/node_modules/chai/chai.js?80a81da918cc1a355fd23c104379920817ef5b78:3627:30\nhttp://localhost:9876/base/test.e2e/spec/services.spec.js?e850318c0e078721d0c01502db8db3ccb18edce7:399:47"],"assertionErrors":[{"name":"AssertionError","message":"expected [ Array(22) ] to be a superset of [ Array(13) ]","showDiff":false}]}] as {"type":2,"nsp":"/","data":["result",{"id":"","description":"has all methods including aliases","suite":["services","MyModel $resource"],"success":false,"skipped":false,"time":5,"log":["expected [ Array(22) ] to be a superset of [ Array(13) ]\nassert@http://localhost:9876/base/node_modules/chai/chai.js?80a81da918cc1a355fd23c104379920817ef5b78:873:67\nhttp://localhost:9876/base/node_modules/chai/chai.js?80a81da918cc1a355fd23c104379920817ef5b78:2293:25\nhttp://localhost:9876/base/node_modules/chai/chai.js?80a81da918cc1a355fd23c104379920817ef5b78:3627:30\nhttp://localhost:9876/base/test.e2e/spec/services.spec.js?e850318c0e078721d0c01502db8db3ccb18edce7:399:47"],"assertionErrors":[{"name":"AssertionError","message":"expected [ Array(22) ] to be a superset of [ Array(13) ]","showDiff":false}]}]}
Tue, 24 May 2016 12:10:35 GMT socket.io:socket got packet {"type":2,"nsp":"/","data":["result",{"id":"","description":"has all methods including aliases","suite":["services","MyModel $resource"],"success":false,"skipped":false,"time":5,"log":["expected [ Array(22) ] to be a superset of [ Array(13) ]\nassert@http://localhost:9876/base/node_modules/chai/chai.js?80a81da918cc1a355fd23c104379920817ef5b78:873:67\nhttp://localhost:9876/base/node_modules/chai/chai.js?80a81da918cc1a355fd23c104379920817ef5b78:2293:25\nhttp://localhost:9876/base/node_modules/chai/chai.js?80a81da918cc1a355fd23c104379920817ef5b78:3627:30\nhttp://localhost:9876/base/test.e2e/spec/services.spec.js?e850318c0e078721d0c01502db8db3ccb18edce7:399:47"],"assertionErrors":[{"name":"AssertionError","message":"expected [ Array(22) ] to be a superset of [ Array(13) ]","showDiff":false}]}]}
Tue, 24 May 2016 12:10:35 GMT socket.io:socket emitting event ["result",{"id":"","description":"has all methods including aliases","suite":["services","MyModel $resource"],"success":false,"skipped":false,"time":5,"log":["expected [ Array(22) ] to be a superset of [ Array(13) ]\nassert@http://localhost:9876/base/node_modules/chai/chai.js?80a81da918cc1a355fd23c104379920817ef5b78:873:67\nhttp://localhost:9876/base/node_modules/chai/chai.js?80a81da918cc1a355fd23c104379920817ef5b78:2293:25\nhttp://localhost:9876/base/node_modules/chai/chai.js?80a81da918cc1a355fd23c104379920817ef5b78:3627:30\nhttp://localhost:9876/base/test.e2e/spec/services.spec.js?e850318c0e078721d0c01502db8db3ccb18edce7:399:47"],"assertionErrors":[{"name":"AssertionError","message":"expected [ Array(22) ] to be a superset of [ Array(13) ]","showDiff":false}]}]
✖ has all methods including aliases Tue, 24 May 2016 12:10:37 GMT socket.io-parser decoded 2["result",{"id":"","description":"creates multiple related models","suite":["services","for models with hasAndBelongsToMany relations"],"success":false,"skipped":false,"time":0,"log":["undefined is not an object (evaluating 'action.apply')\ncreateMany@http://localhost:3838/services?services::with%20authentication%20using%20built-in%20User%20model::adds%20auth-related%20methods%20to%20the%20User%20model:913:24\nhttp://localhost:9876/base/test.e2e/spec/services.spec.js?e850318c0e078721d0c01502db8db3ccb18edce7:936:49"],"assertionErrors":[]}] as {"type":2,"nsp":"/","data":["result",{"id":"","description":"creates multiple related models","suite":["services","for models with hasAndBelongsToMany relations"],"success":false,"skipped":false,"time":0,"log":["undefined is not an object (evaluating 'action.apply')\ncreateMany@http://localhost:3838/services?services::with%20authentication%20using%20built-in%20User%20model::adds%20auth-related%20methods%20to%20the%20User%20model:913:24\nhttp://localhost:9876/base/test.e2e/spec/services.spec.js?e850318c0e078721d0c01502db8db3ccb18edce7:936:49"],"assertionErrors":[]}]}
Tue, 24 May 2016 12:10:37 GMT express:router <anonymous> : /setup
Tue, 24 May 2016 12:10:37 GMT socket.io:socket got packet {"type":2,"nsp":"/","data":["result",{"id":"","description":"creates multiple related models","suite":["services","for models with hasAndBelongsToMany relations"],"success":false,"skipped":false,"time":0,"log":["undefined is not an object (evaluating 'action.apply')\ncreateMany@http://localhost:3838/services?services::with%20authentication%20using%20built-in%20User%20model::adds%20auth-related%20methods%20to%20the%20User%20model:913:24\nhttp://localhost:9876/base/test.e2e/spec/services.spec.js?e850318c0e078721d0c01502db8db3ccb18edce7:936:49"],"assertionErrors":[]}]}
Tue, 24 May 2016 12:10:37 GMT express:router jsonParser : /setup
Tue, 24 May 2016 12:10:37 GMT body-parser:json content-type "application/json"
Tue, 24 May 2016 12:10:37 GMT socket.io:socket emitting event ["result",{"id":"","description":"creates multiple related models","suite":["services","for models with hasAndBelongsToMany relations"],"success":false,"skipped":false,"time":0,"log":["undefined is not an object (evaluating 'action.apply')\ncreateMany@http://localhost:3838/services?services::with%20authentication%20using%20built-in%20User%20model::adds%20auth-related%20methods%20to%20the%20User%20model:913:24\nhttp://localhost:9876/base/test.e2e/spec/services.spec.js?e850318c0e078721d0c01502db8db3ccb18edce7:936:49"],"assertionErrors":[]}]
Tue, 24 May 2016 12:10:37 GMT body-parser:json content-encoding "identity"
Tue, 24 May 2016 12:10:37 GMT body-parser:json read body
✖ creates multiple related models Tue, 24 May 2016 12:10:37 GMT engine:ws received "42["result",{"id":"","description":"removes all related models","suite":["services","for models with hasAndBelongsToMany relations"],"success":false,"skipped":true,"time":0,"log":[],"assertionErrors":[]}]"
Tue, 24 May 2016 12:10:37 GMT engine:socket packet
Tue, 24 May 2016 12:10:37 GMT socket.io-parser decoded 2["result",{"id":"","description":"removes all related models","suite":["services","for models with hasAndBelongsToMany relations"],"success":false,"skipped":true,"time":0,"log":[],"assertionErrors":[]}] as {"type":2,"nsp":"/","data":["result",{"id":"","description":"removes all related models","suite":["services","for models with hasAndBelongsToMany relations"],"success":false,"skipped":true,"time":0,"log":[],"assertionErrors":[]}]}
Tue, 24 May 2016 12:10:37 GMT socket.io:socket got packet {"type":2,"nsp":"/","data":["result",{"id":"","description":"removes all related models","suite":["services","for models with hasAndBelongsToMany relations"],"success":false,"skipped":true,"time":0,"log":[],"assertionErrors":[]}]}
Tue, 24 May 2016 12:10:37 GMT socket.io:socket emitting event ["result",{"id":"","description":"removes all related models","suite":["services","for models with hasAndBelongsToMany relations"],"success":false,"skipped":true,"time":0,"log":[],"assertionErrors":[]}]
✖ removes all related models (skipped) FAILED TESTS:
MyModel $resource
✖ can create multiple new resources
PhantomJS 2.1.1 (Linux 0.0.0)
undefined is not a function (evaluating 'MyModel.createMany')
/mnt/jenkins/workspace/loopback-sdk-angular/89d66e82/test.e2e/spec/services.spec.js:331:37
✖ has all methods including aliases
PhantomJS 2.1.1 (Linux 0.0.0)
expected [ Array(22) ] to be a superset of [ Array(13) ]
assert@/mnt/jenkins/workspace/loopback-sdk-angular/89d66e82/node_modules/chai/chai.js:873:67
/mnt/jenkins/workspace/loopback-sdk-angular/89d66e82/node_modules/chai/chai.js:2293:25
/mnt/jenkins/workspace/loopback-sdk-angular/89d66e82/node_modules/chai/chai.js:3627:30
/mnt/jenkins/workspace/loopback-sdk-angular/89d66e82/test.e2e/spec/services.spec.js:399:47
✖ creates multiple related models
PhantomJS 2.1.1 (Linux 0.0.0)
undefined is not an object (evaluating 'action.apply')
createMany@http://localhost:3838/services?services::with%20authentication%20using%20built-in%20User%20model::adds%20auth-related%20methods%20to%20the%20User%20model:913:24
/mnt/jenkins/workspace/loopback-sdk-angular/89d66e82/test.e2e/spec/services.spec.js:936:49 |
af1d364
to
b66a4a0
Compare
@slnode test please |
@richardpringle what's the status of this patch? |
@richardpringle I have rebased the patch on the current master and resolved merge conflicts. Run |
AFAICT, the downstream CI failures are not related to this change. I am going to land it. |
BREAKING CHANGE
@bajtos, please review.
connect to strongloop/loopback#2123