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 multiline deprecation and enum warnings #2579

Merged
merged 5 commits into from
Oct 12, 2022
Merged
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
4 changes: 4 additions & 0 deletions Apollo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
19E9F6AC26D58A9A003AB80E /* OperationMessageIdCreatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E9F6AA26D58A92003AB80E /* OperationMessageIdCreatorTests.swift */; };
19E9F6B526D6BF25003AB80E /* OperationMessageIdCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E9F6A826D5867E003AB80E /* OperationMessageIdCreator.swift */; };
2EE7FFD0276802E30035DC39 /* CacheKeyConstructionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EE7FFCF276802E30035DC39 /* CacheKeyConstructionTests.swift */; };
534A754528EB21D6003291BE /* TemplateString+AvailabilityDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 534A754428EB21D6003291BE /* TemplateString+AvailabilityDeprecated.swift */; };
54DDB0921EA045870009DD99 /* InMemoryNormalizedCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54DDB0911EA045870009DD99 /* InMemoryNormalizedCache.swift */; };
5AC6CA4322AAF7B200B7C94D /* GraphQLHTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AC6CA4222AAF7B200B7C94D /* GraphQLHTTPMethod.swift */; };
5BB2C0232380836100774170 /* VersionNumberTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BB2C0222380836100774170 /* VersionNumberTests.swift */; };
Expand Down Expand Up @@ -1075,6 +1076,7 @@
19E9F6A826D5867E003AB80E /* OperationMessageIdCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationMessageIdCreator.swift; sourceTree = "<group>"; };
19E9F6AA26D58A92003AB80E /* OperationMessageIdCreatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationMessageIdCreatorTests.swift; sourceTree = "<group>"; };
2EE7FFCF276802E30035DC39 /* CacheKeyConstructionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheKeyConstructionTests.swift; sourceTree = "<group>"; };
534A754428EB21D6003291BE /* TemplateString+AvailabilityDeprecated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TemplateString+AvailabilityDeprecated.swift"; sourceTree = "<group>"; };
54DDB0911EA045870009DD99 /* InMemoryNormalizedCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InMemoryNormalizedCache.swift; sourceTree = "<group>"; };
5AC6CA4222AAF7B200B7C94D /* GraphQLHTTPMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphQLHTTPMethod.swift; sourceTree = "<group>"; };
5BB2C0222380836100774170 /* VersionNumberTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionNumberTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2446,6 +2448,7 @@
E674DB40274C0A9B009BB90E /* Glob.swift */,
DE5FD600276923620033EE23 /* TemplateString.swift */,
DED5B35A286CF16600AE6BFF /* TemplateString+CodegenConfiguration.swift */,
534A754428EB21D6003291BE /* TemplateString+AvailabilityDeprecated.swift */,
DE31A437276A78140020DC44 /* Templates */,
E6E3BBDC276A8D6200E5218B /* FileGenerators */,
);
Expand Down Expand Up @@ -4889,6 +4892,7 @@
E6B42D0927A472A700A3BD58 /* SwiftPackageManagerModuleTemplate.swift in Sources */,
DE100B1A28872D0F00BE11C2 /* Documentation.docc in Sources */,
E6D90D07278FA595009CAC5D /* InputObjectFileGenerator.swift in Sources */,
534A754528EB21D6003291BE /* TemplateString+AvailabilityDeprecated.swift in Sources */,
E64F7EB827A0854E0059C021 /* UnionTemplate.swift in Sources */,
E60AE2EE27E3FC6C003C093A /* TemplateRenderer.swift in Sources */,
9BCA8C0926618226004FF2F6 /* UntypedGraphQLRequestBodyCreator.swift in Sources */,
Expand Down
9 changes: 8 additions & 1 deletion Sources/ApolloAPI/GraphQLEnum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/// ``GraphQLEnum`` provides an ``unknown(_:)`` case that is used when the response returns a value
/// that is not recognized as a valid enum case. This is usually caused by future cases added to
/// the enum on the schema after code generation.
public enum GraphQLEnum<T: EnumType>: Hashable, RawRepresentable {
public enum GraphQLEnum<T: EnumType>: CaseIterable, Hashable, RawRepresentable {
public typealias RawValue = String

/// A recognized case of the wrapped enum.
Expand Down Expand Up @@ -61,6 +61,13 @@ public enum GraphQLEnum<T: EnumType>: Hashable, RawRepresentable {
case let .unknown(value): return value
}
}

/// A collection of all known values of the wrapped enum.
/// This collection does not include the `unknown` case.
@inlinable public static var allCases: [GraphQLEnum<T>] {
return T.allCases.map { .case($0) }
}

}

// MARK: CustomScalarType
Expand Down
1 change: 1 addition & 0 deletions Sources/ApolloAPI/SchemaTypes/EnumType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
/// [GraphQLSpec - Enums](https://spec.graphql.org/draft/#sec-Enums)
public protocol EnumType:
RawRepresentable,
CaseIterable,
JSONEncodable,
GraphQLOperationVariableValue
where RawValue == String {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
extension TemplateString.StringInterpolation {

mutating func appendInterpolation(
deprecationReason: String?,
config: ApolloCodegen.ConfigurationContext
) {
guard
config.options.warningsOnDeprecatedUsage == .include,
let deprecationReason = deprecationReason
else {
removeLineIfEmpty()
return
}

let deprecationReasonLines = deprecationReason
.split(separator: "\n", omittingEmptySubsequences: false)

if deprecationReasonLines.count > 1 {
appendInterpolation("""
@available(*, deprecated, message: \"\"\"
\(deprecationReasonLines.joinedAsLines(withIndent: " "))
\"\"\")
""")
} else {
appendInterpolation("""
@available(*, deprecated, message: \"\(deprecationReason)\")
""")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ extension TemplateString.StringInterpolation {
return
}

appendInterpolation(documentation: documentation)
appendInterpolation(forceDocumentation: documentation)
}

}
6 changes: 3 additions & 3 deletions Sources/ApolloCodegenLib/TemplateString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ struct TemplateString: ExpressibleByStringInterpolation, CustomStringConvertible
}

mutating func appendInterpolation(
documentation: String?
forceDocumentation documentation: String?
) {
appendInterpolation(comment: documentation, withLinePrefix: "///")
}
Expand Down Expand Up @@ -318,7 +318,7 @@ func +(lhs: String, rhs: TemplateString) -> TemplateString {

// MARK: - Extensions

fileprivate extension Array where Element == Substring {
extension Array where Element == Substring {
func joinedAsLines(withIndent indent: String) -> String {
var iterator = self.makeIterator()
var string = iterator.next()?.description ?? ""
Expand All @@ -333,7 +333,7 @@ fileprivate extension Array where Element == Substring {
return string
}

func joinedAsCommentLines(withLinePrefix prefix: String) -> String {
fileprivate func joinedAsCommentLines(withLinePrefix prefix: String) -> String {
var string = ""

func add(line: Substring) {
Expand Down
12 changes: 8 additions & 4 deletions Sources/ApolloCodegenLib/Templates/EnumTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@ struct EnumTemplate: TemplateRenderer {
return nil
}

let shouldRenderDocumentation = graphqlEnumValue.documentation != nil &&
config.options.schemaDocumentation == .include

return """
\(documentation: graphqlEnumValue.documentation, config: config)
\(ifLet: graphqlEnumValue.deprecationReason, where: config.options.warningsOnDeprecatedUsage == .include, {"""
@available(*, deprecated, message: \"\($0)\")
"""})
\(if: shouldRenderDocumentation, "\(forceDocumentation: graphqlEnumValue.documentation)")
\(ifLet: graphqlEnumValue.deprecationReason, { """
\(if: shouldRenderDocumentation, "///")
\(forceDocumentation: "**Deprecated**: \($0)")
""" })
\(caseDefinition(for: graphqlEnumValue))
"""
}
Expand Down
5 changes: 1 addition & 4 deletions Sources/ApolloCodegenLib/Templates/InputObjectTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,7 @@ struct InputObjectTemplate: TemplateRenderer {
private func FieldPropertyTemplate(_ field: GraphQLInputField) -> TemplateString {
"""
\(documentation: field.documentation, config: config)
\(ifLet: field.deprecationReason,
where: config.options.warningsOnDeprecatedUsage == .include, {
"@available(*, deprecated, message: \"\($0)\")"
})
\(deprecationReason: field.deprecationReason, config: config)
public var \(field.name.asInputParameterName): \(field.renderInputValueType(config: config.config)) {
get { __data.\(field.name.asInputParameterName) }
set { __data.\(field.name.asInputParameterName) = newValue }
Expand Down
5 changes: 1 addition & 4 deletions Sources/ApolloCodegenLib/Templates/MockObjectTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,7 @@ struct MockObjectTemplate: TemplateRenderer {
public struct MockFields {
\(fields.map {
TemplateString("""
\(ifLet: $0.deprecationReason,
where: config.options.warningsOnDeprecatedUsage == .include, {
"@available(*, deprecated, message: \"\($0)\")"
})
\(deprecationReason: $0.deprecationReason, config: config)
@Field<\($0.type)>("\($0.responseKey)") public var \($0.propertyName)
""")
}, separator: "\n")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,7 @@ struct SelectionSetTemplate {
}()
return """
\(documentation: field.underlyingField.documentation, config: config)
\(ifLet: field.underlyingField.deprecationReason,
where: config.options.warningsOnDeprecatedUsage == .include, {
"@available(*, deprecated, message: \"\($0)\")"
})
\(deprecationReason: field.underlyingField.deprecationReason, config: config)
public var \(field.responseKey.firstLowercased.asFieldAccessorPropertyName): \
\(typeName(for: field, forceOptional: isConditionallyIncluded)) {\
\(if: isMutable,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ class EnumTemplateTests: XCTestCase {

// MARK: Deprecation Tests

func test__render__givenOption_deprecatedInclude_warningsExclude_whenDeprecation_shouldGenerateEnumCase_noAvailableAttribute() throws {
func test__render__givenOption_deprecatedInclude_warningsExclude_whenDeprecation_shouldGenerateEnumCase_withDeprecationComment() throws {
// given / when
buildSubject(
values: [
Expand All @@ -394,6 +394,7 @@ class EnumTemplateTests: XCTestCase {
let expected = """
enum TestEnum: String, EnumType {
case one = "ONE"
/// **Deprecated**: Deprecated for tests
case two = "TWO"
case three = "THREE"
}
Expand All @@ -407,7 +408,7 @@ class EnumTemplateTests: XCTestCase {
expect(actual).to(equalLineByLine(expected))
}

func test__render__givenOption_deprecatedInclude_warningsInclude_whenDeprecation_shouldGenerateEnumCase_withAvailableAttribute() throws {
func test__render__givenOption_deprecatedInclude_warningsInclude_whenDeprecation_shouldGenerateEnumCase_withDeprecationComment() throws {
// given / when
buildSubject(
values: [
Expand All @@ -424,7 +425,7 @@ class EnumTemplateTests: XCTestCase {
let expected = """
enum TestEnum: String, EnumType {
case one = "ONE"
@available(*, deprecated, message: "Deprecated for tests")
/// **Deprecated**: Deprecated for tests
case two = "TWO"
case three = "THREE"
}
Expand Down Expand Up @@ -517,11 +518,12 @@ class EnumTemplateTests: XCTestCase {
/// \(documentation)
enum TestEnum: String, EnumType {
/// Doc: One
@available(*, deprecated, message: "Deprecated for tests")
///
/// **Deprecated**: Deprecated for tests
case one = "ONE"
/// Doc: Two
case two = "TWO"
@available(*, deprecated, message: "Deprecated for tests")
/// **Deprecated**: Deprecated for tests
case three = "THREE"
}

Expand Down Expand Up @@ -555,9 +557,12 @@ class EnumTemplateTests: XCTestCase {
/// \(documentation)
enum TestEnum: String, EnumType {
/// Doc: One
///
/// **Deprecated**: Deprecated for tests
case one = "ONE"
/// Doc: Two
case two = "TWO"
/// **Deprecated**: Deprecated for tests
case three = "THREE"
}

Expand Down Expand Up @@ -589,8 +594,10 @@ class EnumTemplateTests: XCTestCase {

let expected = """
enum TestEnum: String, EnumType {
/// **Deprecated**: Deprecated for tests
case one = "ONE"
case two = "TWO"
/// **Deprecated**: Deprecated for tests
case three = "THREE"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5433,6 +5433,100 @@ class SelectionSetTemplateTests: XCTestCase {
expect(actual).to(equalLineByLine(expected, atLine: 13, ignoringExtraLines: true))
}


func test__render_fieldAccessors__givenWarningsOnDeprecatedUsage_include_hasDeprecatedField_withMultilineDocumentation_shouldGenerateWarningBelowDocumentationWithMultilineLiteral() throws {
// given
schemaSDL = """
type Query {
allAnimals: [Animal!]
}

type Animal {
"This field is a string."
string: String! @deprecated(reason: "Cause I\\nsaid so!")
}
""" // Escaping the backslash is required to allow the frontend to parse correctly this string.
// Removing the escape leads to a "unterminated string literal" error when parsing the schema.

document = """
query TestOperation {
allAnimals {
string
}
}
"""

let expected = #"""
/// This field is a string.
@available(*, deprecated, message: """
Cause I
said so!
""")
public var string: String { __data["string"] }
"""#

// when
try buildSubjectAndOperation(
schemaDocumentation: .include,
warningsOnDeprecatedUsage: .include
)
let allAnimals = try XCTUnwrap(
operation[field: "query"]?[field: "allAnimals"] as? IR.EntityField
)

let actual = subject.render(field: allAnimals)

// then
expect(actual).to(equalLineByLine(expected, atLine: 13, ignoringExtraLines: true))
}

func test__render_fieldAccessors__givenWarningsOnDeprecatedUsage_include_hasDeprecatedField_withMultilineDocumentation_includingEmptyLine_shouldGenerateWarningBelowDocumentationWithMultilineLiteral() throws {
// given
schemaSDL = """
type Query {
allAnimals: [Animal!]
}

type Animal {
"This field is a string."
string: String! @deprecated(reason: "Cause I\\n\\nsaid so!")
}
""" // Escaping the backslash is required to allow the frontend to parse correctly this string.
// Removing the escape leads to a "unterminated string literal" error when parsing the schema.

document = """
query TestOperation {
allAnimals {
string
}
}
"""

let expected = #"""
/// This field is a string.
@available(*, deprecated, message: """
Cause I

said so!
""")
public var string: String { __data["string"] }
"""#

// when
try buildSubjectAndOperation(
schemaDocumentation: .include,
warningsOnDeprecatedUsage: .include
)
let allAnimals = try XCTUnwrap(
operation[field: "query"]?[field: "allAnimals"] as? IR.EntityField
)

let actual = subject.render(field: allAnimals)

// then
expect(actual).to(equalLineByLine(expected, atLine: 13, ignoringExtraLines: true))
}

func test__render_fieldAccessors__givenWarningsOnDeprecatedUsage_exclude_hasDeprecatedField_shouldNotGenerateWarning() throws {
// given
schemaSDL = """
Expand Down
Loading