Skip to content

Commit

Permalink
Make FieldMerging config use MergeStrategy internally and implement s…
Browse files Browse the repository at this point in the history
…tringly typed Codable usage for JSON encoding
  • Loading branch information
AnthonyMDev committed May 1, 2024
1 parent d4a2466 commit 86fec35
Show file tree
Hide file tree
Showing 2 changed files with 281 additions and 11 deletions.
189 changes: 189 additions & 0 deletions Tests/ApolloCodegenTests/ApolloCodegenConfigurationCodableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class ApolloCodegenConfigurationCodableTests: XCTestCase {
],
deprecatedEnumCases: .exclude,
schemaDocumentation: .exclude,
fieldMerging: .all,
cocoapodsCompatibleImportStatements: true,
warningsOnDeprecatedUsage: .exclude,
conversionStrategies:.init(
Expand Down Expand Up @@ -99,6 +100,9 @@ class ApolloCodegenConfigurationCodableTests: XCTestCase {
"inputObjects" : "none"
},
"deprecatedEnumCases" : "exclude",
"fieldMerging" : [
"all"
],
"markOperationDefinitionsAsFinal" : true,
"operationDocumentFormat" : [
"definition"
Expand Down Expand Up @@ -759,6 +763,191 @@ class ApolloCodegenConfigurationCodableTests: XCTestCase {
).to(throwError())
}

// MARK: - Field Merging Tests

func test__encode_fieldMerging__givenAll_shouldReturnAll() throws {
// given
let subject: ApolloCodegenConfiguration.FieldMerging = .all

let expected = """
[
"all"
]
"""

// when
let actual = try testJSONEncoder.encode(subject).asString

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

func test__decode_fieldMerging__givenAll_shouldReturnAll() throws {
// given
let subject = """
["all"]
""".asData

// when
let decoded = try JSONDecoder().decode(
ApolloCodegenConfiguration.FieldMerging.self,
from: subject
)

// then
expect(decoded).to(equal(.all))
}

func test__encode_fieldMerging__givenAncestorsOnly_shouldReturnAncestors() throws {
// given
let subject: ApolloCodegenConfiguration.FieldMerging = .ancestors

let expected = """
[
"ancestors"
]
"""

// when
let actual = try testJSONEncoder.encode(subject).asString

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

func test__decode_fieldMerging__givenAncestorsOnly_shouldReturnAncestors() throws {
// given
let subject = """
["ancestors"]
""".asData

// when
let decoded = try JSONDecoder().decode(
ApolloCodegenConfiguration.FieldMerging.self,
from: subject
)

// then
expect(decoded).to(equal(.ancestors))
}

func test__encode_fieldMerging__givenSiblingsOnly_shouldReturnSiblings() throws {
// given
let subject: ApolloCodegenConfiguration.FieldMerging = .siblings

let expected = """
[
"siblings"
]
"""

// when
let actual = try testJSONEncoder.encode(subject).asString

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

func test__decode_fieldMerging__givenSiblingsOnly_shouldReturnSiblings() throws {
// given
let subject = """
["siblings"]
""".asData

// when
let decoded = try JSONDecoder().decode(
ApolloCodegenConfiguration.FieldMerging.self,
from: subject
)

// then
expect(decoded).to(equal(.siblings))
}

func test__encode_fieldMerging__givenNamedFragmentsOnly_shouldReturnNamedFragments() throws {
// given
let subject: ApolloCodegenConfiguration.FieldMerging = .namedFragments

let expected = """
[
"namedFragments"
]
"""

// when
let actual = try testJSONEncoder.encode(subject).asString

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

func test__decode_fieldMerging__givenNamedFragmentsOnly_shouldReturnNamedFragments() throws {
// given
let subject = """
["namedFragments"]
""".asData

// when
let decoded = try JSONDecoder().decode(
ApolloCodegenConfiguration.FieldMerging.self,
from: subject
)

// then
expect(decoded).to(equal(.namedFragments))
}

func test__encode_fieldMerging__givenMultipleValues_shouldReturnGivenValues() throws {
// given
let subject: ApolloCodegenConfiguration.FieldMerging = [.ancestors, .namedFragments]

let expected = """
[
"ancestors",
"namedFragments"
]
"""

// when
let actual = try testJSONEncoder.encode(subject).asString

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

func test__decode_fieldMerging__givenMultipleValues_shouldReturnGivenValues() throws {
// given
let subject = """
["ancestors", "siblings"]
""".asData

// when
let decoded = try JSONDecoder().decode(
ApolloCodegenConfiguration.FieldMerging.self,
from: subject
)

// then
expect(decoded).to(equal([.ancestors, .siblings]))
}

func test__decode_fieldMerging__givenUnrecognizedValue_shouldThrowError() throws {
// given
let subject = """
["invalid"]
""".asData

// when
expect(
try JSONDecoder().decode(
ApolloCodegenConfiguration.FieldMerging.self,
from: subject
)
)
// then
.to(throwError(errorType: DecodingError.self))
}

// MARK: - APQConfig Tests

func encodedValue(_ case: ApolloCodegenConfiguration.APQConfig) -> String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -866,9 +866,6 @@ public struct ApolloCodegenConfiguration: Codable, Equatable {
/// different instances together to indicate all the types you would like to generate
/// initializers for.
public struct SelectionSetInitializers: Codable, Equatable, ExpressibleByArrayLiteral {
private var options: SelectionSetInitializers.Options
private var definitions: Set<String>

/// Option to generate initializers for all named fragments.
public static let namedFragments: SelectionSetInitializers = .init(.namedFragments)

Expand All @@ -895,6 +892,9 @@ public struct ApolloCodegenConfiguration: Codable, Equatable {
.init(definitionName: named)
}

private var options: SelectionSetInitializers.Options
private var definitions: Set<String>

/// Initializes a `SelectionSetInitializer` with an array of values.
public init(arrayLiteral elements: SelectionSetInitializers...) {
guard var options = elements.first else {
Expand Down Expand Up @@ -926,29 +926,41 @@ public struct ApolloCodegenConfiguration: Codable, Equatable {
///
/// By default, all possible fields and named fragment accessors are merged into each selection
/// set.
public struct FieldMerging: OptionSet, Codable, Equatable {
public struct FieldMerging: Codable, Equatable, ExpressibleByArrayLiteral {
/// Merges fields and fragment accessors from the selection set's direct ancestors.
public static let ancestors = FieldMerging(rawValue: 1 << 0)
public static let ancestors = FieldMerging(.ancestors)

/// Merges fields and fragment accessors from sibling inline fragments that match the selection
/// set's scope.
public static let siblings = FieldMerging(rawValue: 1 << 1)
public static let siblings = FieldMerging(.siblings)

/// Merges fields and fragment accessors from named fragments that have been spread into the
/// selection set.
public static let namedFragments = FieldMerging(rawValue: 1 << 2)
public static let namedFragments = FieldMerging(.namedFragments)

/// Merges all possible fields and fragment accessors from all sources.
public static let all: FieldMerging = [.ancestors, .siblings, .namedFragments]

/// Disables field merging entirely. Aside from removal of redundant selections, the shape of
/// the generated models will directly mirror the GraphQL definition.
public static let none: FieldMerging = FieldMerging(rawValue: 0)
public static let none: FieldMerging = []

public let rawValue: Int
private var options: MergedSelections.MergingStrategy

public init(rawValue: Int) {
self.rawValue = rawValue
private init(_ options: MergedSelections.MergingStrategy) {
self.options = options
}

public init(arrayLiteral elements: FieldMerging...) {
self.options = []
for element in elements {
self.options.insert(element.options)
}
}

/// Inserts a `SelectionSetInitializer` into the receiver.
public mutating func insert(_ member: FieldMerging) {
self.options.insert(member.options)
}
}

Expand Down Expand Up @@ -1267,6 +1279,75 @@ extension ApolloCodegenConfiguration.SelectionSetInitializers {
}
}

// MARK: - FieldMerging - Private Implementation

extension ApolloCodegenConfiguration.FieldMerging {

// MARK: - Codable

private enum CodableValues: String {
case all
case ancestors
case siblings
case namedFragments
}

public init(from decoder: any Decoder) throws {
var values = try decoder.unkeyedContainer()

var options: MergedSelections.MergingStrategy = []

while !values.isAtEnd {
let option = try values.decode(String.self)
switch option {
case CodableValues.all.rawValue:
self.options = [.all]
return

case CodableValues.ancestors.rawValue:
options.insert(.ancestors)

case CodableValues.siblings.rawValue:
options.insert(.siblings)

case CodableValues.namedFragments.rawValue:
options.insert(.namedFragments)

default:
throw DecodingError.dataCorrupted(
DecodingError.Context(
codingPath: values.codingPath,
debugDescription: "Unrecognized value: \(option)"
)
)
}
}

self.options = options
}

public func encode(to encoder: any Encoder) throws {
var container = encoder.unkeyedContainer()

if options == .all {
try container.encode(CodableValues.all.rawValue)
return
}

if options.contains(.ancestors) {
try container.encode(CodableValues.ancestors.rawValue)
}

if options.contains(.siblings) {
try container.encode(CodableValues.siblings.rawValue)
}

if options.contains(.namedFragments) {
try container.encode(CodableValues.namedFragments.rawValue)
}
}
}

// MARK: - Deprecations

extension ApolloCodegenConfiguration {
Expand Down

0 comments on commit 86fec35

Please sign in to comment.