Skip to content

Commit

Permalink
fix: @dynamicMember conflicting field name (#2965)
Browse files Browse the repository at this point in the history
  • Loading branch information
calvincestari authored Apr 17, 2023
1 parent 0390d98 commit cafeffa
Show file tree
Hide file tree
Showing 83 changed files with 716 additions and 606 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,16 @@ public extension Mock where O == Bird {
wingspan: Double? = nil
) {
self.init()
self.bodyTemperature = bodyTemperature
self.favoriteToy = favoriteToy
self.height = height
self.humanName = humanName
self.id = id
self.laysEggs = laysEggs
self.owner = owner
self.predators = predators
self.skinCovering = skinCovering
self.species = species
self.wingspan = wingspan
_set(bodyTemperature, for: \.bodyTemperature)
_set(favoriteToy, for: \.favoriteToy)
_set(height, for: \.height)
_set(humanName, for: \.humanName)
_set(id, for: \.id)
_set(laysEggs, for: \.laysEggs)
_set(owner, for: \.owner)
_set(predators, for: \.predators)
_set(skinCovering, for: \.skinCovering)
_set(species, for: \.species)
_set(wingspan, for: \.wingspan)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,16 @@ public extension Mock where O == Cat {
species: String? = nil
) {
self.init()
self.bodyTemperature = bodyTemperature
self.favoriteToy = favoriteToy
self.height = height
self.humanName = humanName
self.id = id
self.isJellicle = isJellicle
self.laysEggs = laysEggs
self.owner = owner
self.predators = predators
self.skinCovering = skinCovering
self.species = species
_set(bodyTemperature, for: \.bodyTemperature)
_set(favoriteToy, for: \.favoriteToy)
_set(height, for: \.height)
_set(humanName, for: \.humanName)
_set(id, for: \.id)
_set(isJellicle, for: \.isJellicle)
_set(laysEggs, for: \.laysEggs)
_set(owner, for: \.owner)
_set(predators, for: \.predators)
_set(skinCovering, for: \.skinCovering)
_set(species, for: \.species)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ public extension Mock where O == Crocodile {
species: String? = nil
) {
self.init()
self.height = height
self.id = id
self.predators = predators
self.skinCovering = skinCovering
self.species = species
_set(height, for: \.height)
_set(id, for: \.id)
_set(predators, for: \.predators)
_set(skinCovering, for: \.skinCovering)
_set(species, for: \.species)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,16 @@ public extension Mock where O == Dog {
species: String? = nil
) {
self.init()
self.birthdate = birthdate
self.bodyTemperature = bodyTemperature
self.favoriteToy = favoriteToy
self.height = height
self.humanName = humanName
self.id = id
self.laysEggs = laysEggs
self.owner = owner
self.predators = predators
self.skinCovering = skinCovering
self.species = species
_set(birthdate, for: \.birthdate)
_set(bodyTemperature, for: \.bodyTemperature)
_set(favoriteToy, for: \.favoriteToy)
_set(height, for: \.height)
_set(humanName, for: \.humanName)
_set(id, for: \.id)
_set(laysEggs, for: \.laysEggs)
_set(owner, for: \.owner)
_set(predators, for: \.predators)
_set(skinCovering, for: \.skinCovering)
_set(species, for: \.species)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ public extension Mock where O == Fish {
species: String? = nil
) {
self.init()
self.favoriteToy = favoriteToy
self.height = height
self.humanName = humanName
self.id = id
self.owner = owner
self.predators = predators
self.skinCovering = skinCovering
self.species = species
_set(favoriteToy, for: \.favoriteToy)
_set(height, for: \.height)
_set(humanName, for: \.humanName)
_set(id, for: \.id)
_set(owner, for: \.owner)
_set(predators, for: \.predators)
_set(skinCovering, for: \.skinCovering)
_set(species, for: \.species)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ public extension Mock where O == Height {
relativeSize: GraphQLEnum<AnimalKingdomAPI.RelativeSize>? = nil
) {
self.init()
self.centimeters = centimeters
self.feet = feet
self.inches = inches
self.meters = meters
self.relativeSize = relativeSize
_set(centimeters, for: \.centimeters)
_set(feet, for: \.feet)
_set(inches, for: \.inches)
_set(meters, for: \.meters)
_set(relativeSize, for: \.relativeSize)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ public extension Mock where O == Human {
species: String? = nil
) {
self.init()
self.bodyTemperature = bodyTemperature
self.firstName = firstName
self.height = height
self.id = id
self.laysEggs = laysEggs
self.predators = predators
self.skinCovering = skinCovering
self.species = species
_set(bodyTemperature, for: \.bodyTemperature)
_set(firstName, for: \.firstName)
_set(height, for: \.height)
_set(id, for: \.id)
_set(laysEggs, for: \.laysEggs)
_set(predators, for: \.predators)
_set(skinCovering, for: \.skinCovering)
_set(species, for: \.species)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ public extension Mock where O == Mutation {
adoptPet: AnyMock? = nil
) {
self.init()
self.adoptPet = adoptPet
_set(adoptPet, for: \.adoptPet)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ public extension Mock where O == PetRock {
owner: Mock<Human>? = nil
) {
self.init()
self.favoriteToy = favoriteToy
self.humanName = humanName
self.id = id
self.owner = owner
_set(favoriteToy, for: \.favoriteToy)
_set(humanName, for: \.humanName)
_set(id, for: \.id)
_set(owner, for: \.owner)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ public extension Mock where O == Query {
pets: [AnyMock]? = nil
) {
self.init()
self.allAnimals = allAnimals
self.classroomPets = classroomPets
self.pets = pets
_set(allAnimals, for: \.allAnimals)
_set(classroomPets, for: \.classroomPets)
_set(pets, for: \.pets)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ public extension Mock where O == Rat {
species: String? = nil
) {
self.init()
self.favoriteToy = favoriteToy
self.height = height
self.humanName = humanName
self.id = id
self.owner = owner
self.predators = predators
self.skinCovering = skinCovering
self.species = species
_set(favoriteToy, for: \.favoriteToy)
_set(height, for: \.height)
_set(humanName, for: \.humanName)
_set(id, for: \.id)
_set(owner, for: \.owner)
_set(predators, for: \.predators)
_set(skinCovering, for: \.skinCovering)
_set(species, for: \.species)
}
}
7 changes: 5 additions & 2 deletions Sources/ApolloCodegenLib/TemplateString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,15 @@ struct TemplateString: ExpressibleByStringInterpolation, CustomStringConvertible
terminator: String? = nil
) where T: LazySequenceProtocol, T.Element: CustomStringConvertible {
var iterator = sequence.makeIterator()
guard var elementsString = iterator.next()?.description else {
guard
var elementsString = iterator.next()?.description,
!elementsString.isEmpty
else {
removeLineIfEmpty()
return
}

while let element = iterator.next() {
while let element = iterator.next(), !element.description.isEmpty {
elementsString.append(separator + element.description)
}

Expand Down
16 changes: 15 additions & 1 deletion Sources/ApolloCodegenLib/Templates/MockObjectTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,14 @@ struct MockObjectTemplate: TemplateRenderer {
TemplateString("""
public extension Mock where O == \(objectName) {
\(conflictingFieldNameProperties(fields))
convenience init(
\(fields.map { """
\($0.propertyName)\(ifLet: $0.initializerParameterName, {" \($0)"}): \($0.mockType)? = nil
""" }, separator: ",\n")
) {
self.init()
\(fields.map { "self.\($0.propertyName) = \($0.initializerParameterName ?? $0.propertyName)" }, separator: "\n")
\(fields.map { "_set(\($0.initializerParameterName ?? $0.propertyName), for: \\.\($0.propertyName))" }, separator: "\n")
}
}
""") : TemplateString(stringLiteral: "")
Expand All @@ -68,6 +69,19 @@ struct MockObjectTemplate: TemplateRenderer {
"""
}

private func conflictingFieldNameProperties(_ fields: [TemplateField]) -> TemplateString {
"""
\(fields.map { """
\(if: $0.propertyName.isConflictingTestMockFieldName, """
var \($0.propertyName): \($0.mockType)? {
get { _data["\($0.propertyName)"] as? \($0.mockType) }
set { _set(newValue, for: \\.\($0.propertyName)) }
}
""")
""" }, separator: "\n", terminator: "\n")
"""
}

private func mockTypeName(for type: GraphQLType) -> String {
func nameReplacement(for type: GraphQLType, forceNonNull: Bool) -> String {
switch type {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ extension String {
"\(self)_value" : nil
}

var isConflictingTestMockFieldName: Bool {
SwiftKeywords.TestMockConflictingFieldNames.contains(self)
}

private func escapeIf(in set: Set<String>) -> String {
set.contains(self) ? "`\(self)`" : self
}
Expand Down Expand Up @@ -81,6 +85,13 @@ enum SwiftKeywords {
"Actor"
]

/// There are some field names that conflict with function names due to the @dynamicMember
/// subscripting of `Mock`. This set is used to match those field names and generate properties
/// instead of just relying on the subscript access.
static let TestMockConflictingFieldNames: Set<String> = [
"hash"
]

fileprivate static let FieldAccessorNamesToEscape: Set<String> = [
"associatedtype",
"class",
Expand Down
37 changes: 30 additions & 7 deletions Sources/ApolloTestSupport/TestMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,26 @@ public class Mock<O: MockObject>: AnyMock, Hashable {

public var __typename: String { _data["__typename"] as! String }

public subscript<T: AnyScalarType & Hashable>(dynamicMember keyPath: KeyPath<O.MockFields, Field<T>>) -> T? {
public subscript<T: AnyScalarType & Hashable>(
dynamicMember keyPath: KeyPath<O.MockFields, Field<T>>
) -> T? {
get {
let field = O._mockFields[keyPath: keyPath]
return _data[field.key.description] as? T
}
set {
let field = O._mockFields[keyPath: keyPath]
_data[field.key.description] = newValue
_set(newValue, for: keyPath)
}
}

public func _set<T: AnyScalarType & Hashable>(
_ value: T?,
for keyPath: KeyPath<O.MockFields, Field<T>>
) {
let field = O._mockFields[keyPath: keyPath]
_data[field.key.description] = value
}

public subscript<T: MockFieldValue>(
dynamicMember keyPath: KeyPath<O.MockFields, Field<T>>
) -> T.MockValueCollectionType.Element? {
Expand All @@ -34,11 +43,18 @@ public class Mock<O: MockObject>: AnyMock, Hashable {
return _data[field.key.description] as? T.MockValueCollectionType.Element
}
set {
let field = O._mockFields[keyPath: keyPath]
_data[field.key.description] = (newValue as? AnyHashable)
_set(newValue, for: keyPath)
}
}

public func _set<T: MockFieldValue>(
_ value: T.MockValueCollectionType.Element?,
for keyPath: KeyPath<O.MockFields, Field<T>>
) {
let field = O._mockFields[keyPath: keyPath]
_data[field.key.description] = (value as? AnyHashable)
}

public subscript<T: MockFieldValue>(
dynamicMember keyPath: KeyPath<O.MockFields, Field<Array<T>>>
) -> [T.MockValueCollectionType.Element]? {
Expand All @@ -47,11 +63,18 @@ public class Mock<O: MockObject>: AnyMock, Hashable {
return _data[field.key.description] as? [T.MockValueCollectionType.Element]
}
set {
let field = O._mockFields[keyPath: keyPath]
_data[field.key.description] = newValue?._unsafelyConvertToMockValue()
_set(newValue, for: keyPath)
}
}

public func _set<T: MockFieldValue>(
_ value: [T.MockValueCollectionType.Element]?,
for keyPath: KeyPath<O.MockFields, Field<Array<T>>>
) {
let field = O._mockFields[keyPath: keyPath]
_data[field.key.description] = value?._unsafelyConvertToMockValue()
}

public var _selectionSetMockData: JSONObject {
_data.mapValues {
if let mock = $0 as? AnyMock {
Expand Down
Loading

0 comments on commit cafeffa

Please sign in to comment.