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

Add require(_:) extension to Parameters. #2402

Merged
merged 5 commits into from
Jul 15, 2020

Conversation

ileitch
Copy link
Contributor

@ileitch ileitch commented Jun 18, 2020

Adds a require(_:) helper to Parameters, allowing for more succinct parameter handling. (#2402)

Previously the optional case needed to be handled explicitly:

guard let name = req.parameters.get("name") else {
    throw Abort(.internalServerError)
}

Now it can be written as:

let name = try req.parameters.require("name")

@ileitch
Copy link
Contributor Author

ileitch commented Jun 19, 2020

The default error is up for debate here, perhaps a 400 Bad Request would be more appropriate?

public extension Parameters {
/// Grabs the named parameter from the parameter bag.
/// An `Abort(.unprocessableEntity)` error is thrown if the parameter does not exist.
func require(_ name: String, error: Error = Abort(.unprocessableEntity)) throws -> String {
Copy link
Member

Choose a reason for hiding this comment

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

I'd prefer the label of the error to be or, so or error: Error.

This follows the style used by EventLoopFuture.unwrap seen here:

public func unwrap(or error: @autoclosure @escaping () -> Error) -> EventLoopFuture<Value.WrappedType> {

/// Grabs the named parameter from the parameter bag, casting it to
/// a `LosslessStringConvertible` type.
/// An `Abort(.unprocessableEntity)` error is thrown if the parameter does not exist.
func require<T>(_ name: String, as type: T.Type = T.self, error: Error = Abort(.unprocessableEntity)) throws -> T?
Copy link
Member

Choose a reason for hiding this comment

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

Same here

@Joannis
Copy link
Member

Joannis commented Jun 22, 2020

I believe .unprocessableEntity and .badRequest to be equally correct. I'd prefer the opinion of other people on this.

Copy link
Sponsor Member

@MrLotU MrLotU left a comment

Choose a reason for hiding this comment

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

Agree with @Joannis' comments. As for the status code, IMO, a plain .badRequest is a catchall for all 4xx codes if you can't find a better suited subcode, which in this case we can. So in my opinion the choice would be between .unprocessableEntity and .notFound. I would lean to a .notFound since there is an error with the actual location of the request, and not so much the entity/body sent along with the request, which .unprocessableEntity is more geared towards

Tests/VaporTests/RouteTests.swift Show resolved Hide resolved
@ileitch
Copy link
Contributor Author

ileitch commented Jun 24, 2020

One point in favor of badRequest is that it's consistent with ValidationsError, which this feels somewhat similar to.

/// Grabs the named parameter from the parameter bag, casting it to
/// a `LosslessStringConvertible` type.
/// An `Abort(.unprocessableEntity)` error is thrown if the parameter does not exist.
func require<T>(_ name: String, as type: T.Type = T.self, or error: Error = Abort(.unprocessableEntity)) throws -> T?
Copy link
Member

Choose a reason for hiding this comment

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

Why does this return an optional? Doesn't that negate the whole use of using require?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@ileitch ileitch requested a review from 0xTim July 11, 2020 10:19
@tanner0101 tanner0101 added the enhancement New feature or request label Jul 14, 2020
@tanner0101 tanner0101 added this to Awaiting Review in Vapor 4 via automation Jul 14, 2020
@tanner0101 tanner0101 added the semver-minor Contains new API label Jul 14, 2020
Copy link
Member

@tanner0101 tanner0101 left a comment

Choose a reason for hiding this comment

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

+1, thanks!

Regarding error to be thrown, that's kind of a tricky one. There are two situations in which get<T> returns nil:

  • 1: The string cannot be converted to the desired type. 422 makes sense here as a default.
  • 2: There is no route parameter with that name. IMO this should always be a 500 since it's a developer error.

We could detect these two different failure modes by only using the Parameters.get method that returns a string. That will only ever return nil if the parameter does not exist (and thus the developer misconfigured the routes).

I don't think this is a requirement to merge, but it could be helpful for debugging misspelled parameter names.

@@ -0,0 +1,24 @@
public extension Parameters {
/// Grabs the named parameter from the parameter bag.
/// An `Abort(.unprocessableEntity)` error is thrown if the parameter does not exist.
Copy link
Member

Choose a reason for hiding this comment

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

Docblock should have a - parameters: list.

/// Grabs the named parameter from the parameter bag.
/// An `Abort(.unprocessableEntity)` error is thrown if the parameter does not exist.
func require(_ name: String, or error: Error = Abort(.unprocessableEntity)) throws -> String {
guard let value = get(name) else {
Copy link
Member

Choose a reason for hiding this comment

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

This should be able to call the method below, right?

@@ -0,0 +1,24 @@
public extension Parameters {
Copy link
Member

Choose a reason for hiding this comment

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

Please put public in front of each method instead of using the public extension syntax (Vapor style).

@tanner0101 tanner0101 moved this from Awaiting Review to Awaiting Updates in Vapor 4 Jul 14, 2020
@ileitch
Copy link
Contributor Author

ileitch commented Jul 15, 2020

@tanner0101 should the errors for both scenarios be configurable, or just hard-code them? Having two parameters feels a bit overkill, and there's precedent for hard-coded statuses, e.g in ValidationsError.

@ileitch
Copy link
Contributor Author

ileitch commented Jul 15, 2020

@tanner0101 went ahead and hard-coded the errors, since that feels the most natural to me.

Copy link
Sponsor Member

@MrLotU MrLotU left a comment

Choose a reason for hiding this comment

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

Overall LGTM, just 2 nits


/// Grabs the named parameter from the parameter bag, casting it to a `LosslessStringConvertible` type.
/// If the parameter does not exist, `Abort(.internalServerError)` is thrown.
/// If the parameter value cannot be converted to the generic type, `Abort(.unprocessableEntity)` is thrown.
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Would rename this to the required type

Suggested change
/// If the parameter value cannot be converted to the generic type, `Abort(.unprocessableEntity)` is thrown.
/// If the parameter value cannot be converted to the required type, `Abort(.unprocessableEntity)` is thrown.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@tanner0101
Copy link
Member

Thanks!

@tanner0101 tanner0101 changed the title Add require(_:error:) extension to Parameters. Add require(_:) extension to Parameters. Jul 15, 2020
@tanner0101 tanner0101 merged commit 867ce4a into vapor:master Jul 15, 2020
Vapor 4 automation moved this from Awaiting Updates to Done Jul 15, 2020
@tanner0101
Copy link
Member

These changes are now available in 4.20.0

@ileitch ileitch deleted the require-parameters branch July 15, 2020 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request semver-minor Contains new API
Projects
Vapor 4
  
Done
Development

Successfully merging this pull request may close these issues.

None yet

5 participants