Skip to content

Commit

Permalink
feat: Codegen config access modifier (#2917)
Browse files Browse the repository at this point in the history
  • Loading branch information
calvincestari committed May 15, 2023
1 parent 2e8bd2c commit c4600ff
Show file tree
Hide file tree
Showing 50 changed files with 2,301 additions and 553 deletions.
4 changes: 4 additions & 0 deletions Apollo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,7 @@
E660A4D529CAEA0F001EA373 /* MockInterceptorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E660A4D429CAEA0F001EA373 /* MockInterceptorProvider.swift */; };
E6630B8C26F0639B002D9E41 /* MockNetworkSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6D79AB926EC05290094434A /* MockNetworkSession.swift */; };
E6630B8E26F071F9002D9E41 /* SchemaRegistryApolloSchemaDownloaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6630B8D26F071F9002D9E41 /* SchemaRegistryApolloSchemaDownloaderTests.swift */; };
E66644F129D7D2AE005E9140 /* MockTemplateRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E66644EF29D7D27C005E9140 /* MockTemplateRenderer.swift */; };
E669352B2803EE11004E1FFC /* CustomScalarTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E669352A2803EE11004E1FFC /* CustomScalarTemplate.swift */; };
E669352D2803EF67004E1FFC /* CustomScalarTemplateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E669352C2803EF67004E1FFC /* CustomScalarTemplateTests.swift */; };
E669352F2803F09C004E1FFC /* CustomScalarFileGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E669352E2803F09C004E1FFC /* CustomScalarFileGenerator.swift */; };
Expand Down Expand Up @@ -1931,6 +1932,7 @@
E660A4D229CA5890001EA373 /* MockHTTPResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockHTTPResponse.swift; sourceTree = "<group>"; };
E660A4D429CAEA0F001EA373 /* MockInterceptorProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockInterceptorProvider.swift; sourceTree = "<group>"; };
E6630B8D26F071F9002D9E41 /* SchemaRegistryApolloSchemaDownloaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SchemaRegistryApolloSchemaDownloaderTests.swift; sourceTree = "<group>"; };
E66644EF29D7D27C005E9140 /* MockTemplateRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTemplateRenderer.swift; sourceTree = "<group>"; };
E669352A2803EE11004E1FFC /* CustomScalarTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomScalarTemplate.swift; sourceTree = "<group>"; };
E669352C2803EF67004E1FFC /* CustomScalarTemplateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomScalarTemplateTests.swift; sourceTree = "<group>"; };
E669352E2803F09C004E1FFC /* CustomScalarFileGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomScalarFileGenerator.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3878,6 +3880,7 @@
DE5FD5FE276922AA0033EE23 /* MockApolloCodegenConfiguration.swift */,
E6BEEFA527FAB1C700D94FF4 /* MockFileGenerator.swift */,
E6EFDD0C27E8377200B17FE5 /* MockFileTemplate.swift */,
E66644EF29D7D27C005E9140 /* MockTemplateRenderer.swift */,
DE5B313F27A482C80051C9D3 /* Resources.swift */,
DECD490E262F81BF00924527 /* Info.plist */,
);
Expand Down Expand Up @@ -5775,6 +5778,7 @@
DEE2060B27E14498002B4B82 /* IR+InclusionConditionsMock.swift in Sources */,
DE01451628A5A971000F6F18 /* MockValidationOptions.swift in Sources */,
E6BF98FC272C8FFC00C1FED8 /* MockFileManager.swift in Sources */,
E66644F129D7D2AE005E9140 /* MockTemplateRenderer.swift in Sources */,
E6BEEFA627FAB1C700D94FF4 /* MockFileGenerator.swift in Sources */,
DECD4921262F81CE00924527 /* CodegenTestHelper.swift in Sources */,
DEAFB78327064F6900BE02F3 /* MockGraphQLType.swift in Sources */,
Expand Down
8 changes: 4 additions & 4 deletions Sources/ApolloCodegenLib/ApolloCodegen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ public class ApolloCodegen {
""")
}

if case let .embeddedInTarget(targetName) = context.output.schemaTypes.moduleType,
if case let .embeddedInTarget(targetName, _) = context.output.schemaTypes.moduleType,
SwiftKeywords.DisallowedEmbeddedTargetNames.contains(targetName.lowercased()) {
throw Error.targetNameConflict(name: targetName)
}
Expand Down Expand Up @@ -430,13 +430,13 @@ public class ApolloCodegen {
switch config.output.operations {
case .inSchemaModule: break

case let .absolute(operationsPath):
case let .absolute(operationsPath, _):
globs.append(Glob(
["\(operationsPath)/**/*.graphql.swift"],
relativeTo: config.rootURL
))

case let .relative(subpath):
case let .relative(subpath, _):
let searchPaths = config.input.operationSearchPaths.map { searchPath -> String in
let startOfLastPathComponent = searchPath.lastIndex(of: "/") ?? searchPath.firstIndex(of: ".")!
var path = searchPath.prefix(upTo: startOfLastPathComponent)
Expand All @@ -454,7 +454,7 @@ public class ApolloCodegen {
}

switch config.output.testMocks {
case let .absolute(testMocksPath):
case let .absolute(testMocksPath, _):
globs.append(Glob(
["\(testMocksPath)/**/*.graphql.swift"],
relativeTo: config.rootURL
Expand Down
151 changes: 143 additions & 8 deletions Sources/ApolloCodegenLib/ApolloCodegenConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,16 @@ public struct ApolloCodegenConfiguration: Codable, Equatable {
}
}

/// Swift access control configuration.
public enum AccessModifier: String, Codable, Equatable {
/// Enable entities to be used within any source file from their defining module, and also in
/// a source file from another module that imports the defining module.
case `public`
/// Enable entities to be used within any source file from their defining module, but not in
/// any source file outside of that module.
case `internal`
}

/// The local path structure for the generated schema types files.
public struct SchemaTypesFileOutput: Codable, Equatable {
/// Local path where the generated schema types files should be stored.
Expand All @@ -245,12 +255,13 @@ public struct ApolloCodegenConfiguration: Codable, Equatable {
/// Compatible dependency manager automation.
public enum ModuleType: Codable, Equatable {
/// Generated schema types will be manually embedded in a target with the specified `name`.
/// No module will be created for the generated schema types.
/// No module will be created for the generated schema types. Use `accessModifier` to control
/// the visibility of generated code, defaults to `.internal`.
///
/// - Note: Generated files must be manually added to your application target. The generated
/// schema types files will be namespaced with the value of your configuration's
/// `schemaNamespace` to prevent naming conflicts.
case embeddedInTarget(name: String)
case embeddedInTarget(name: String, accessModifier: AccessModifier = .internal)
/// Generates a `Package.swift` file that is suitable for linking the generated schema types
/// files to your project using Swift Package Manager.
case swiftPackageManager
Expand All @@ -263,6 +274,40 @@ public struct ApolloCodegenConfiguration: Codable, Equatable {
/// create the podspec file that is expecting the generated files in the configured output
/// location.
case other

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

guard let key = container.allKeys.first else {
throw DecodingError.typeMismatch(Self.self, DecodingError.Context.init(
codingPath: container.codingPath,
debugDescription: "Invalid number of keys found, expected one.",
underlyingError: nil
))
}

switch key {
case .embeddedInTarget:
let nestedContainer = try container.nestedContainer(
keyedBy: EmbeddedInTargetCodingKeys.self,
forKey: .embeddedInTarget
)

let name = try nestedContainer.decode(String.self, forKey: .name)
let accessModifier = try nestedContainer.decodeIfPresent(
AccessModifier.self,
forKey: .accessModifier
) ?? .internal

self = .embeddedInTarget(name: name, accessModifier: accessModifier)

case .swiftPackageManager:
self = .swiftPackageManager

case .other:
self = .other
}
}
}
}

Expand All @@ -273,23 +318,71 @@ public struct ApolloCodegenConfiguration: Codable, Equatable {
/// Operation object files will be co-located relative to the defining operation `.graphql`
/// file. If `subpath` is specified a subfolder will be created relative to the `.graphql` file
/// and the operation object files will be generated there. If no `subpath` is defined then all
/// operation object files will be generated alongside the `.graphql` file.
case relative(subpath: String?)
/// All operation object files will be located in the specified path.
case absolute(path: String)
/// operation object files will be generated alongside the `.graphql` file. Use `accessModifier`
/// to control the visibility of generated code, defaults to `.public`.
case relative(subpath: String? = nil, accessModifier: AccessModifier = .public)
/// All operation object files will be located in the specified `path`. Use `accessModifier` to
/// control the visibility of generated code, defaults to `.public`.
case absolute(path: String, accessModifier: AccessModifier = .public)

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

guard let key = container.allKeys.first else {
throw DecodingError.typeMismatch(Self.self, DecodingError.Context.init(
codingPath: container.codingPath,
debugDescription: "Invalid number of keys found, expected one.",
underlyingError: nil
))
}

switch key {
case .inSchemaModule:
self = .inSchemaModule

case .relative:
let nestedContainer = try container.nestedContainer(
keyedBy: RelativeCodingKeys.self,
forKey: .relative
)

let subpath = try nestedContainer.decodeIfPresent(String.self, forKey: .subpath)
let accessModifier = try nestedContainer.decodeIfPresent(
AccessModifier.self,
forKey: .accessModifier
) ?? .public

self = .relative(subpath: subpath, accessModifier: accessModifier)

case .absolute:
let nestedContainer = try container.nestedContainer(
keyedBy: AbsoluteCodingKeys.self,
forKey: .absolute
)

let path = try nestedContainer.decode(String.self, forKey: .path)
let accessModifier = try nestedContainer.decodeIfPresent(
AccessModifier.self,
forKey: .accessModifier
) ?? .public

self = .absolute(path: path, accessModifier: accessModifier)
}
}
}

/// The local path structure for the generated test mock object files.
public enum TestMockFileOutput: Codable, Equatable {
/// Test mocks will not be generated. This is the default value.
case none
/// Generated test mock files will be located in the specified path.
/// Generated test mock files will be located in the specified `path`. Use `accessModifier` to
/// control the visibility of generated code, defaults to `.public`.
/// No module will be created for the generated test mocks.
///
/// - Note: Generated files must be manually added to your test target. Test mocks generated
/// this way may also be manually embedded in a test utility module that is imported by your
/// test target.
case absolute(path: String)
case absolute(path: String, accessModifier: AccessModifier = .public)
/// Generated test mock files will be included in a target defined in the generated
/// `Package.swift` file that is suitable for linking the generated test mock files to your
/// test target using Swift Package Manager.
Expand All @@ -301,6 +394,47 @@ public struct ApolloCodegenConfiguration: Codable, Equatable {
/// If this option is provided without the `.swiftPackageManager` module type, code generation
/// will fail.
case swiftPackage(targetName: String? = nil)

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

guard let key = container.allKeys.first else {
throw DecodingError.typeMismatch(Self.self, DecodingError.Context.init(
codingPath: container.codingPath,
debugDescription: "Invalid number of keys found, expected one.",
underlyingError: nil
))
}

switch key {
case .none:
self = .none

case .absolute:
let nestedContainer = try container.nestedContainer(
keyedBy: AbsoluteCodingKeys.self,
forKey: .absolute
)

let path = try nestedContainer.decode(String.self, forKey: .path)
let accessModifier = try nestedContainer.decodeIfPresent(
AccessModifier.self,
forKey: .accessModifier
) ?? .public

self = .absolute(path: path, accessModifier: accessModifier)

case .swiftPackage:
let nestedContainer = try container.nestedContainer(
keyedBy: SwiftPackageCodingKeys.self,
forKey: .swiftPackage
)

let targetName = try nestedContainer.decode(String.self, forKey: .targetName)

self = .swiftPackage(targetName: targetName)
}
}
}

// MARK: - Other Types
Expand Down Expand Up @@ -872,6 +1006,7 @@ extension ApolloCodegenConfiguration.OutputOptions {
}

// MARK: - SelectionSetInitializers - Private Implementation

extension ApolloCodegenConfiguration.SelectionSetInitializers {
struct Options: OptionSet, Codable, Equatable {
let rawValue: Int
Expand Down
10 changes: 5 additions & 5 deletions Sources/ApolloCodegenLib/FileGenerators/FileGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,11 @@ enum FileTarget: Equatable {

return url.appendingPathComponent(subpath).path

case let .absolute(path):
case let .absolute(path, _):
return URL(fileURLWithPath: path, relativeTo: config.rootURL)
.appendingPathComponent(subpath).path

case let .relative(subpath):
case let .relative(subpath, _):
return resolveRelativePath(
sourceURL: URL(fileURLWithPath: fragment.filePath),
withSubpath: subpath
Expand Down Expand Up @@ -175,11 +175,11 @@ enum FileTarget: Equatable {
.appendingPathComponent(subpath)
.path

case let .absolute(path):
case let .absolute(path, _):
return URL(fileURLWithPath: path, relativeTo: config.rootURL)
.appendingPathComponent(subpath).path

case let .relative(subpath):
case let .relative(subpath, _):
return resolveRelativePath(
sourceURL: URL(fileURLWithPath: operation.filePath),
withSubpath: subpath
Expand All @@ -196,7 +196,7 @@ enum FileTarget: Equatable {
case let .swiftPackage(targetName):
return URL(fileURLWithPath: config.output.schemaTypes.path, relativeTo: config.rootURL)
.appendingPathComponent(targetName ?? "TestMocks").path
case let .absolute(path):
case let .absolute(path, _):
return URL(fileURLWithPath: path, relativeTo: config.rootURL).path
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct CustomScalarTemplate: TemplateRenderer {
TemplateString(
"""
\(documentation: documentationTemplate, config: config)
\(embeddedAccessControlModifier)\
\(accessControlModifier(for: .parent))\
typealias \(graphqlScalar.name.firstUppercased) = String
"""
Expand Down
2 changes: 1 addition & 1 deletion Sources/ApolloCodegenLib/Templates/EnumTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct EnumTemplate: TemplateRenderer {
TemplateString(
"""
\(documentation: graphqlEnum.documentation, config: config)
\(embeddedAccessControlModifier)\
\(accessControlModifier(for: .parent))\
enum \(graphqlEnum.name.firstUppercased): String, EnumType {
\(graphqlEnum.values.compactMap({
enumCase(for: $0)
Expand Down
8 changes: 5 additions & 3 deletions Sources/ApolloCodegenLib/Templates/FragmentTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@ struct FragmentTemplate: TemplateRenderer {

return TemplateString(
"""
\(embeddedAccessControlModifier)\
\(accessControlModifier(for: .parent))\
struct \(fragment.generatedDefinitionName): \
\(definition.renderedSelectionSetType(config)), Fragment {
public static var fragmentDefinition: StaticString { ""\"
\(accessControlModifier(for: .member))\
static var fragmentDefinition: StaticString { ""\"
\(fragment.definition.source)
""\" }
\(SelectionSetTemplate(
definition: definition,
generateInitializers: config.options.shouldGenerateSelectionSetInitializers(for: fragment),
config: config
config: config,
renderAccessControl: { accessControlModifier(for: .member) }()
).renderBody())
}
Expand Down
14 changes: 8 additions & 6 deletions Sources/ApolloCodegenLib/Templates/InputObjectTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,21 @@ struct InputObjectTemplate: TemplateRenderer {

var template: TemplateString {
let (validFields, deprecatedFields) = filterFields(graphqlInputObject.fields)
let memberAccessControl = accessControlModifier(for: .member)

return TemplateString(
"""
\(documentation: graphqlInputObject.documentation, config: config)
\(embeddedAccessControlModifier)\
\(accessControlModifier(for: .parent))\
struct \(graphqlInputObject.name.firstUppercased): InputObject {
public private(set) var __data: InputDict
\(memberAccessControl)private(set) var __data: InputDict
public init(_ data: InputDict) {
\(memberAccessControl)init(_ data: InputDict) {
__data = data
}
\(if: !deprecatedFields.isEmpty && !validFields.isEmpty && shouldIncludeDeprecatedWarnings, """
public init(
\(memberAccessControl)init(
\(InitializerParametersTemplate(validFields))
) {
__data = InputDict([
Expand All @@ -38,7 +39,7 @@ struct InputObjectTemplate: TemplateRenderer {
\(if: !deprecatedFields.isEmpty && shouldIncludeDeprecatedWarnings, """
@available(*, deprecated, message: "\(deprecatedMessage(for: deprecatedFields))")
""")
public init(
\(memberAccessControl)init(
\(InitializerParametersTemplate(graphqlInputObject.fields))
) {
__data = InputDict([
Expand Down Expand Up @@ -108,7 +109,8 @@ struct InputObjectTemplate: TemplateRenderer {
"""
\(documentation: field.documentation, config: config)
\(deprecationReason: field.deprecationReason, config: config)
public var \(field.name.asFieldPropertyName): \(field.renderInputValueType(config: config.config)) {
\(accessControlModifier(for: .member))\
var \(field.name.asFieldPropertyName): \(field.renderInputValueType(config: config.config)) {
get { __data["\(field.name)"] }
set { __data["\(field.name)"] = newValue }
}
Expand Down

0 comments on commit c4600ff

Please sign in to comment.