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

Something to just demonstrate #2

Open
wants to merge 6 commits into
base: loop-through-operation-definitions-once
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
105 changes: 85 additions & 20 deletions Sources/ApolloCodegenLib/ApolloCodegen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class ApolloCodegen {
case invalidSchemaName(_ name: String, message: String)
case targetNameConflict(name: String)
case typeNameConflict(name: String, conflictingName: String, containingObject: String)
case failedToComputeOperationIdentifier(type: String, name: String, error: Swift.Error)

public var errorDescription: String? {
switch self {
Expand Down Expand Up @@ -64,6 +65,8 @@ public class ApolloCodegen {
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
"""
case let .failedToComputeOperationIdentifier(type, name, error):
return "Received failure while computing operation identifier for \(type) named '\(name)', Error: \(error.localizedDescription)"
}
}
}
Expand Down Expand Up @@ -91,6 +94,9 @@ public class ApolloCodegen {

}

/// A `nil` result is treated as a cancellation, and the default operationIdentifier is used
public typealias ComputeOperationIdentifier = (any IROperation, @escaping (Result<String, Swift.Error>?) -> Void) -> Void

/// Executes the code generation engine with a specified configuration.
///
/// - Parameters:
Expand All @@ -103,16 +109,18 @@ public class ApolloCodegen {
public static func build(
with configuration: ApolloCodegenConfiguration,
withRootURL rootURL: URL? = nil,
itemsToGenerate: ItemsToGenerate = [.code]
itemsToGenerate: ItemsToGenerate = [.code],
computeOperationIdentifier: ComputeOperationIdentifier? = nil
) throws {
try build(with: configuration, rootURL: rootURL, itemsToGenerate: itemsToGenerate)
try build(with: configuration, rootURL: rootURL, itemsToGenerate: itemsToGenerate, computeOperationIdentifier: computeOperationIdentifier)
}

internal static func build(
with configuration: ApolloCodegenConfiguration,
rootURL: URL? = nil,
fileManager: ApolloFileManager = .default,
itemsToGenerate: ItemsToGenerate
itemsToGenerate: ItemsToGenerate,
computeOperationIdentifier: ComputeOperationIdentifier? = nil
) throws {

let configContext = ConfigurationContext(
Expand All @@ -131,29 +139,34 @@ public class ApolloCodegen {

let ir = IR(compilationResult: compilationResult)

var existingGeneratedFilePaths: Set<String>?

if itemsToGenerate.contains(.code) && configuration.options.pruneGeneratedFiles {
existingGeneratedFilePaths = try findExistingGeneratedFilePaths(
let generate: () throws -> Void = {
try generateFiles(
compilationResult: compilationResult,
ir: ir,
config: configContext,
fileManager: fileManager
fileManager: fileManager,
itemsToGenerate: itemsToGenerate,
computeOperationIdentifier: computeOperationIdentifier
)
}

try generateFiles(
compilationResult: compilationResult,
ir: ir,
config: configContext,
fileManager: fileManager,
itemsToGenerate: itemsToGenerate
)

if var existingGeneratedFilePaths {
let generateWithPruning: () throws -> Void = {
var existingGeneratedFilePaths = try findExistingGeneratedFilePaths(
config: configContext,
fileManager: fileManager
)
try generate()
try deleteExtraneousGeneratedFiles(
from: &existingGeneratedFilePaths,
afterCodeGenerationUsing: fileManager
)
}

if itemsToGenerate.contains(.code) && configuration.options.pruneGeneratedFiles {
try generateWithPruning()
} else {
try generate()
}
}

// MARK: Internal
Expand Down Expand Up @@ -412,7 +425,8 @@ public class ApolloCodegen {
ir: IR,
config: ConfigurationContext,
fileManager: ApolloFileManager = .default,
itemsToGenerate: ItemsToGenerate
itemsToGenerate: ItemsToGenerate,
computeOperationIdentifier: ComputeOperationIdentifier? = nil
) throws {

if itemsToGenerate.contains(.code) {
Expand All @@ -431,9 +445,40 @@ public class ApolloCodegen {
operationIDsFileGenerator = OperationManifestFileGenerator(config: config)
}

for operation in compilationResult.operations {
let irOperations = compilationResult.operations.map { ir.build(operation: $0) }
var results = [Result<String, Swift.Error>?](repeating: nil, count: irOperations.count)

if let computeOperationIdentifier {
let dispatchGroup = DispatchGroup()
DispatchQueue.concurrentPerform(iterations: irOperations.count) { index in
let irOperation = irOperations[index]
var sources: [String] = [irOperation.definition.source.convertedToSingleLine()]
for fragment in irOperation.referencedFragments {
sources.append(fragment.definition.source.convertedToSingleLine())
}
dispatchGroup.enter()
computeOperationIdentifier(irOperation) { result in
results[index] = result
dispatchGroup.leave()
}
}
dispatchGroup.wait()
}

for (index, irOperation) in irOperations.enumerated() {
try autoreleasepool {
let irOperation = ir.build(operation: operation)
if let result = results[index] {
switch result {
case .success(let operationIdentifier):
irOperation.operationIdentifier = operationIdentifier
case .failure(let error):
throw Error.failedToComputeOperationIdentifier(
type: irOperation.definition.operationType.rawValue,
name: irOperation.definition.name,
error: error
)
}
}

if itemsToGenerate.contains(.code) {
try validateTypeConflicts(for: irOperation.rootField.selectionSet, with: config, in: irOperation.definition.name)
Expand Down Expand Up @@ -611,4 +656,24 @@ public class ApolloCodegen {

}

public protocol IROperation: AnyObject {
var filePath: String { get }
var name: String { get }
var source: String { get }
var type: CompilationResult.OperationType { get }
}

extension IR.Operation: IROperation {
public var filePath: String { definition.filePath }
public var name: String { definition.name }
public var source: String {
var sources: [String] = [definition.source.convertedToSingleLine()]
for fragment in referencedFragments {
sources.append(fragment.definition.source.convertedToSingleLine())
}
return sources.joined(separator: "\n")
}
public var type: CompilationResult.OperationType { definition.operationType }
}

#endif
21 changes: 13 additions & 8 deletions Sources/ApolloCodegenLib/ApolloSchemaDownloader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,13 @@ public struct ApolloSchemaDownloader {
/// - configuration: The `ApolloSchemaDownloadConfiguration` used to download the schema.
/// - rootURL: The root `URL` to resolve relative `URL`s in the configuration's paths against.
/// If `nil`, the current working directory of the executing process will be used.
/// - session: The network session to use for the download. If `nil` the `URLSession.Shared` will be used by default.
/// - Returns: Output from a successful fetch or throws an error.
/// - Throws: Any error which occurs during the fetch.
public static func fetch(
configuration: ApolloSchemaDownloadConfiguration,
withRootURL rootURL: URL? = nil
withRootURL rootURL: URL? = nil,
session: NetworkSession? = nil
) throws {
try ApolloFileManager.default.createContainingDirectoryIfNeeded(
forPath: configuration.outputPath
Expand All @@ -63,14 +65,16 @@ public struct ApolloSchemaDownloader {
httpMethod: httpMethod,
includeDeprecatedInputValues: includeDeprecatedInputValues,
configuration: configuration,
withRootURL: rootURL
withRootURL: rootURL,
session: session
)

case .apolloRegistry(let settings):
try self.downloadFrom(
registry: settings,
configuration: configuration,
withRootURL: rootURL
withRootURL: rootURL,
session: session
)
}
}
Expand Down Expand Up @@ -136,7 +140,8 @@ public struct ApolloSchemaDownloader {
static func downloadFrom(
registry: ApolloSchemaDownloadConfiguration.DownloadMethod.ApolloRegistrySettings,
configuration: ApolloSchemaDownloadConfiguration,
withRootURL rootURL: URL?
withRootURL rootURL: URL?,
session: NetworkSession? = nil
) throws {
CodegenLogger.log("Downloading schema from registry", logLevel: .debug)

Expand All @@ -145,7 +150,7 @@ public struct ApolloSchemaDownloader {
.parentFolderURL()
.appendingPathComponent("registry_response.json")

try URLDownloader().downloadSynchronously(
try URLDownloader(session: session).downloadSynchronously(
urlRequest,
to: jsonOutputURL,
timeout: configuration.downloadTimeout
Expand Down Expand Up @@ -339,7 +344,8 @@ public struct ApolloSchemaDownloader {
httpMethod: ApolloSchemaDownloadConfiguration.DownloadMethod.HTTPMethod,
includeDeprecatedInputValues: Bool,
configuration: ApolloSchemaDownloadConfiguration,
withRootURL: URL?
withRootURL: URL?,
session: NetworkSession? = nil
) throws {

CodegenLogger.log("Downloading schema via introspection from \(endpoint)", logLevel: .debug)
Expand All @@ -361,8 +367,7 @@ public struct ApolloSchemaDownloader {
}
}()


try URLDownloader().downloadSynchronously(
try URLDownloader(session: session).downloadSynchronously(
urlRequest,
to: jsonOutputURL,
timeout: configuration.downloadTimeout
Expand Down
22 changes: 11 additions & 11 deletions Sources/ApolloCodegenLib/Frontend/CompilationResult.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import JavaScriptCore

/// The output of the frontend compiler.
public class CompilationResult: JavaScriptObject {
public final class CompilationResult: JavaScriptObject {
private enum Constants {
static let LocalCacheMutationDirectiveName = "apollo_client_ios_localCacheMutation"
}
Expand All @@ -15,15 +15,15 @@ public class CompilationResult: JavaScriptObject {

lazy var schemaDocumentation: String? = self["schemaDocumentation"]

public class RootTypeDefinition: JavaScriptObject {
public final class RootTypeDefinition: JavaScriptObject {
lazy var queryType: GraphQLNamedType = self["queryType"]

lazy var mutationType: GraphQLNamedType? = self["mutationType"]

lazy var subscriptionType: GraphQLNamedType? = self["subscriptionType"]
}

public class OperationDefinition: JavaScriptObject, Hashable {
public final class OperationDefinition: JavaScriptObject, Hashable {
lazy var name: String = self["name"]

lazy var operationType: OperationType = self["operationType"]
Expand Down Expand Up @@ -94,15 +94,15 @@ public class CompilationResult: JavaScriptObject {
}
}

public class VariableDefinition: JavaScriptObject {
public final class VariableDefinition: JavaScriptObject {
lazy var name: String = self["name"]

lazy var type: GraphQLType = self["type"]

lazy var defaultValue: GraphQLValue? = self["defaultValue"]
}

public class FragmentDefinition: JavaScriptObject, Hashable {
public final class FragmentDefinition: JavaScriptObject, Hashable {
lazy var name: String = self["name"]

lazy var type: GraphQLCompositeType = self["typeCondition"]
Expand Down Expand Up @@ -132,7 +132,7 @@ public class CompilationResult: JavaScriptObject {
}
}

public class SelectionSet: JavaScriptWrapper, Hashable, CustomDebugStringConvertible {
public final class SelectionSet: JavaScriptWrapper, Hashable, CustomDebugStringConvertible {
lazy var parentType: GraphQLCompositeType = self["parentType"]

lazy var selections: [Selection] = self["selections"]
Expand Down Expand Up @@ -165,7 +165,7 @@ public class CompilationResult: JavaScriptObject {
}
}

public class InlineFragment: JavaScriptObject, Hashable {
public final class InlineFragment: JavaScriptObject, Hashable {
lazy var selectionSet: SelectionSet = self["selectionSet"]

lazy var inclusionConditions: [InclusionCondition]? = self["inclusionConditions"]
Expand All @@ -187,7 +187,7 @@ public class CompilationResult: JavaScriptObject {

/// Represents an individual selection that includes a named fragment in a selection set.
/// (ie. `...FragmentName`)
public class FragmentSpread: JavaScriptObject, Hashable {
public final class FragmentSpread: JavaScriptObject, Hashable {
lazy var fragment: FragmentDefinition = self["fragment"]

lazy var inclusionConditions: [InclusionCondition]? = self["inclusionConditions"]
Expand Down Expand Up @@ -253,7 +253,7 @@ public class CompilationResult: JavaScriptObject {
}
}

public class Field: JavaScriptWrapper, Hashable, CustomDebugStringConvertible {
public final class Field: JavaScriptWrapper, Hashable, CustomDebugStringConvertible {
lazy var name: String = self["name"]!

lazy var alias: String? = self["alias"]
Expand Down Expand Up @@ -330,7 +330,7 @@ public class CompilationResult: JavaScriptObject {
}
}

public class Argument: JavaScriptObject, Hashable {
public final class Argument: JavaScriptObject, Hashable {
lazy var name: String = self["name"]

lazy var type: GraphQLType = self["type"]
Expand All @@ -352,7 +352,7 @@ public class CompilationResult: JavaScriptObject {
}
}

public class Directive: JavaScriptObject, Hashable {
public final class Directive: JavaScriptObject, Hashable {
lazy var name: String = self["name"]

lazy var arguments: [Argument]? = self["arguments"]
Expand Down
Loading