diff --git a/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/InitializerArgument.swift b/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/InitializerArgument.swift index 01a19f7..09bbdbe 100644 --- a/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/InitializerArgument.swift +++ b/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/InitializerArgument.swift @@ -20,10 +20,12 @@ extension Struct where CurrentStage == Stage.Prepared { .filter { $0.graphqlPath?.resolved.isConnection ?? true } .map { InitializerArgument(name: $0.name, type: $0.type) } + let extraAPIArguments = additionalReferencedAPIs.filter { $0.property == nil }.map { InitializerArgument(name: $0.api.name.camelized, type: $0.api.name) } + let queryArgument = query != nil ? [InitializerArgument(name: "data", type: "Data")] : [] let fragmentArguments = fragments.map { InitializerArgument(name: $0.target.name.camelized, type: $0.target.name.upperCamelized) } - return stockArguments + queryArgument + fragmentArguments + return stockArguments + extraAPIArguments + queryArgument + fragmentArguments } } diff --git a/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/InitializerArgumentAssignmentFromQueryData.swift b/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/InitializerArgumentAssignmentFromQueryData.swift index f85dcd0..5279aa8 100644 --- a/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/InitializerArgumentAssignmentFromQueryData.swift +++ b/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/InitializerArgumentAssignmentFromQueryData.swift @@ -21,10 +21,15 @@ extension Struct where CurrentStage == Stage.Prepared { .map { InitializerArgumentAssignmentFromQueryData(name: $0.name, expression: $0.expression(in: self)) } + let extraAPIArguments = additionalReferencedAPIs + .filter { $0.property == nil } + .map { InitializerArgumentAssignmentFromQueryData(name: $0.api.name.camelized, + expression: $0.expression(in: self)) } + let queryArgument = query != nil ? [InitializerArgumentAssignmentFromQueryData(name: "data", expression: "data")] : [] let fragmentArguments = fragments.map { InitializerArgumentAssignmentFromQueryData(name: $0.target.name.camelized, expression: $0.target.name.camelized) } - return stockArguments + queryArgument + fragmentArguments + return stockArguments + extraAPIArguments + queryArgument + fragmentArguments } } @@ -46,3 +51,12 @@ extension Property where CurrentStage == Stage.Prepared { } } + +extension AdditionalReferencedAPI { + + fileprivate func expression(in graphQlStruct: Struct) -> CodeTransformable { + guard api != graphQlStruct.query?.api else { return "self" } + return api.name.camelized + } + +} diff --git a/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/InitializerValueAssignment.swift b/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/InitializerValueAssignment.swift index 27e73d3..064c2e0 100644 --- a/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/InitializerValueAssignment.swift +++ b/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/InitializerValueAssignment.swift @@ -20,7 +20,7 @@ extension Struct where CurrentStage == Stage.Prepared { return properties.map { property in switch property.graphqlPath { case .some(let path): - let expression = path.initializerExpression() ?? property.name + let expression = path.initializerExpression(in: self) ?? property.name return InitializerValueAssignment(name: property.name, expression: "GraphQL(\(expression))") case .none: @@ -33,10 +33,11 @@ extension Struct where CurrentStage == Stage.Prepared { extension Stage.Cleaned.Path { - func initializerExpression() -> String? { + func initializerExpression(in parent: Struct) -> String? { if resolved.isMutation { - // TODO: fix naming - return ".init(api: \(resolved.validated.api.name.camelized))" + let referencedAPI = parent.additionalReferencedAPIs.first { $0.api == resolved.validated.api } ?! fatalError() + let name = referencedAPI.property?.name ?? referencedAPI.api.name.camelized + return ".init(api: \(name))" } if resolved.isConnection { diff --git a/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/MutationStruct.swift b/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/MutationStruct.swift index db4b652..39f8a37 100644 --- a/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/MutationStruct.swift +++ b/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/MutationStruct.swift @@ -13,7 +13,8 @@ struct MutationStruct: ExtraValuesSwiftCodeTransformable { func arguments(from context: Stencil.Context, arguments: [Any?]) throws -> [String : Any] { return [ - "swiftType": mutation.returnType.swiftType(api: mutation.api.name), + "swiftUnderlyingType": mutation.returnType.swiftType(api: mutation.api.name), + "swiftType": mutation.returnType.swiftType(api: mutation.api.name, for: mutation.referencedFragment), "queryRendererArguments": mutation.queryRendererArguments, "queryArgumentAssignments": mutation.queryArgumentAssignments, "expression": Stage.Cleaned.Path(resolved: mutation.path, diff --git a/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/QueryRendererArgument.swift b/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/QueryRendererArgument.swift index 0a3cf8d..d10a5ba 100644 --- a/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/QueryRendererArgument.swift +++ b/Sources/Graphaello/Processing/Code Generstion/Swift/Implementation/QueryRendererArgument.swift @@ -22,6 +22,11 @@ extension Struct where CurrentStage: ResolvedStage { let argumentsFromStruct = properties .compactMap { $0.directArgument } .filter { $0.type != api } + + let extraAPIArguments = additionalReferencedAPIs + .filter { $0.property == nil } + .filter { $0.api.name != api } + .map { QueryRendererArgument(name: $0.api.name.camelized, type: $0.api.name, expression: nil) } let argumentsFromQuery = query? .arguments @@ -32,7 +37,7 @@ extension Struct where CurrentStage: ResolvedStage { expression: argument.defaultValue) } ?? [] - return argumentsFromQuery + argumentsFromStruct + return argumentsFromQuery + argumentsFromStruct + extraAPIArguments } } diff --git a/Sources/Graphaello/Processing/Model/Schema/TypeReference.swift b/Sources/Graphaello/Processing/Model/Schema/TypeReference.swift index e79049e..bd3616e 100644 --- a/Sources/Graphaello/Processing/Model/Schema/TypeReference.swift +++ b/Sources/Graphaello/Processing/Model/Schema/TypeReference.swift @@ -54,7 +54,7 @@ extension Schema.GraphQLType.Field.TypeReference { } } - func swiftType(api: String?) -> String { + func swiftType(api: String?, for fragment: GraphQLFragment? = nil) -> String { switch self { case .concrete(let definition): @@ -72,17 +72,22 @@ extension Schema.GraphQLType.Field.TypeReference { return "String?" } } + + if let fragment = fragment { + assert(fragment.target.name == definition.name) + return "Apollo\(api.upperCamelized).\(fragment.name.upperCamelized)?" + } return "\(api).\(name.upperCamelized)?" case .complex(let definition, let ofType): switch definition.kind { case .list: - return "[\(ofType.swiftType(api: api))]?" + return "[\(ofType.swiftType(api: api, for: fragment))]?" case .nonNull: - return String(ofType.swiftType(api: api).dropLast()) + return String(ofType.swiftType(api: api, for: fragment).dropLast()) case .scalar, .object, .enum, .interface, .inputObject, .union: - return ofType.swiftType(api: api) + return ofType.swiftType(api: api, for: fragment) } } } diff --git a/Sources/Graphaello/Processing/Pipeline/Component/4. Resolution/Resolution + Context/Structs/StructResolution+FragmentName+init.swift b/Sources/Graphaello/Processing/Pipeline/Component/4. Resolution/Resolution + Context/Structs/StructResolution+FragmentName+init.swift index 1d2db64..c7ab6a2 100644 --- a/Sources/Graphaello/Processing/Pipeline/Component/4. Resolution/Resolution + Context/Structs/StructResolution+FragmentName+init.swift +++ b/Sources/Graphaello/Processing/Pipeline/Component/4. Resolution/Resolution + Context/Structs/StructResolution+FragmentName+init.swift @@ -23,6 +23,8 @@ extension StructResolution.FragmentName { case let expression as ArrayExprSyntax: guard let syntax = Array(expression.elements).single()?.expression else { throw GraphQLFragmentResolverError.invalidTypeNameForFragment(expression.description) } try self.init(syntax: syntax) + case let expression as ArrayTypeSyntax: + try self.init(syntax: expression.elementType) case let expression as OptionalTypeSyntax: try self.init(syntax: expression.wrappedType) case let expression as MemberTypeIdentifierSyntax: diff --git a/Sources/Graphaello/Processing/Pipeline/Stage/3. Validated/Validated.swift b/Sources/Graphaello/Processing/Pipeline/Stage/3. Validated/Validated.swift index 8c2ccda..1e3ddb7 100644 --- a/Sources/Graphaello/Processing/Pipeline/Stage/3. Validated/Validated.swift +++ b/Sources/Graphaello/Processing/Pipeline/Stage/3. Validated/Validated.swift @@ -66,23 +66,30 @@ extension Stage.Validated.Path { var returnType: Schema.GraphQLType.Field.TypeReference { guard let last = components.last else { fatalError() } return components - .dropFirst() + .dropLast() .reversed() .reduce(last.fieldType) { returnType, component in - - switch (returnType, component.fieldType) { - case (.concrete, .concrete): - return returnType - case (.complex(let definition, let ofType), .concrete) where definition.kind == .nonNull: - return ofType - case (.complex, .concrete): - return returnType - case (.complex(let lhs, _), .complex(let rhs, _)) where lhs.kind == .nonNull && rhs.kind == .nonNull: - return returnType - case (_, .complex(let definition, _)): - return .complex(definition, ofType: returnType) - } + component.fieldType.embed(returnType: returnType) } } } + +extension Schema.GraphQLType.Field.TypeReference { + + fileprivate func embed(returnType: Schema.GraphQLType.Field.TypeReference) -> Schema.GraphQLType.Field.TypeReference { + switch (returnType, self) { + case (.concrete, .concrete): + return returnType + case (.complex(let definition, let ofType), .concrete) where definition.kind == .nonNull: + return ofType + case (.complex, .concrete): + return returnType + case (.complex(let lhs, _), .complex(let rhs, _)) where lhs.kind == .nonNull && rhs.kind == .nonNull: + return returnType + case (_, .complex(let definition, let ofType)): + return .complex(definition, ofType: ofType.embed(returnType: returnType)) + } + } + +} diff --git a/Sources/Graphaello/Processing/Pipeline/Stage/4. Resolved/AdditionalReferencedAPI.swift b/Sources/Graphaello/Processing/Pipeline/Stage/4. Resolved/AdditionalReferencedAPI.swift new file mode 100644 index 0000000..96e03f7 --- /dev/null +++ b/Sources/Graphaello/Processing/Pipeline/Stage/4. Resolved/AdditionalReferencedAPI.swift @@ -0,0 +1,21 @@ +// +// AdditionalReferencedAPI.swift +// +// +// Created by Mathias Quintero on 12.01.20. +// + +import Foundation + +struct AdditionalReferencedAPI: Hashable { + let api: API + let property: Property? + + static func == (lhs: AdditionalReferencedAPI, rhs: AdditionalReferencedAPI) -> Bool { + return lhs.api == rhs.api + } + + func hash(into hasher: inout Hasher) { + api.hash(into: &hasher) + } +} diff --git a/Sources/Graphaello/Processing/Pipeline/Stage/4. Resolved/Resolved+Struct+Properties.swift b/Sources/Graphaello/Processing/Pipeline/Stage/4. Resolved/Resolved+Struct+Properties.swift index 7056da9..09e2dcb 100644 --- a/Sources/Graphaello/Processing/Pipeline/Stage/4. Resolved/Resolved+Struct+Properties.swift +++ b/Sources/Graphaello/Processing/Pipeline/Stage/4. Resolved/Resolved+Struct+Properties.swift @@ -24,6 +24,11 @@ extension Struct where CurrentStage: ResolvedStage { return context[.mutations] } + var additionalReferencedAPIs: OrderedSet> { + let types = Dictionary(properties.filter { $0.graphqlPath == nil }.map { ($0.type, $0) }) { $1 } + return OrderedSet(mutations.map { AdditionalReferencedAPI(api: $0.api, property: types[$0.api.name]) }) + } + init(code: SourceCode, name: String, properties: [Property], diff --git a/templates/swift/MutationStruct.swift.stencil b/templates/swift/MutationStruct.swift.stencil index 59c11d7..0360cfe 100644 --- a/templates/swift/MutationStruct.swift.stencil +++ b/templates/swift/MutationStruct.swift.stencil @@ -1,5 +1,5 @@ extension {{ structPrepared.name }} { - class {{ mutationStruct.mutation.name }}{% if mutationStruct.mutation.referencedFragment %}{% endif %}: Mutation{% if mutationStruct.mutation.referencedFragment %} where V.UnderlyingType == {{ swiftType }}{% endif %} { + class {{ mutationStruct.mutation.name }}{% if mutationStruct.mutation.referencedFragment %}{% endif %}: Mutation{% if mutationStruct.mutation.referencedFragment %} where V.UnderlyingType == {{ swiftUnderlyingType }}{% endif %} { {% if mutationStruct.mutation.referencedFragment %} typealias Value = V @@ -32,7 +32,7 @@ extension {{ structPrepared.name }} { } {% if mutationStruct.mutation.referencedFragment %} -extension {{ structPrepared.name }}.{{ mutationStruct.mutation.name }} where V == Apollo{{ mutationStruct.mutation.api.name }}. { +extension {{ structPrepared.name }}.{{ mutationStruct.mutation.name }} where V == {{ swiftType }} { {% else %} extension {{ structPrepared.name }}.{{ mutationStruct.mutation.name }} { {% endif %}