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

Generate Operation Variables & Field Arguments #2163

Merged
merged 25 commits into from
Feb 22, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1e1a6e9
Render Operation Variable - single variable
AnthonyMDev Feb 14, 2022
35a5272
Operation Variables - Multiple Variables
AnthonyMDev Feb 14, 2022
b2770a7
WIP: Operation Variable - nullables
AnthonyMDev Feb 14, 2022
f9dcc92
Recfactored to add InputValueRenderable
AnthonyMDev Feb 15, 2022
9006ab9
Fix renamed but not deleted file
AnthonyMDev Feb 15, 2022
b5864cb
Add ExpressibleBy Literals to GraphQLNullable
AnthonyMDev Feb 16, 2022
110a6bf
Correct OperationVariable rendering test behavior
AnthonyMDev Feb 16, 2022
14a0989
WIP: Implementing rendering
AnthonyMDev Feb 16, 2022
fedc884
Fix template string indentation
AnthonyMDev Feb 16, 2022
3c2c08c
Add operation variable default values to CompilationResult
AnthonyMDev Feb 16, 2022
001a49a
Add pet adoption mutation
AnthonyMDev Feb 16, 2022
6af18f7
Add public modifier to input object template
AnthonyMDev Feb 16, 2022
eda88fb
WIP: Fixing Input Object Accessors
AnthonyMDev Feb 16, 2022
df531ec
Add public to input object init and fields
AnthonyMDev Feb 17, 2022
e49a422
Generate InputObject fields with dynamic member accessors
AnthonyMDev Feb 17, 2022
99b65b6
Rename input object "dict" to "data"
AnthonyMDev Feb 17, 2022
b7b56bb
Added PetSearchQuery to AnimalKingdom API
AnthonyMDev Feb 17, 2022
6528aec
Fixed rendering of input enum values
AnthonyMDev Feb 17, 2022
6650fff
Fixed conversion of default value dictionary literal to InputObject
AnthonyMDev Feb 17, 2022
2a18a8a
WIP
AnthonyMDev Feb 18, 2022
ba9c640
Restructured tests to be accurate for behavior with initializing inpu…
AnthonyMDev Feb 18, 2022
3c105a1
Changed Implementation for direct input value initialization
AnthonyMDev Feb 18, 2022
224eda6
WIP: FieldArgument Rendering
AnthonyMDev Feb 18, 2022
3c780f3
Fix field argument rendering to use literals
AnthonyMDev Feb 21, 2022
ddd74ac
Regenerate AnimalKingdomAPI with field arguments
AnthonyMDev Feb 21, 2022
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
22 changes: 21 additions & 1 deletion Apollo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@
DE674D9F261CEEE4000E8FC8 /* a.txt in Resources */ = {isa = PBXBuildFile; fileRef = 9B2061192591B3550020D1E0 /* a.txt */; };
DE6B15AF26152BE10068D642 /* DefaultInterceptorProviderIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE6B15AE26152BE10068D642 /* DefaultInterceptorProviderIntegrationTests.swift */; };
DE6B15B126152BE10068D642 /* Apollo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9FC750441D2A532C00458D91 /* Apollo.framework */; };
DE6D07F927BC3B6D009F5F33 /* InputValueRenderable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE6D07F827BC3B6D009F5F33 /* InputValueRenderable.swift */; };
DE6D07FD27BC3D53009F5F33 /* OperationDefinition_VariableDefinition_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE6D07FA27BC3BE9009F5F33 /* OperationDefinition_VariableDefinition_Tests.swift */; };
DE736F4626FA6EE6007187F2 /* InflectorKit in Frameworks */ = {isa = PBXBuildFile; productRef = E6E4209126A7DF4200B82624 /* InflectorKit */; };
DE796429276998B000978A03 /* IR+RootFieldBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE796428276998B000978A03 /* IR+RootFieldBuilder.swift */; };
DE79642B276999E700978A03 /* IRNamedFragmentBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE79642A276999E700978A03 /* IRNamedFragmentBuilderTests.swift */; };
Expand Down Expand Up @@ -316,6 +318,7 @@
DED46035261CEA660086EF63 /* ApolloTestSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F8A95781EC0FC1200304A2D /* ApolloTestSupport.framework */; };
DED46042261CEA8A0086EF63 /* TestServerURLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED45C172615308E0086EF63 /* TestServerURLs.swift */; };
DED46051261CEAD20086EF63 /* StarWarsAPI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9FCE2CFA1E6C213D00E34457 /* StarWarsAPI.framework */; };
DEE2DAA227BAF00500EC0607 /* GraphQLType+Rendered.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEE2DAA127BAF00500EC0607 /* GraphQLType+Rendered.swift */; };
DEFBBC86273470F70088AABC /* IR+Field.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEFBBC85273470F70088AABC /* IR+Field.swift */; };
DEFE0FC52748822900FFA440 /* IR+EntitySelectionTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEFE0FC42748822900FFA440 /* IR+EntitySelectionTree.swift */; };
E603863C27B2FB1D00E7B060 /* PetDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = E603765727B2FB1900E7B060 /* PetDetails.swift */; };
Expand Down Expand Up @@ -980,6 +983,8 @@
DE6B160B26152D210068D642 /* Project-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Project-Debug.xcconfig"; sourceTree = "<group>"; };
DE6B160C26152D210068D642 /* Workspace-Packaging.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Workspace-Packaging.xcconfig"; sourceTree = "<group>"; };
DE6B160D26152D210068D642 /* Workspace-Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Workspace-Shared.xcconfig"; sourceTree = "<group>"; };
DE6D07F827BC3B6D009F5F33 /* InputValueRenderable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputValueRenderable.swift; sourceTree = "<group>"; };
DE6D07FA27BC3BE9009F5F33 /* OperationDefinition_VariableDefinition_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationDefinition_VariableDefinition_Tests.swift; sourceTree = "<group>"; };
DE796428276998B000978A03 /* IR+RootFieldBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IR+RootFieldBuilder.swift"; sourceTree = "<group>"; };
DE79642A276999E700978A03 /* IRNamedFragmentBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IRNamedFragmentBuilderTests.swift; sourceTree = "<group>"; };
DE79642C27699A6A00978A03 /* IR+NamedFragmentBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IR+NamedFragmentBuilder.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1016,6 +1021,7 @@
DEE1B3F3273B08D8007350E5 /* AllAnimals.graphql.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllAnimals.graphql.swift; sourceTree = "<group>"; };
DEE1B3F4273B08D8007350E5 /* Types.graphql.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.graphql.swift; sourceTree = "<group>"; };
DEE1B3F5273B08D8007350E5 /* WarmBloodedDetails.graphql.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarmBloodedDetails.graphql.swift; sourceTree = "<group>"; };
DEE2DAA127BAF00500EC0607 /* GraphQLType+Rendered.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GraphQLType+Rendered.swift"; sourceTree = "<group>"; };
DEFBBC85273470F70088AABC /* IR+Field.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IR+Field.swift"; sourceTree = "<group>"; };
DEFE0FC42748822900FFA440 /* IR+EntitySelectionTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IR+EntitySelectionTree.swift"; sourceTree = "<group>"; };
E603765727B2FB1900E7B060 /* PetDetails.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PetDetails.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2015,6 +2021,7 @@
DE31A437276A78140020DC44 /* Templates */ = {
isa = PBXGroup;
children = (
DE6D07FC27BC3C81009F5F33 /* RenderingHelpers */,
E6C9849227929EBE009481BE /* EnumTemplate.swift */,
DE5FD60A276970FC0033EE23 /* FragmentTemplate.swift */,
E608FBA427B1EFDF00493756 /* HeaderCommentTemplate.swift */,
Expand All @@ -2040,8 +2047,9 @@
E69BEDA62798B89600000D10 /* InputObjectTemplateTests.swift */,
E623FD2B2797A700008B4CED /* InterfaceTemplateTests.swift */,
E64F7EC227A1243A0059C021 /* ObjectTemplateTests.swift */,
DE09F9C5270269F800795949 /* OperationDefinitionTemplate_DocumentType_Tests.swift */,
DE09066E27A4713F00211300 /* OperationDefinitionTemplateTests.swift */,
DE09F9C5270269F800795949 /* OperationDefinitionTemplate_DocumentType_Tests.swift */,
DE6D07FA27BC3BE9009F5F33 /* OperationDefinition_VariableDefinition_Tests.swift */,
DE5FD608276956C70033EE23 /* SchemaTemplateTests.swift */,
E6B42D0C27A4749100A3BD58 /* SwiftPackageManagerModuleTemplateTests.swift */,
E64F7EB927A085D90059C021 /* UnionTemplateTests.swift */,
Expand Down Expand Up @@ -2146,6 +2154,15 @@
path = Configuration/Shared;
sourceTree = SOURCE_ROOT;
};
DE6D07FC27BC3C81009F5F33 /* RenderingHelpers */ = {
isa = PBXGroup;
children = (
DEE2DAA127BAF00500EC0607 /* GraphQLType+Rendered.swift */,
DE6D07F827BC3B6D009F5F33 /* InputValueRenderable.swift */,
);
path = RenderingHelpers;
sourceTree = "<group>";
};
DE9C04AB26EA763E00EC35E7 /* Accumulators */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -3152,6 +3169,7 @@
DE5EEC8527988F1A00AF5913 /* IR+SelectionSet.swift in Sources */,
DE3484622746FF8F0065B77E /* IR+OperationBuilder.swift in Sources */,
E610D8DF278F8F1E0023E495 /* UnionFileGenerator.swift in Sources */,
DEE2DAA227BAF00500EC0607 /* GraphQLType+Rendered.swift in Sources */,
DE223C2D2721FCE8004A0148 /* ScopedSelectionSetHashable.swift in Sources */,
DE79642D27699A6A00978A03 /* IR+NamedFragmentBuilder.swift in Sources */,
9B47518D2575AA850001FB87 /* Pluralizer.swift in Sources */,
Expand All @@ -3167,6 +3185,7 @@
E610D8DB278EB0900023E495 /* InterfaceFileGenerator.swift in Sources */,
9B7B6F69233C2C0C00F32205 /* FileManager+Apollo.swift in Sources */,
E674DB41274C0A9B009BB90E /* Glob.swift in Sources */,
DE6D07F927BC3B6D009F5F33 /* InputValueRenderable.swift in Sources */,
DE5B318F27A48E060051C9D3 /* ImportStatementTemplate.swift in Sources */,
9F1A966C258F34BB00A06EEB /* GraphQLSchema.swift in Sources */,
9BE74D3D23FB4A8E006D354F /* FileFinder.swift in Sources */,
Expand Down Expand Up @@ -3250,6 +3269,7 @@
E6C9849527929FED009481BE /* EnumTemplateTests.swift in Sources */,
DE5FD609276956C70033EE23 /* SchemaTemplateTests.swift in Sources */,
E61EF713275EC99A00191DA7 /* ApolloCodegenTests.swift in Sources */,
DE6D07FD27BC3D53009F5F33 /* OperationDefinition_VariableDefinition_Tests.swift in Sources */,
E6B4E9992798A8CB004EC8C4 /* InterfaceTemplateTests.swift in Sources */,
9F62DF8E2590539A00E6E808 /* SchemaIntrospectionTests.swift in Sources */,
E6D90D0D278FFE35009CAC5D /* SchemaFileGeneratorTests.swift in Sources */,
Expand Down
8 changes: 8 additions & 0 deletions Sources/ApolloCodegenLib/TemplateString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ struct TemplateString: ExpressibleByStringInterpolation, CustomStringConvertible
appendInterpolation(elementsString)
}

mutating func appendInterpolation<T>(list: T)
where T: Collection, T.Element: CustomStringConvertible {
let shouldWrapInNewlines = list.count > 1
if shouldWrapInNewlines { appendLiteral("\n ") }
appendInterpolation(list)
if shouldWrapInNewlines { appendInterpolation("\n") }
}

mutating func appendInterpolation(
if bool: Bool,
_ template: @autoclosure () -> TemplateString,
Expand Down
70 changes: 2 additions & 68 deletions Sources/ApolloCodegenLib/Templates/InputObjectTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ struct InputObjectTemplate {
private func InitializerParametersTemplate() -> TemplateString {
TemplateString("""
\(graphqlInputObject.fields.map({
"\($1.name): \($1.renderType(includeDefault: true))"
"\($1.name): \($1.renderInputValueType(includeDefault: true))"
}), separator: ",\n")
""")
}
Expand All @@ -44,76 +44,10 @@ struct InputObjectTemplate {

private func FieldPropertyTemplate(_ field: GraphQLInputField) -> String {
"""
var \(field.name): \(field.renderType()) {
var \(field.name): \(field.renderInputValueType()) {
get { dict[\"\(field.name)\"] }
set { dict[\"\(field.name)\"] = newValue }
}
"""
}
}

fileprivate extension GraphQLInputField {
func renderType(includeDefault: Bool = false) -> String {
"\(type.render())\(isSwiftOptional ? "?" : "")\(includeDefault && hasSwiftNilDefault ? " = nil" : "")"
}

var isSwiftOptional: Bool {
!isNullable && hasSchemaDefault
}

var hasSwiftNilDefault: Bool {
isNullable && !hasSchemaDefault
}

var isNullable: Bool {
switch type {
case .nonNull(_): return false
default: return true
}
}

var hasSchemaDefault: Bool {
switch defaultValue {
case .none, .some(nil):
return false
case let .some(value):
guard let value = value as? JSValue else {
fatalError("Cannot determine default value for Input field: \(self)")
}

return !value.isUndefined
}
}
}

fileprivate extension GraphQLType {
enum NullabilityContainer {
case none
case graphqlNullable
case swiftOptional
}

func render(nullability: NullabilityContainer = .graphqlNullable) -> String {
switch self {
case let .entity(type as GraphQLNamedType),
let .enum(type as GraphQLNamedType),
let .scalar(type as GraphQLNamedType),
let .inputObject(type as GraphQLNamedType):

switch nullability {
case .none: return type.swiftName
case .graphqlNullable: return "GraphQLNullable<\(type.swiftName)>"
case .swiftOptional: return "\(type.swiftName)?"
}

case let .nonNull(ofType):
return ofType.render(nullability: .none)

case let .list(ofType):
let inner = "[\(ofType.render(nullability: .swiftOptional))]"

if nullability == .graphqlNullable { return "GraphQLNullable<\(inner)>" }
else { return inner }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ struct OperationDefinitionTemplate {
\(OperationDeclaration(operation.definition))
\(DocumentType.render(operation.definition, fragments: operation.referencedFragments, apq: config.apqs))

public init() {}
\(section: Variables.Properties(operation.definition.variables))

\(Initializer(operation.definition.variables))

\(section: Variables.Accessors(operation.definition.variables))

\(SelectionSetTemplate(schema: schema).render(for: operation))
}
Expand Down Expand Up @@ -60,6 +64,50 @@ struct OperationDefinitionTemplate {
)
}
}

private func Initializer(
_ variables: [CompilationResult.VariableDefinition]
) -> TemplateString {
let `init` = "public init"
if variables.isEmpty {
return "\(`init`)() {}"
}

return """
\(`init`)(\(list: variables.map(Variables.Parameter))) {
\(variables.map { "self.\($0.name) = \($0.name)" }, separator: "\n")
}
"""
}

enum Variables {
static func Properties(
_ variables: [CompilationResult.VariableDefinition]
) -> TemplateString {
"""
\(variables.map { "public var \($0.name): \($0.renderInputValueType())"}, separator: "\n")
"""
}

static func Parameter(_ variable: CompilationResult.VariableDefinition) -> String {
"\(variable.name): \(variable.renderInputValueType(includeDefault: true))"
}

static func Accessors(
_ variables: [CompilationResult.VariableDefinition]
) -> TemplateString {
guard !variables.isEmpty else {
return ""
}

return """
public var variables: Variables? {
[\(variables.map { "\"\($0.name)\": \($0.name)"}, separator: ",\n ")]
}
"""
}
}

}

fileprivate extension ApolloCodegenConfiguration.APQConfig {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
extension GraphQLType {
var rendered: String {
rendered(containedInNonNull: false)
}

func rendered(replacingNamedTypeWith newTypeName: String) -> String {
rendered(containedInNonNull: false, replacingNamedTypeWith: newTypeName)
}

private func rendered(
containedInNonNull: Bool,
replacingNamedTypeWith newTypeName: String? = nil
) -> String {
switch self {
case let .entity(type as GraphQLNamedType),
let .scalar(type as GraphQLNamedType),
let .inputObject(type as GraphQLNamedType):

let typeName = newTypeName ?? type.swiftName

return containedInNonNull ? typeName : "\(typeName)?"

case let .enum(type as GraphQLNamedType):
let typeName = newTypeName ?? type.name
let enumType = "GraphQLEnum<\(typeName)>"

return containedInNonNull ? enumType : "\(enumType)?"

case let .nonNull(ofType):
return ofType.rendered(containedInNonNull: true, replacingNamedTypeWith: newTypeName)

case let .list(ofType):
let inner = "[\(ofType.rendered(containedInNonNull: false, replacingNamedTypeWith: newTypeName))]"

return containedInNonNull ? inner : "\(inner)?"
}
}

// MARK: Input Value

func renderAsInputValue() -> String {
return renderAsInputValue(inNullable: true)
}

private func renderAsInputValue(inNullable: Bool) -> String {
switch self {
case .entity, .enum, .scalar, .inputObject:
let typeName = self.rendered(containedInNonNull: true)
return inNullable ? "GraphQLNullable<\(typeName)>" : typeName

case let .nonNull(ofType):
return ofType.renderAsInputValue(inNullable: false)

case let .list(ofType):
let typeName = "[\(ofType.rendered)]"
return inNullable ? "GraphQLNullable<\(typeName)>" : typeName
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import JavaScriptCore

protocol InputValueRenderable {
var name: String { get }
var type: GraphQLType { get }
var hasDefaultValue: Bool { get }
}

extension InputValueRenderable {
func renderInputValueType(includeDefault: Bool = false) -> String {
"\(type.renderAsInputValue())\(isSwiftOptional ? "?" : "")\(includeDefault && hasSwiftNilDefault ? " = nil" : "")"
}

private var isSwiftOptional: Bool {
!isNullable && hasDefaultValue
}

private var hasSwiftNilDefault: Bool {
isNullable && !hasDefaultValue
}

var isNullable: Bool {
switch type {
case .nonNull: return false
default: return true
}
}
}

extension GraphQLInputField: InputValueRenderable {
var hasDefaultValue: Bool {
switch defaultValue {
case .none, .some(nil):
return false
case let .some(value):
guard let value = value as? JSValue else {
fatalError("Cannot determine default value for Input field: \(self)")
}

return !value.isUndefined
}
}
}

extension CompilationResult.VariableDefinition: InputValueRenderable {
var hasDefaultValue: Bool {
switch defaultValue {
case .none, .some(nil), .some(.null): return false
default: return true
}
}
}
Loading