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

fix: Schema/Target/Module name with spaces breaks generated code #2760

Merged
merged 8 commits into from
Jan 9, 2023
2 changes: 1 addition & 1 deletion Plugins/InstallCLI/InstallCLIPluginCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct InstallCLIPluginCommand: CommandPlugin {
let task = Process()
task.standardInput = nil
task.environment = ProcessInfo.processInfo.environment
task.arguments = ["-c", "ln -f -s \(from.string) \(to.string)"]
task.arguments = ["-c", "ln -f -s '\(from.string)' '\(to.string)'"]
task.executableURL = URL(fileURLWithPath: "/bin/zsh")
try task.run()
task.waitUntilExit()
Expand Down
58 changes: 43 additions & 15 deletions Sources/ApolloCodegenLib/ApolloCodegen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class ApolloCodegen {
case cannotLoadSchema
case cannotLoadOperations
case invalidConfiguration(message: String)
case invalidSchemaName(_ name: String, message: String)

public var errorDescription: String? {
switch self {
Expand Down Expand Up @@ -47,6 +48,8 @@ public class ApolloCodegen {
return "No GraphQL operations could be found. Please verify the operation search paths."
case let .invalidConfiguration(message):
return "The codegen configuration has conflicting values: \(message)"
case let .invalidSchemaName(name, message):
return "The schema name `\(name)` is invalid: \(message)"
}
}
}
Expand Down Expand Up @@ -76,14 +79,14 @@ public class ApolloCodegen {
rootURL: rootURL
)

try validate(config: configContext)
try validate(configContext)

let compilationResult = try compileGraphQLResult(
configContext,
experimentalFeatures: configuration.experimentalFeatures
)

try validate(schemaName: configContext.schemaName, compilationResult: compilationResult)
try validate(configContext, with: compilationResult)

let ir = IR(compilationResult: compilationResult)

Expand Down Expand Up @@ -130,26 +133,50 @@ public class ApolloCodegen {
}
}

/// Performs validation against deterministic errors that will cause code generation to fail.
static func validate(config: ConfigurationContext) throws {
if case .swiftPackage = config.output.testMocks,
config.output.schemaTypes.moduleType != .swiftPackageManager {
/// Validates the configuration against deterministic errors that will cause code generation to
/// fail. This validation step does not take into account schema and operation specific types, it
/// is only a static analysis of the configuration.
///
/// - Parameter config: Code generation configuration settings.
public static func _validate(config: ApolloCodegenConfiguration) throws {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The public method prefixed with _ to indicate that while it's public it is still considered private.

try validate(ConfigurationContext(config: config))
}

static private func validate(_ context: ConfigurationContext) throws {
guard
!context.schemaName.isEmpty,
!context.schemaName.contains(where: { $0.isWhitespace })
else {
throw Error.invalidSchemaName(context.schemaName, message: """
Cannot be empty nor contain spaces. If your schema name has spaces consider replacing them \
with the underscore character.
""")
}

guard
!SwiftKeywords.DisallowedSchemaNamespaceNames.contains(context.schemaName.lowercased())
else {
throw Error.schemaNameConflict(name: context.schemaName)
}

if case .swiftPackage = context.output.testMocks,
context.output.schemaTypes.moduleType != .swiftPackageManager {
throw Error.testMocksInvalidSwiftPackageConfiguration
}

if case .swiftPackageManager = config.output.schemaTypes.moduleType,
config.options.cocoapodsCompatibleImportStatements == true {
if case .swiftPackageManager = context.output.schemaTypes.moduleType,
context.options.cocoapodsCompatibleImportStatements == true {
throw Error.invalidConfiguration(message: """
cocoapodsCompatibleImportStatements cannot be set to 'true' when the output schema types \
module type is Swift Package Manager. Change the cocoapodsCompatibleImportStatements \
value to 'false', or choose a different module type, to resolve the conflict.
""")
}

for searchPath in config.input.schemaSearchPaths {
for searchPath in context.input.schemaSearchPaths {
try validate(inputSearchPath: searchPath)
}
for searchPath in config.input.operationSearchPaths {
for searchPath in context.input.operationSearchPaths {
try validate(inputSearchPath: searchPath)
}
}
Expand All @@ -160,17 +187,18 @@ public class ApolloCodegen {
}
}

static func validate(schemaName: String, compilationResult: CompilationResult) throws {
/// Validates the configuration context against the GraphQL compilation result, checking for
/// configuration errors that are dependent on the schema and operations.
static func validate(_ context: ConfigurationContext, with compilationResult: CompilationResult) throws {
guard
!SwiftKeywords.DisallowedSchemaNamespaceNames.contains(schemaName.lowercased()),
!compilationResult.referencedTypes.contains(where: { namedType in
namedType.swiftName == schemaName.firstUppercased
namedType.swiftName == context.schemaName.firstUppercased
}),
!compilationResult.fragments.contains(where: { fragmentDefinition in
fragmentDefinition.name == schemaName.firstUppercased
fragmentDefinition.name == context.schemaName.firstUppercased
})
else {
throw Error.schemaNameConflict(name: schemaName)
throw Error.schemaNameConflict(name: context.schemaName)
}
}

Expand Down
14 changes: 5 additions & 9 deletions Sources/CodegenCLI/Commands/Initialize.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,6 @@ public struct Initialize: ParsableCommand {
public init() { }

public func validate() throws {
guard !schemaName.trimmingCharacters(in: .whitespaces).isEmpty else {
throw ValidationError("--schema-name value cannot be empty.")
}

switch (moduleType, targetName?.isEmpty) {
case (.embeddedInTarget, nil), (.embeddedInTarget, true):
throw ValidationError("""
Expand All @@ -86,11 +82,11 @@ public struct Initialize: ParsableCommand {

func _run(fileManager: ApolloFileManager = .default, output: OutputClosure? = nil) throws {
let encoded = try ApolloCodegenConfiguration
.minimalJSON(
schemaName: schemaName,
moduleType: moduleType,
targetName: targetName
).asData()
.minimalJSON(schemaName: schemaName, moduleType: moduleType, targetName: targetName)
.asData()

let decoded = try JSONDecoder().decode(ApolloCodegenConfiguration.self, from: encoded)
calvincestari marked this conversation as resolved.
Show resolved Hide resolved
try ApolloCodegen._validate(config: decoded)

if print {
try print(data: encoded, output: output)
Expand Down
Loading