Skip to content
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

feat: adds mongodb: {dataType: 'ObjectID'} to model properties #517

Merged
merged 1 commit into from
Jun 3, 2019

Conversation

hacksparrow
Copy link
Contributor

@hacksparrow hacksparrow commented May 9, 2019

Adds mongodb: {dataType: 'ObjectID'} to model properties. Enforces strictObjectIDCoercion settings on all model properties.

Related issues

loopbackio/loopback-next#2085

  • connect to <link_to_referenced_issue>

Checklist

  • New tests added or existing tests modified to cover all changes
  • Code conforms with the style
    guide

@hacksparrow hacksparrow changed the title feat: add isObjectID to property options [WIP]feat: add isObjectID to property options May 9, 2019
@hacksparrow hacksparrow force-pushed the feat/isObjectID branch 4 times, most recently from 954f159 to 35af7d6 Compare May 9, 2019 04:45
test/id.test.js Outdated Show resolved Hide resolved
@hacksparrow hacksparrow force-pushed the feat/isObjectID branch 2 times, most recently from ba18b44 to 3bc1a45 Compare May 9, 2019 08:36
lib/mongodb.js Outdated Show resolved Hide resolved
@hacksparrow hacksparrow force-pushed the feat/isObjectID branch 3 times, most recently from c6a2db5 to 2e4c2af Compare May 9, 2019 13:23
@hacksparrow hacksparrow changed the title [WIP]feat: add isObjectID to property options feat: add isObjectID to property options May 9, 2019
@hacksparrow hacksparrow changed the title feat: add isObjectID to property options [WIP]feat: add isObjectID to property options May 9, 2019
@hacksparrow hacksparrow force-pushed the feat/isObjectID branch 2 times, most recently from 693ccf4 to 9836d99 Compare May 14, 2019 06:56
lib/mongodb.js Outdated Show resolved Hide resolved
lib/mongodb.js Outdated Show resolved Hide resolved
test/id.test.js Show resolved Hide resolved
test/id.test.js Outdated Show resolved Hide resolved
test/id.test.js Outdated Show resolved Hide resolved
test/id.test.js Outdated Show resolved Hide resolved
lib/mongodb.js Outdated Show resolved Hide resolved
@hacksparrow hacksparrow force-pushed the feat/isObjectID branch 4 times, most recently from adc266a to 3b73d23 Compare May 27, 2019 12:45
@hacksparrow hacksparrow changed the title [WIP]feat: adds mongodb: {dataType: 'ObjectID'} to model properties feat: adds mongodb: {dataType: 'ObjectID'} to model properties May 27, 2019
lib/mongodb.js Outdated Show resolved Hide resolved
if (isDecimal) {
cond = Decimal128.fromString(cond);
debug('buildWhere decimal value: %s, constructor name: %s', cond, cond.constructor.name);
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to handle prop.mongodb.dataType.toLowerCase() === 'objectid' too, so that string properties stored as ObjectIDs are handled in where queries too. Please start with a unit test that fails now.

I am ok to leave this part out of scope of this pull request if you prefer, but I think it should be implemented before we can call the user story done.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given:

const ObjectIdTypeRegex = /objectid/i;

Shouldn't ObjectIdTypeRegex.test() take care of things?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should do it. Can we add a test where we specify the type as dataType: 'objectid'?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't ObjectIdTypeRegex.test() take care of things?

What will happen when the value is not an ObjectID string? IIUC, in your proposal the query will return no records, because the non-ObjectID string will not match any of the ObjectID values stored in the database.

Are we fine with that behavior?

I was thinking that we should reject such requests with 400 Bad Request error instead.

Also, what happens when strictObjectIDCoercion is enabled and the property is defined as {type: 'string', mongodb: {dataType: 'ObjectID'}}. Will we correctly coerce the string value to ObjectID, so that it can match any existing values in the database?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got your point, I was thinking of something else.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't ObjectIdTypeRegex.test() take care of things?

What will happen when the value is not an ObjectID string? IIUC, in your proposal the query will return no records, because the non-ObjectID string will not match any of the ObjectID values stored in the database.

Are we fine with that behavior?

I was thinking that we should reject such requests with 400 Bad Request error instead.

Also, what happens when strictObjectIDCoercion is enabled and the property is defined as {type: 'string', mongodb: {dataType: 'ObjectID'}}. Will we correctly coerce the string value to ObjectID, so that it can match any existing values in the database?

I misunderstood this, sorry. Rejecting non-objectID string makes sense to me (I thought this was the behaviour). Also, for the case when strict coercion flag is enabled, we should coerce string values to ObjectID, and also reject/throw an error if we can't (invalid values).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bajtos @hacksparrow is this something we can create a follow-up story on?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's create a follow up story.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@b-admike @hacksparrow @bajtos
Created #545 as I believe it relates to this conversation. This commit and its related code appears to fix the issue related to string-properties-stored-as-ObjectId but does not account for a property that is an Array of ObjectID or Array of ObjectID-like-strings.

lib/mongodb.js Outdated Show resolved Hide resolved
lib/mongodb.js Outdated Show resolved Hide resolved
lib/mongodb.js Show resolved Hide resolved
lib/mongodb.js Show resolved Hide resolved
@jannyHou
Copy link
Contributor

If both models are backed by MongoDB, I think we need to ensure that categoryId property is always set to ObjectID value, even if the caller provided a string only (e.g. because categoryId was obtained from HTTP request path like /api/categories/{some-objectid-string}/products.

@bajtos Thank you for the detailed explanation 👍 I forgot the relation constraint. Fair enough.

Copy link
Contributor

@b-admike b-admike left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great. Thanks for adding some docs in the README. I've put my thoughts on the ongoing discussions.

if (isDecimal) {
cond = Decimal128.fromString(cond);
debug('buildWhere decimal value: %s, constructor name: %s', cond, cond.constructor.name);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should do it. Can we add a test where we specify the type as dataType: 'objectid'?

test/objectid.test.js Outdated Show resolved Hide resolved
test/id.test.js Outdated

it('should throw if id is not a ObjectID-like string', async function() {
// Why is this not working?
// await User.create({id: 'abc', name: 'John'}).should.be.rejectedWith(/not an ObjectID string/);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are two ways of doing it (one is for expect/chai, one for mocha):
expect:
https://github.com/strongloop/loopback-next/blob/75939a4a144b3068ff2890394c178faad58d7458/packages/cli/test/integration/generators/relation.integration.js#L39-L48

Mocha:
https://github.com/strongloop/loopback-datasource-juggler/blob/91ab3e916281e56e8675889ab7c5a31dc223c22c/test/datasource.test.js#L446-L451

I suggest trying to have a promise based test:

it('should throw if id is not an ObjectID-like string', () => {

  return user.create({id: 'abc', name: 'John'}).should.be.rejectedWith(/not an ObjectID string/);
});

Copy link
Contributor Author

@hacksparrow hacksparrow May 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return user.create({id: 'abc', name: 'John'}).should.be.rejectedWith(/not an ObjectID string/);

Doesn't help.

Would it be ok to replace should with expect in this repo? Will do so in a separate PR after this one lands, if ok.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe my example is wrong. I didn't get to try it out. I'll try it out and let you know. In terms of replacing should with expect, it is ok, but not sure how much effort that is and how much beneficial it is.

lib/mongodb.js Outdated Show resolved Hide resolved
lib/mongodb.js Show resolved Hide resolved
@hacksparrow hacksparrow force-pushed the feat/isObjectID branch 3 times, most recently from 6fb8e55 to 9114a18 Compare May 31, 2019 08:53
Copy link
Member

@bajtos bajtos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the most important problems were addressed by now, but I don't have enough bandwidth to review the changes in full detail.

Please work with @b-admike and @jannyHou to get their approval, they have much better understanding of this codebase and MongoDB features than I do.

if (isDecimal) {
cond = Decimal128.fromString(cond);
debug('buildWhere decimal value: %s, constructor name: %s', cond, cond.constructor.name);
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't ObjectIdTypeRegex.test() take care of things?

What will happen when the value is not an ObjectID string? IIUC, in your proposal the query will return no records, because the non-ObjectID string will not match any of the ObjectID values stored in the database.

Are we fine with that behavior?

I was thinking that we should reject such requests with 400 Bad Request error instead.

Also, what happens when strictObjectIDCoercion is enabled and the property is defined as {type: 'string', mongodb: {dataType: 'ObjectID'}}. Will we correctly coerce the string value to ObjectID, so that it can match any existing values in the database?

lib/mongodb.js Show resolved Hide resolved
lib/mongodb.js Show resolved Hide resolved
Copy link
Contributor

@b-admike b-admike left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes LGTM 👍. I think there are two discussions where we can create follow-up tasks/PRs on.

@hacksparrow
Copy link
Contributor Author

@hacksparrow
Copy link
Contributor Author

Ok, all green now. Let's land this today.

Adds mongodb.dataType to model property definition

Feedback
Copy link
Contributor

@b-admike b-admike left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🎉

Copy link
Contributor

@jannyHou jannyHou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏 LGTM great effort!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants