Skip to content
This repository has been archived by the owner on Nov 16, 2020. It is now read-only.

Add support for sorted keys. #9

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open

Add support for sorted keys. #9

wants to merge 3 commits into from

Conversation

jseibert
Copy link

Swift on macOS and Linux have different dictionary enumeration algorithms that lead to non-deterministic encoding output, which breaks testing.

Mimicking JSONSerialization (see https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/JSONSerialization.swift), we must offer support to sort dictionary keys in order to guarantee output consistency.

@jseibert
Copy link
Author

Note that I have not changed the default encoder implementation. This must be explicitly enable by overriding the contentConfig:

// Fix the key order for the standard URLEncodedFormEncoder
do {
    let encoder = URLEncodedFormEncoder()
    encoder.outputFormatting = .sortedKeys
    contentConfig.use(encoder: encoder, for: .urlEncodedForm)
    contentConfig.use(decoder: URLEncodedFormDecoder(), for: .urlEncodedForm)
}

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.

Changes LGTM, thanks! Just some gardening comments, plus:

  • test to verify key sorting works (also will help ensure API doesn't break)

@@ -11,6 +11,22 @@
/// See [Mozilla's](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) docs for more information about
/// url-encoded forms.
public final class URLEncodedFormEncoder: DataEncoder {
/// The formatting of the output data.
public struct OutputFormatting : OptionSet {
Copy link
Member

Choose a reason for hiding this comment

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

JSON coder calls it "writing options". We should probably follow suit.

Copy link
Author

Choose a reason for hiding this comment

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

Maybe we're looking at different things? This code is copied from https://github.com/apple/swift/blob/master/stdlib/public/SDK/Foundation/JSONEncoder.swift where it is called:

/// The output format to produce. Defaults to `[]`.
open var outputFormatting: OutputFormatting = []

Copy link
Member

Choose a reason for hiding this comment

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

Small change here:

- OutputFormatting : OptionSet
+ OutputFormatting: OptionSet

static let `default` = URLEncodedFormSerializer()
static let `default` = URLEncodedFormSerializer(sortedKeys: false)

private var sortedKeys: Bool
Copy link
Member

Choose a reason for hiding this comment

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

Missing comment


for (key, val) in elements {
let key = try key.urlEncodedFormEncoded()
let subdata = try serialize(val, forKey: key)
Copy link
Member

Choose a reason for hiding this comment

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

Any way we can reduce code dupe here?

@tanner0101 tanner0101 added the enhancement New feature or request label May 30, 2018
@tanner0101 tanner0101 self-assigned this May 30, 2018
@jseibert
Copy link
Author

@tanner0101 ok should be good to go!

@jseibert
Copy link
Author

@tanner0101 any chance this can ship?

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.

Sorry for the long delay on this. We should try to get it into 1.1.0 tag for this package. Just needs some small code style changes and it will be good to go.


/// Serializes the data.
func serialize(_ URLEncodedFormEncoded: [String: URLEncodedFormData]) throws -> Data {
var data: [Data] = []
for (key, val) in URLEncodedFormEncoded {
var elements: [(key: String, value: URLEncodedFormData)]!
Copy link
Member

Choose a reason for hiding this comment

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

We should be able to avoid using an IUO here since the compiler can be sure we set elements w/ the following if / else.

}

/// The output format to produce. Defaults to `[]`.
public var outputFormatting: OutputFormatting = []
Copy link
Member

Choose a reason for hiding this comment

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

Vapor style is to set all properties in the init method. We try to avoid default properties on members since they can be easy to miss when refactoring.

@@ -11,6 +11,22 @@
/// See [Mozilla's](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) docs for more information about
/// url-encoded forms.
public final class URLEncodedFormEncoder: DataEncoder {
/// The formatting of the output data.
public struct OutputFormatting : OptionSet {
Copy link
Member

Choose a reason for hiding this comment

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

Small change here:

- OutputFormatting : OptionSet
+ OutputFormatting: OptionSet

@@ -32,10 +48,21 @@ final class URLEncodedFormSerializer {

/// Serializes a `[String: URLEncodedFormData]` at a given key.
private func serialize(_ dictionary: [String: URLEncodedFormData], forKey key: Data) throws -> Data {
let values = try dictionary.map { subKey, value -> Data in
var elements: [(key: String, value: URLEncodedFormData)]!
Copy link
Member

Choose a reason for hiding this comment

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

We should be able to remove the IUO here as well.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants