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

fix: ApolloSchemaDownloadConfiguration HTTP headers json #2811

Merged
merged 4 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 37 additions & 7 deletions Sources/ApolloCodegenLib/ApolloSchemaDownloadConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,12 @@ public struct ApolloSchemaDownloadConfiguration: Equatable, Codable {
}

}


/// An HTTP header that will be sent in the schema download request.
public struct HTTPHeader: Equatable, CustomDebugStringConvertible, Codable {
/// The name of the header field. HTTP header field names are case insensitive.
let key: String
/// The value for the header field.
let value: String

public var debugDescription: String {
Expand All @@ -130,14 +133,17 @@ public struct ApolloSchemaDownloadConfiguration: Equatable, Codable {
}
}

/// Dictionary used to extract header fields without needing the HTTPHeader "key" and "value" keys.
private typealias HTTPHeaderDictionary = [String: String]

// MARK: - Properties

/// How to download your schema. Supports the Apollo Registry and GraphQL Introspection methods.
public let downloadMethod: DownloadMethod
/// The maximum time (in seconds) to wait before indicating that the download timed out.
/// Defaults to 30 seconds.
public let downloadTimeout: Double
/// Any additional headers to include when retrieving your schema. Defaults to nil.
/// Any additional HTTP headers to include when retrieving your schema. Defaults to nil.
public let headers: [HTTPHeader]
/// The local path where the downloaded schema should be written to.
public let outputPath: String
Expand All @@ -155,7 +161,7 @@ public struct ApolloSchemaDownloadConfiguration: Equatable, Codable {
/// - downloadMethod: How to download your schema.
/// - downloadTimeout: The maximum time (in seconds) to wait before indicating that the
/// download timed out. Defaults to 30 seconds.
/// - headers: [optional] Any additional headers to include when retrieving your schema.
/// - headers: [optional] Any additional HTTP headers to include when retrieving your schema.
/// Defaults to nil
/// - outputPath: The local path where the downloaded schema should be written to.
public init(
Expand Down Expand Up @@ -190,10 +196,34 @@ public struct ApolloSchemaDownloadConfiguration: Equatable, Codable {
forKey: .downloadTimeout
) ?? Default.downloadTimeout

self.headers = try container.decodeIfPresent(
[HTTPHeader].self,
forKey: .headers
) ?? Default.headers
self.headers = try Self.decode(headers: container) ?? Default.headers
}

private static func decode(
headers container: KeyedDecodingContainer<CodingKeys>
) throws -> [HTTPHeader]? {

do {
let headers = try container.decodeIfPresent(
[HTTPHeader].self,
forKey: .headers
)
return headers

} catch {
do {
let headers = try container.decodeIfPresent(
HTTPHeaderDictionary.self,
forKey: .headers
)
return headers?
.sorted(by: { $0.key < $1.key })
.map({ HTTPHeader(key: $0, value: $1) })

} catch {
return nil
}
}
}

public var outputFormat: DownloadMethod.OutputFormat {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,27 @@ class ApolloSchemaDownloadConfigurationCodableTests: XCTestCase {

// MARK: - ApolloSchemaDownloadConfiguration Tests

enum MockApolloSchemaDownloadConfiguration {
static var decodedStruct: ApolloSchemaDownloadConfiguration {
.init(
using: .introspection(
endpointURL: URL(string: "http://server.com")!,
httpMethod: .POST,
outputFormat: .SDL,
includeDeprecatedInputValues: true),
timeout: 120,
headers: [],
outputPath: "ServerSchema.graphqls"
)
}
func test__encodeApolloSchemaDownloadConfiguration__givenAllParameters_shouldReturnJSON_withHTTPHeadersAsArrayOfStructs() throws {
// given
let subject = ApolloSchemaDownloadConfiguration(
using: .introspection(
endpointURL: URL(string: "http://server.com")!,
httpMethod: .POST,
outputFormat: .SDL,
includeDeprecatedInputValues: true),
timeout: 120,
headers: [
.init(key: "Accept-Encoding", value: "gzip"),
.init(key: "Authorization", value: "Bearer <token>")
],
outputPath: "ServerSchema.graphqls"
)

static var encodedJSON: String {
"""
// when
let encodedJSON = try testJSONEncoder.encode(subject)
let actual = encodedJSON.asString

let expected = """
{
"downloadMethod" : {
"introspection" : {
Expand All @@ -52,35 +57,125 @@ class ApolloSchemaDownloadConfigurationCodableTests: XCTestCase {
},
"downloadTimeout" : 120,
"headers" : [

{
"key" : "Accept-Encoding",
"value" : "gzip"
},
{
"key" : "Authorization",
"value" : "Bearer <token>"
}
],
"outputPath" : "ServerSchema.graphqls"
}
"""
}

// then
expect(actual).to(equal(expected))
}

func test__encodeApolloSchemaDownloadConfiguration__givenAllParameters_shouldReturnJSON() throws {
func test__decodeApolloSchemaDownloadConfiguration__givenAllParameters_withHTTPHeadersAsArrayOfStructs_shouldReturnStruct() throws {
// given
let subject = MockApolloSchemaDownloadConfiguration.decodedStruct
let subject = """
{
"downloadMethod" : {
"introspection" : {
"endpointURL" : "http://server.com",
"httpMethod" : {
"POST" : {

}
},
"includeDeprecatedInputValues" : true,
"outputFormat" : "SDL"
}
},
"downloadTimeout" : 120,
"headers" : [
{
"key" : "Accept-Encoding",
"value" : "gzip"
},
{
"key" : "Authorization",
"value" : "Bearer <token>"
}
],
"outputPath" : "ServerSchema.graphqls"
}
"""

// when
let encodedJSON = try testJSONEncoder.encode(subject)
let actual = encodedJSON.asString
let actual = try JSONDecoder().decode(
ApolloSchemaDownloadConfiguration.self,
from: subject.asData
)

let expected = ApolloSchemaDownloadConfiguration(
using: .introspection(
endpointURL: URL(string: "http://server.com")!,
httpMethod: .POST,
outputFormat: .SDL,
includeDeprecatedInputValues: true),
timeout: 120,
headers: [
.init(key: "Accept-Encoding", value: "gzip"),
.init(key: "Authorization", value: "Bearer <token>")
],
outputPath: "ServerSchema.graphqls"
)

// then
expect(actual).to(equal(MockApolloSchemaDownloadConfiguration.encodedJSON))
expect(actual).to(equal(expected))
}

func test__decodeApolloSchemaDownloadConfiguration__givenAllParameters_shouldReturnStruct() throws {
func test__decodeApolloSchemaDownloadConfiguration__givenAllParameters_withHTTPHeadersAsDictionary_shouldReturnStruct() throws {
// given
let subject = MockApolloSchemaDownloadConfiguration.encodedJSON.asData
let subject = """
{
"downloadMethod" : {
"introspection" : {
"endpointURL" : "http://server.com",
"httpMethod" : {
"POST" : {

}
},
"includeDeprecatedInputValues" : true,
"outputFormat" : "SDL"
}
},
"downloadTimeout" : 120,
"headers" : {
"Accept-Encoding" : "gzip",
"Authorization" : "Bearer <token>"
},
"outputPath" : "ServerSchema.graphqls"
}
"""

// when
let actual = try JSONDecoder().decode(ApolloSchemaDownloadConfiguration.self, from: subject)
let actual = try JSONDecoder().decode(
ApolloSchemaDownloadConfiguration.self,
from: subject.asData
)

let expected = ApolloSchemaDownloadConfiguration(
using: .introspection(
endpointURL: URL(string: "http://server.com")!,
httpMethod: .POST,
outputFormat: .SDL,
includeDeprecatedInputValues: true),
timeout: 120,
headers: [
.init(key: "Accept-Encoding", value: "gzip"),
.init(key: "Authorization", value: "Bearer <token>")
],
outputPath: "ServerSchema.graphqls"
)

// then
expect(actual).to(equal(MockApolloSchemaDownloadConfiguration.decodedStruct))
expect(actual).to(equal(expected))
}

func test__decodeApolloSchemaDownloadConfiguration__givenOnlyRequiredParameters_shouldReturnStruct() throws {
Expand Down