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

Parameter Encoding Tests #2

Merged
merged 11 commits into from
Nov 16, 2018
Merged

Parameter Encoding Tests #2

merged 11 commits into from
Nov 16, 2018

Conversation

brendanlensink
Copy link
Collaborator

@nbrooke

Split out the parameter encoding and url qualifying into separate functions and added some unit tests. A couple questions:

  1. Is this useful? I understand the tests are pretty basic right now but I think a reasonable starting point?
  2. Did I miss any obvious tests?
  3. Does this try/catch pattern make sense? Is there a better way?

* master:
  fixed request throwing weird wrapped errors
  fix error in encoding logic
  update podspec
  bump version
  remove todos, update project settings that were throwing a warning
Copy link
Member

@nbrooke nbrooke 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 this is a pretty good starting point for the tests, but I do have some stuff I think could be improved with the parameter encoding and error handling (which I don't think had been reviewed yet)

SCNetworkAPI/Source/SCNetworkAPI.swift Outdated Show resolved Hide resolved
SCNetworkAPI/Source/SCNetworkAPI.swift Outdated Show resolved Hide resolved
case .get:
guard let url = url,
var components = URLComponents(url: url, resolvingAgainstBaseURL: true),
let paramsDict = request.parameters as? [String: Codable] else {
Copy link
Member

Choose a reason for hiding this comment

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

I don't like the hidden requirement for the parameters to be a dictionary. Even though that's definitely the most common case, I think ideally you should be able to do something like this:

struct MyParams: Codable {
    var a: String
    var b: Int
}

struct MyRequest: Request {
    typealias Parameters = MyParams
    typealias Returning = /*...*/

    public var method: HTTPMethod { return .get }

    public var path: String {
        return "somewhere"
    }

    public var params: Codable {
        return MyParams(a: "foo", b: 2)
    }
}

and have the url generate to somewhere?a=foo&b=2, which this approach prevents. We're gonna have to do some runtime checking here (because I don't think we can reasonably support passing an array, anything with more than one level of hierarchy, or anything that is a single value container encodable to the URL parameter encoding), but I think this it too strict.

Implementing that is a bit of a pain though, there's no way to Encoder something straight to a dictionary's so I believe right now we need to encode it to JSON and the decode the JSON to a dictionary, which kinda sucks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Got the other changes in and made some progress on this, but I'm running in to some trouble checking for params that are arrays or nested dictionaries and catching them before it tries to encode them.

I've committed what I've got, but it basically comes down to Array != Array. It doesn't seem right, or feasible for that matter, that I would have to check for each different type of Array. Am I missing something obvious here?

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 the trick is to do the validation after the encode rather than before. Instead of trying to prove that the original types follow the rules, just encode it anyway, and then verify that a) you get a dictionary and b) all of the members are just strings/numbers.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Lets try this again

SCNetworkAPI/Source/URLRequest+EncodeParameters.swift Outdated Show resolved Hide resolved
SCNetworkAPI/Source/SCNetworkAPI.swift Outdated Show resolved Hide resolved
@nbrooke nbrooke assigned brendanlensink and unassigned nbrooke Nov 2, 2018
simplified some try/catch logic
add unknownError type
switch requestFailed error type from String to Error
moved NetworkAPIError: Equatable into tests, fixed tests
add anyDecodableType to remove restriction on GET params being [String: String]
changed passing an empty object for get param encoding from an error to just carrying on
simplifyed some try/catch blocks.
let message = "Decoding error: \(error)"
debugLogError(message)
guard let networkError = error as? NetworkAPIError else {
completion(.failure(.codingError(message)))
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 this should also be a wrapped error rather than as string?

let jsonEncodedParams = try JSONEncoder().encode(request.parameters)
guard let url = url,
var components = URLComponents(url: url, resolvingAgainstBaseURL: true),
let paramsDict = try JSONDecoder().decode(AnyDecodable.self, from: jsonEncodedParams).value as? [String: Any] else {
Copy link
Member

Choose a reason for hiding this comment

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

Two other things related to this:

  • As we were talking about this is encode to text then decode loop is probably better than the alternatives (including a large dependancy just to get dictionary encoding), but it is pretty crummy, and should probably a) be commented b) have a bug filed to switch to encode-to-dictionary once the standard library supports it (not sure if that's on tap for the Swift 5 timeframe, I know there were some discussions about it but couldn't find them in a quick swift-evolution search).

  • This might be cleaner, and not need AnyDecodable, if it did the decoding with NSJSONSerialization rather than JSONDecoder.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Created #3 to address this

@nbrooke nbrooke assigned brendanlensink and unassigned nbrooke Nov 9, 2018
…of unpermitted types

Add some more tests, refined current tests
removed unused debugging prints
…of unpermitted types

Add some more tests, refined current tests
removed unused debugging prints
…into bwl/testing

* 'bwl/testing' of https://github.com/steamclock/networkAPI:
  Fixed get param encoding to use NSJSONSerialization, better handling of unpermitted types Add some more tests, refined current tests removed unused debugging prints
@nbrooke nbrooke assigned brendanlensink and unassigned nbrooke Nov 16, 2018
@brendanlensink brendanlensink merged commit 190c085 into master Nov 16, 2018
@brendanlensink brendanlensink deleted the bwl/testing branch November 16, 2018 18:12
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.

2 participants