Skip to content

Commit

Permalink
Check type name conflicts in SelectionSet (#3009)
Browse files Browse the repository at this point in the history
  • Loading branch information
BobaFetters committed May 15, 2023
1 parent c4600ff commit 415f7fe
Show file tree
Hide file tree
Showing 10 changed files with 546 additions and 40 deletions.
4 changes: 4 additions & 0 deletions Apollo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
54DDB0921EA045870009DD99 /* InMemoryNormalizedCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54DDB0911EA045870009DD99 /* InMemoryNormalizedCache.swift */; };
5AC6CA4322AAF7B200B7C94D /* GraphQLHTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AC6CA4222AAF7B200B7C94D /* GraphQLHTTPMethod.swift */; };
5BB2C0232380836100774170 /* VersionNumberTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BB2C0222380836100774170 /* VersionNumberTests.swift */; };
66321AE72A126C4400CC35CB /* IR+Formatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66321AE62A126C4400CC35CB /* IR+Formatting.swift */; };
96F32D3B27CCD16B00F3383C /* animalkingdom-graphql in Resources */ = {isa = PBXBuildFile; fileRef = 96F32D3A27CCD16B00F3383C /* animalkingdom-graphql */; };
96F32D3C27CCD16D00F3383C /* animalkingdom-graphql in Resources */ = {isa = PBXBuildFile; fileRef = 96F32D3A27CCD16B00F3383C /* animalkingdom-graphql */; };
9B1CCDD92360F02C007C9032 /* Bundle+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B1CCDD82360F02C007C9032 /* Bundle+Helpers.swift */; };
Expand Down Expand Up @@ -1134,6 +1135,7 @@
54DDB0911EA045870009DD99 /* InMemoryNormalizedCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InMemoryNormalizedCache.swift; sourceTree = "<group>"; };
5AC6CA4222AAF7B200B7C94D /* GraphQLHTTPMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphQLHTTPMethod.swift; sourceTree = "<group>"; };
5BB2C0222380836100774170 /* VersionNumberTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionNumberTests.swift; sourceTree = "<group>"; };
66321AE62A126C4400CC35CB /* IR+Formatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IR+Formatting.swift"; sourceTree = "<group>"; };
90690D05224333DA00FC2E54 /* Apollo-Project-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Apollo-Project-Debug.xcconfig"; sourceTree = "<group>"; };
90690D06224333DA00FC2E54 /* Apollo-Target-Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Apollo-Target-Framework.xcconfig"; sourceTree = "<group>"; };
90690D07224333DA00FC2E54 /* Apollo-Project-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Apollo-Project-Release.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2473,6 +2475,7 @@
9B7B6F68233C2C0C00F32205 /* FileManager+Apollo.swift */,
9BAEEBF62346F0A000808306 /* StaticString+Apollo.swift */,
9B8C3FB1248DA2EA00707B13 /* URL+Apollo.swift */,
66321AE62A126C4400CC35CB /* IR+Formatting.swift */,
);
name = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -5037,6 +5040,7 @@
9F62E03F2590896400E6E808 /* GraphQLError.swift in Sources */,
E6AAA732286BC58200F4659D /* OperationIdentifiersFileGenerator.swift in Sources */,
9B7B6F5A233C287200F32205 /* ApolloCodegenConfiguration.swift in Sources */,
66321AE72A126C4400CC35CB /* IR+Formatting.swift in Sources */,
9F1A966B258F34BB00A06EEB /* GraphQLJSFrontend.swift in Sources */,
DEB05B48289C3B4000170299 /* MockInterfacesFileGenerator.swift in Sources */,
DE09114E27288B1F000648E5 /* SortedSelections.swift in Sources */,
Expand Down
44 changes: 42 additions & 2 deletions Sources/ApolloCodegenLib/ApolloCodegen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class ApolloCodegen {
case invalidConfiguration(message: String)
case invalidSchemaName(_ name: String, message: String)
case targetNameConflict(name: String)
case typeNameConflict(name: String, conflictingName: String, containingObject: String)

public var errorDescription: String? {
switch self {
Expand Down Expand Up @@ -53,8 +54,15 @@ public class ApolloCodegen {
return "The schema namespace `\(name)` is invalid: \(message)"
case let .targetNameConflict(name):
return """
Target name '\(name)' conflicts with a reserved library name. Please choose a different \
target name.
Target name '\(name)' conflicts with a reserved library name. Please choose a different \
target name.
"""
case let .typeNameConflict(name, conflictingName, containingObject):
return """
TypeNameConflict - \
Field '\(conflictingName)' conflicts with field '\(name)' in operation/fragment `\(containingObject)`. \
Recommend using a field alias for one of these fields to resolve this conflict. \
For more info see: https://www.apollographql.com/docs/ios/troubleshooting/codegen-troubleshooting#typenameconflict
"""
}
}
Expand Down Expand Up @@ -212,6 +220,36 @@ public class ApolloCodegen {
throw Error.schemaNameConflict(name: context.schemaNamespace)
}
}

/// Validates that there are no type conflicts within a SelectionSet
static private func validateTypeConflicts(for selectionSet: IR.SelectionSet, with context: ConfigurationContext, in containingObject: String) throws {
// Check for type conflicts resulting from singularization/pluralization of fields
var fieldNamesByFormattedTypeName = [String: String]()
var fields: [IR.EntityField] = selectionSet.selections.direct?.fields.values.compactMap { $0 as? IR.EntityField } ?? []
fields.append(contentsOf: selectionSet.selections.merged.fields.values.compactMap { $0 as? IR.EntityField } )

try fields.forEach { field in
let formattedTypeName = field.formattedSelectionSetName(with: context.pluralizer)
if let existingFieldName = fieldNamesByFormattedTypeName[formattedTypeName] {
throw Error.typeNameConflict(
name: existingFieldName,
conflictingName: field.name,
containingObject: containingObject
)
}

fieldNamesByFormattedTypeName[formattedTypeName] = field.name
try validateTypeConflicts(for: field.selectionSet, with: context, in: containingObject)
}

// gather nested fragments to loop through and check as well
var nestedSelectionSets: [IR.SelectionSet] = selectionSet.selections.direct?.inlineFragments.values.elements ?? []
nestedSelectionSets.append(contentsOf: selectionSet.selections.merged.inlineFragments.values)

try nestedSelectionSets.forEach { nestedSet in
try validateTypeConflicts(for: nestedSet, with: context, in: containingObject)
}
}

/// Performs GraphQL source validation and compiles the schema and operation source documents.
static func compileGraphQLResult(
Expand Down Expand Up @@ -304,6 +342,7 @@ public class ApolloCodegen {
for fragment in compilationResult.fragments {
try autoreleasepool {
let irFragment = ir.build(fragment: fragment)
try validateTypeConflicts(for: irFragment.rootField.selectionSet, with: config, in: irFragment.definition.name)
try FragmentFileGenerator(irFragment: irFragment, config: config)
.generate(forConfig: config, fileManager: fileManager)
}
Expand All @@ -314,6 +353,7 @@ public class ApolloCodegen {
for operation in compilationResult.operations {
try autoreleasepool {
let irOperation = ir.build(operation: operation)
try validateTypeConflicts(for: irOperation.rootField.selectionSet, with: config, in: irOperation.definition.name)
try OperationFileGenerator(irOperation: irOperation, config: config)
.generate(forConfig: config, fileManager: fileManager)

Expand Down
40 changes: 40 additions & 0 deletions Sources/ApolloCodegenLib/IR+Formatting.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Foundation

extension GraphQLType {

var isListType: Bool {
switch self {
case .list: return true
case let .nonNull(innerType): return innerType.isListType
case .entity, .enum, .inputObject, .scalar: return false
}
}

}

extension IR.EntityField {

/// Takes the associated ``IR.EntityField`` and formats it into a selection set name
func formattedSelectionSetName(
with pluralizer: Pluralizer
) -> String {
IR.Entity.FieldPathComponent(name: responseKey, type: type)
.formattedSelectionSetName(with: pluralizer)
}

}

extension IR.Entity.FieldPathComponent {

/// Takes the associated ``IR.Entity.FieldPathComponent`` and formats it into a selection set name
func formattedSelectionSetName(
with pluralizer: Pluralizer
) -> String {
var fieldName = name.firstUppercased
if type.isListType {
fieldName = pluralizer.singularize(fieldName)
}
return fieldName.asSelectionSetName
}

}
37 changes: 0 additions & 37 deletions Sources/ApolloCodegenLib/Templates/SelectionSetTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -635,43 +635,6 @@ fileprivate extension IR.SelectionSet {

}

fileprivate extension IR.EntityField {

func formattedSelectionSetName(
with pluralizer: Pluralizer
) -> String {
IR.Entity.FieldPathComponent(name: responseKey, type: type)
.formattedSelectionSetName(with: pluralizer)
}

}

fileprivate extension IR.Entity.FieldPathComponent {

func formattedSelectionSetName(
with pluralizer: Pluralizer
) -> String {
var fieldName = name.firstUppercased
if type.isListType {
fieldName = pluralizer.singularize(fieldName)
}
return fieldName.asSelectionSetName
}

}

fileprivate extension GraphQLType {

var isListType: Bool {
switch self {
case .list: return true
case let .nonNull(innerType): return innerType.isListType
case .entity, .enum, .inputObject, .scalar: return false
}
}

}

fileprivate extension IR.MergedSelections.MergedSource {

func generatedSelectionSetName(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import ApolloAPI

public enum SchemaConfiguration: ApolloAPI.SchemaConfiguration {
public static func cacheKeyInfo(for type: Object, object: some ObjectData) -> CacheKeyInfo? {
public static func cacheKeyInfo(for type: Object, object: ObjectData) -> CacheKeyInfo? {
// Implement this function to configure cache key resolution for your schema types.
return nil
}
Expand Down

0 comments on commit 415f7fe

Please sign in to comment.