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

Better Request Parsing Parameters #331

Merged
merged 3 commits into from Oct 27, 2017
Merged

Conversation

drujensen
Copy link
Member

@drujensen drujensen commented Oct 26, 2017

Edited by @eliasjpr

Description of the Change

The framework should be able to parse JSON request body appropriately by
returning a JSON::Any object.

Currently, there is a bug when trying to parse JSON parameters from the
body of a request where nested JSON payload is not being parsed correctly

{ "test": "test", "address": { "city": "New York" }
params["address"].as(Hash)["city"]

Params is now a custom Hash of the following signature
Hash(String | Symbol, Amber::Router::ParamsType) or
Amber::Router::ParamsHash.new

To give more context all parameters where being parsed using the
HTTP::Params.parse method which returns an object of type
Hash(String, Array(String)) this caused issues when parsing
JSON params from the body, It turns out that nested JSON keys where
being parsed as a string. Basically, all params values are currently
returning as a string.

With the changes presented in this PR users can now define model objects
with JSON mapping that should be evaluated correctly based on the mapping
defined.

Controller
The controller now generates the app using params.validation similar to
Rails strong params. It adds a method to the Controller and overloads the
Granite initializer and set_attributes methods.

private def animal_params
  params.validation do
    required(:name) { |v| v.str? & !v.empty? }
    required(:last_name, "Last name is required")
    optional(:phone)  
  end
end

# Validate! returns a hash of only the specified params above.
animal = Animal.new(animal_params.validate!)

Alternate Designs

It would be nice to specify a type that can be cast automatically

required(:last_name, :string, "Last name is required") do
...validation code here...
end

Benefits

Possible Drawbacks

-Fear something can break

Example app using this branch

https://github.com/eliasjpr/params-test-app

Issue: #310

The framework should be able to parse JSON request body appropriately by
returning a `JSON::Any` object.

Currently, there is a bug when trying to parse JSON parameters from the
body of a request where nested JSON payload is not being parsed correctly

```crystal
{ "test": "test", "address": { "city": "New York" }
params["address"].as(Hash)["city"]
```
Params is now a custom Hash of the following signature
`Hash(String | Symbol, Amber::Router::ParamsType)` or
`Amber::Router::ParamsHash.new`

To give more context all parameters where being parsed using the
**HTTP::Params.parse** method which returns an object of type
**Hash(String, Array(String))** this caused issues when parsing
JSON params from the body, It turns out that nested JSON keys where
being parsed as a string. Basically, all `params` values are currently
returning as a string.

With the changes presented in this PR users can now define model objects
with JSON mapping that should be evaluated correctly based on the mapping
defined. So for instance:

```crystal
params["user_id"].as(Int64) => 123
params["address"].as(Hash).each ...
params["user"].as(User) => User.name
User.from_json(params["user"]) => User.name
```
**Controller**
The controller now generates the app using params.validation similar to
Rails strong params. It adds a method to the Controller and overloads the
Granite initializer and set_attributes methods.

```crystal
private def animal_params
  params.validation do
    required("name") { |v| v.str? & !v.empty? }
  end
end
animal = Animal.new(animal_params.validate!)
```

Model
Models generate for the Granite::ORM now includes the following line include
Amber::Models::Granite this will allow models to be populated with the controller
params using the params.validations. The reason this is added here and not to the
Granite::ORM repo is to keep Granite loosely coupled from any framework since the
params.validation is specific to amber.

```crystal
class Animal < Granite::ORM
  include Amber::Models::Granite
end
```
Copy link
Contributor

@faustinoaq faustinoaq left a comment

Choose a reason for hiding this comment

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

Looks good, 👍

@@ -25,7 +25,7 @@ module Amber::CLI
@timestamp = Time.now.to_s("%Y%m%d%H%M%S")
@primary_key = primary_key
@visible_fields = @fields.reject(&.hidden).map(&.name)

add_routes :web, <<-ROUTES
get "/signin", SessionController, :new
post "/session", SessionController, :create
Copy link
Contributor

Choose a reason for hiding this comment

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

Indentation issue

@eliasjpr eliasjpr merged commit f5d3106 into master Oct 27, 2017
@eliasjpr eliasjpr deleted the ep/fixes-params-json-parsing branch October 27, 2017 10:53
marksiemers pushed a commit to marksiemers/amber that referenced this pull request Oct 28, 2017
* Better Request Parsing Parameters

Issue: amberframework#310

The framework should be able to parse JSON request body appropriately by
returning a `JSON::Any` object.

Currently, there is a bug when trying to parse JSON parameters from the
body of a request where nested JSON payload is not being parsed correctly

```crystal
{ "test": "test", "address": { "city": "New York" }
params["address"].as(Hash)["city"]
```
Params is now a custom Hash of the following signature
`Hash(String | Symbol, Amber::Router::ParamsType)` or
`Amber::Router::ParamsHash.new`

To give more context all parameters where being parsed using the
**HTTP::Params.parse** method which returns an object of type
**Hash(String, Array(String))** this caused issues when parsing
JSON params from the body, It turns out that nested JSON keys where
being parsed as a string. Basically, all `params` values are currently
returning as a string.

With the changes presented in this PR users can now define model objects
with JSON mapping that should be evaluated correctly based on the mapping
defined. So for instance:

```crystal
params["user_id"].as(Int64) => 123
params["address"].as(Hash).each ...
params["user"].as(User) => User.name
User.from_json(params["user"]) => User.name
```
**Controller**
The controller now generates the app using params.validation similar to
Rails strong params. It adds a method to the Controller and overloads the
Granite initializer and set_attributes methods.

```crystal
private def animal_params
  params.validation do
    required("name") { |v| v.str? & !v.empty? }
  end
end
animal = Animal.new(animal_params.validate!)
```

Model
Models generate for the Granite::ORM now includes the following line include
Amber::Models::Granite this will allow models to be populated with the controller
params using the params.validations. The reason this is added here and not to the
Granite::ORM repo is to keep Granite loosely coupled from any framework since the
params.validation is specific to amber.

```crystal
class Animal < Granite::ORM
  include Amber::Models::Granite
end
```

* fixup! Better Request Parsing Parameters
elorest pushed a commit that referenced this pull request Nov 17, 2017
* Better Request Parsing Parameters

Issue: #310

The framework should be able to parse JSON request body appropriately by
returning a `JSON::Any` object.

Currently, there is a bug when trying to parse JSON parameters from the
body of a request where nested JSON payload is not being parsed correctly

```crystal
{ "test": "test", "address": { "city": "New York" }
params["address"].as(Hash)["city"]
```
Params is now a custom Hash of the following signature
`Hash(String | Symbol, Amber::Router::ParamsType)` or
`Amber::Router::ParamsHash.new`

To give more context all parameters where being parsed using the
**HTTP::Params.parse** method which returns an object of type
**Hash(String, Array(String))** this caused issues when parsing
JSON params from the body, It turns out that nested JSON keys where
being parsed as a string. Basically, all `params` values are currently
returning as a string.

With the changes presented in this PR users can now define model objects
with JSON mapping that should be evaluated correctly based on the mapping
defined. So for instance:

```crystal
params["user_id"].as(Int64) => 123
params["address"].as(Hash).each ...
params["user"].as(User) => User.name
User.from_json(params["user"]) => User.name
```
**Controller**
The controller now generates the app using params.validation similar to
Rails strong params. It adds a method to the Controller and overloads the
Granite initializer and set_attributes methods.

```crystal
private def animal_params
  params.validation do
    required("name") { |v| v.str? & !v.empty? }
  end
end
animal = Animal.new(animal_params.validate!)
```

Model
Models generate for the Granite::ORM now includes the following line include
Amber::Models::Granite this will allow models to be populated with the controller
params using the params.validations. The reason this is added here and not to the
Granite::ORM repo is to keep Granite loosely coupled from any framework since the
params.validation is specific to amber.

```crystal
class Animal < Granite::ORM
  include Amber::Models::Granite
end
```

* fixup! Better Request Parsing Parameters
elorest pushed a commit that referenced this pull request Nov 17, 2017
* Better Request Parsing Parameters

Issue: #310

The framework should be able to parse JSON request body appropriately by
returning a `JSON::Any` object.

Currently, there is a bug when trying to parse JSON parameters from the
body of a request where nested JSON payload is not being parsed correctly

```crystal
{ "test": "test", "address": { "city": "New York" }
params["address"].as(Hash)["city"]
```
Params is now a custom Hash of the following signature
`Hash(String | Symbol, Amber::Router::ParamsType)` or
`Amber::Router::ParamsHash.new`

To give more context all parameters where being parsed using the
**HTTP::Params.parse** method which returns an object of type
**Hash(String, Array(String))** this caused issues when parsing
JSON params from the body, It turns out that nested JSON keys where
being parsed as a string. Basically, all `params` values are currently
returning as a string.

With the changes presented in this PR users can now define model objects
with JSON mapping that should be evaluated correctly based on the mapping
defined. So for instance:

```crystal
params["user_id"].as(Int64) => 123
params["address"].as(Hash).each ...
params["user"].as(User) => User.name
User.from_json(params["user"]) => User.name
```
**Controller**
The controller now generates the app using params.validation similar to
Rails strong params. It adds a method to the Controller and overloads the
Granite initializer and set_attributes methods.

```crystal
private def animal_params
  params.validation do
    required("name") { |v| v.str? & !v.empty? }
  end
end
animal = Animal.new(animal_params.validate!)
```

Model
Models generate for the Granite::ORM now includes the following line include
Amber::Models::Granite this will allow models to be populated with the controller
params using the params.validations. The reason this is added here and not to the
Granite::ORM repo is to keep Granite loosely coupled from any framework since the
params.validation is specific to amber.

```crystal
class Animal < Granite::ORM
  include Amber::Models::Granite
end
```

* fixup! Better Request Parsing Parameters

Former-commit-id: 9d9ccd6
@faustinoaq faustinoaq added this to Done in Framework 2018 May 5, 2018
@faustinoaq faustinoaq removed this from Done in Framework 2018 Jun 12, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

None yet

3 participants