Skip to content

Refactor and rework http coercion.#231

Merged
ritch merged 2 commits into
strongloop:masterfrom
STRML:reworkHTTPCoercion
Sep 18, 2015
Merged

Refactor and rework http coercion.#231
ritch merged 2 commits into
strongloop:masterfrom
STRML:reworkHTTPCoercion

Conversation

@STRML

@STRML STRML commented Aug 4, 2015

Copy link
Copy Markdown
Member

This fixes a number of subtle bugs and restricts "sloppy"
argument coercion (e.g. 'true' to the bool true) to string-only
HTTP datasources like querystrings and headers.

@raymondfeng

Copy link
Copy Markdown
Member

@STRML Thanks for the patch!

@STRML

STRML commented Aug 4, 2015

Copy link
Copy Markdown
Member Author

Just wanted to note that there are some breaking changes here:

  • application/json bodies are no longer being sloppily coerced (so 'true' no longer coerces to true), since they can encode some proper type information.
  • null/undefined is not automatically coerced, so null no longer coerces to 0 on a 'number' argument, or 'null' on a 'string' argument (this bit me hard in a JSON body today).

Comment thread lib/dynamic.js Outdated

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I should probably expand the comment here - I skip 'array' so that the 'array' converter has a chance to coerce null/undefined to [].

@STRML

STRML commented Aug 4, 2015

Copy link
Copy Markdown
Member Author

I'll squash when ready - open to discussion on this.

Comment thread test/http-context.test.js

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This breaks backwards compat. I understand why this change makes sense, but it also makes things like this impossible in loopback:

GET /todos?filter[where][done]=true

Obviously we need a bit more sophisticated type declaration support... but when you don't even know the structure (eg. a nested property of an object) it gets a bit tricky. I'm open to suggestions to overhaul this type system if it means better support for dynamicly typed queries in URLs. Maybe there is some syntax out there that caries more type information?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is only with JSON request bodies, not with querystrings.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see... you switched up givenMethodExpectArg to send JSON.

@ritch

ritch commented Sep 15, 2015

Copy link
Copy Markdown
Member

I think this might be a good basis for strong-remoting v3.0.0. I'd like to be careful and clearly document the breaking changes instead of trying to preserve the old behavior (any sort of type inference) in some unpredictable way.

We must support something capable of these type of queries though:

GET /todos?filter[where][priority][gt]=100&filter[where][done]=false

Right now we do this by inferring the type information. This makes the framework a bit magic, and gives less control to the consumer of the API as well producer. You do have the ability to specify explicit types, but that only works in the obvious cases. So in summary I welcome the changes to type inference. I am also in favor of just removing it entirely.

But lets not give up on the simplicity of the query syntax, because the flaw of the implementation. Maybe there is a nice syntax / notation / encoding out there that:

  • easily fits into URLs
  • supports explicit typing / doesn't require type inference
  • works well with the browser / curl / and other tools (eg. doesn't get converted into something un-readable by humans)

A couple that come close:

  • ejson or json - don't play nicely in URLs
  • our current approach - no explicit type support (like JSON)

@STRML @bajtos thoughts?

@ritch

ritch commented Sep 15, 2015

Copy link
Copy Markdown
Member

Here is some food for thought that would extend our current syntax with JSON literals:

GET /todos?filter[where][priority][gt]=100&filter[where][done]=false

Would become...

GET /todos?filter[where][priority][gt]=`100`&filter[where][done]=`false`

This would parse to:

{
  where: {
    priority: { gt: 100 },
    done: false
  }
}

We could also limit these values to a subset of JSON to avoid JSON.parse() overhead. Some examples off the top of my head:

  • 100
  • 0.1
  • true
  • false
  • Date(str) - "str" would also match a pattern before being parsed
  • null
  • undefined

@STRML

STRML commented Sep 15, 2015

Copy link
Copy Markdown
Member Author

@ritch See https://github.com/strongloop/strong-remoting/pull/231/files#diff-d54d77feb22761cf4ef39576b7edd949R118, I am handling sloppy coercion when no type data is available (formData and querystring).

@ritch

ritch commented Sep 15, 2015

Copy link
Copy Markdown
Member

Yes I like what you've done... but we are trying to preserve behavior that we should probably just get rid of (sloppy coercion). What if I had the unfortunate name: "Mr. False Undefined Null"?

GET /people?filter[where][firstName]=false

I don't like the fact that as the API consumer I cannot specify that I mean the literal string "false". Maybe its a silly example, but its something I'd like to get rid of. The type inference / sloppy coercion was an attempt to fix a problem with the query string syntax but from the wrong way.

application/json bodies are no longer being sloppily coerced (so 'true' no longer coerces to true), since they can encode some proper type information.

I think we can land/release this as a minor version.

null/undefined is not automatically coerced, so null no longer coerces to 0 on a 'number' argument, or 'null' on a 'string' argument (this bit me hard in a JSON body today).

If this is limited to JSON, we can land/release also as a minor version. When the input is JSON we should not infer any type that JSON can natively express. AFAICT your patch makes this change.

Anyways, we can discuss removing type inference in another issue.

Comment thread lib/dynamic.js

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we avoid this closure?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Not if we're going to pass ctx to converter - and the anon func syntax is still quite a bit faster than bind. It's not async, no chance of a leak here because of vars bound outside the closure.

@STRML

STRML commented Sep 15, 2015

Copy link
Copy Markdown
Member Author

Yes, this is an unfortunate problem with querystring syntax.

This PR is designed to be merged as a minor release as the only behavioral changes it makes should be considered bugs / undesirable behavior.

Unfortunately I don't have a good answer for how to do nice querystring syntax with support for nested attributes and some semblance of type. I don't think we should invent one ourselves, though.

In general, if the API author properly creates models (say, filter could be an actual user model), this won't be a problem. Otherwise, the author should know that if the input is a deep object in a GET param with no provided type information, he should coerce it to the needed type.

Comment thread test/http-context.test.js

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ah... missed this. Makes sense.

@ritch

ritch commented Sep 15, 2015

Copy link
Copy Markdown
Member

I've done a second pass review and I don't see anything preventing us from merging this. Though I am a bit nervous that with this large amount of changes to tests that something was missed. @STRML @bajtos any ideas on this? In theory, since this build is green we shouldn't have to worry about loopback tests failing after merging this.

@STRML have you run any LoopBack apps / automated tests with this version?

@STRML

STRML commented Sep 15, 2015

Copy link
Copy Markdown
Member Author

At this time I have not run any tests outside of the suite with this version.

But I am looking forward to the merge so I can remove this code:

      // FIXME why is strong-remoting sending 'null'???
      if (pgpPubKey == null || pgpPubKey === 'null' || pgpPubKey === user.pgpPubKey) return;

@ritch

ritch commented Sep 15, 2015

Copy link
Copy Markdown
Member

@slnode test please

@ritch

ritch commented Sep 15, 2015

Copy link
Copy Markdown
Member

At this time I have not run any tests outside of the suite with this version.

OK. I'll run it against some apps to get some more confidence in the BC.

@ritch

ritch commented Sep 15, 2015

Copy link
Copy Markdown
Member

Looks like the build failed. I don't think it has to do with these changes though. Something weird is going on with json-rpc.

@ritch

ritch commented Sep 16, 2015

Copy link
Copy Markdown
Member

@slnode test please

@rmg

rmg commented Sep 16, 2015

Copy link
Copy Markdown
Member

Branch needs to be rebased to pull in the fix from #243 that makes our CI green.

This fixes a number of subtle bugs and restricts "sloppy"
argument coercion (e.g. 'true' to the bool true) to string-only
HTTP datasources like querystrings and headers.

Fixes strongloop#223 (coerced Number past MAX_SAFE_INTEGER)
Possible fix for strongloop#208
@STRML
STRML force-pushed the reworkHTTPCoercion branch from 3671c44 to b1037d1 Compare September 16, 2015 17:42
@STRML

STRML commented Sep 16, 2015

Copy link
Copy Markdown
Member Author

Rebased.

@ritch

ritch commented Sep 16, 2015

Copy link
Copy Markdown
Member

@STRML thanks for the PR... this is really great.

I'll try and land this today. Need to ensure the top down build is all passing and a couple of examples are failing for unrelated reasons.

@rmg

rmg commented Sep 16, 2015

Copy link
Copy Markdown
Member

@slnode test please

@rmg

rmg commented Sep 16, 2015

Copy link
Copy Markdown
Member

@STRML don't be alarmed by the large number of failures - it's a result of me adding an additional level of dependencies to the CI mix and a lot of those modules were added only recently and are still lacking real tests.

@bajtos

bajtos commented Sep 17, 2015

Copy link
Copy Markdown
Member

@ritch I don't have a better solution for nice query strings, I am fine with landing this patch as an incremental (non-breaking) enhancement. As for unit-tests, it seems to me that most existing tests remain unchanged and this patch is adding more new tests. I guess it would be nice to run the new tests against the old (current) implementation, to see if there are any unanticipated changes?

@ritch

ritch commented Sep 17, 2015

Copy link
Copy Markdown
Member

@slnode test please

ritch added a commit that referenced this pull request Sep 18, 2015
Refactor and rework http coercion.
@ritch
ritch merged commit 5fdf291 into strongloop:master Sep 18, 2015
@STRML

STRML commented Oct 17, 2015

Copy link
Copy Markdown
Member Author

Did this get rebased out of master and force-pushed somehow?

@STRML

STRML commented Oct 17, 2015

Copy link
Copy Markdown
Member Author

Additionally, there's more that I'd like to add to this in another PR - for example, null in POST bodies is incorrectly coerced to 0 when the type is number, which feels almost certainly incorrect.

@STRML

STRML commented Dec 4, 2015

Copy link
Copy Markdown
Member Author

@bajtos What happened here?

I just got snagged by a bug because null was being coerced to 'null'. Why was this rebased out of master without any notification?

@bajtos

bajtos commented Dec 4, 2015

Copy link
Copy Markdown
Member

This is weird, I have no idea why 5fdf291 is no longer on the master. @ritch @raymondfeng any ideas?

@STRML

STRML commented Dec 9, 2015

Copy link
Copy Markdown
Member Author

@bajtos Why does it take almost two months to get an answer as to why a commit was rebased out and force pushed to loopback's most important dependency?

I understand there are some bugs - working through them now - but notification + a revert (and/or new failing tests) would be the correct way to do it, simply rebasing the commit out without notice is unacceptable.

@STRML STRML mentioned this pull request Dec 9, 2015
@bajtos

bajtos commented Dec 14, 2015

Copy link
Copy Markdown
Member

@STRML it looks like somebody has accidentally run git push -f on master and remove the merge commit which landed this patch. I have carefully reviewed the current git history on master against the list of pull requests and parent commits, I believe this is the only patch that was lost.

I opened a new PR #265 to verify the patch on CI before landing it again.

To prevent this situation from happening in the future, I have configured master and 1.x branches as protected (docs), which means GitHub will reject git push -f from now on.

I apologise for the troubles.

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.

Strings which are only numbers causes issues.

6 participants