Skip to content
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
1 change: 1 addition & 0 deletions .idea/dictionaries/project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ class IncompatibleDefinition(
/**
* Fields have different shapes and cannot be merged
*
* There is a discussion about whether this can be relaxed.
* See https://github.com/apollographql/apollo-kotlin/issues/4320.
*/
class DifferentShape(override val message: String, override val sourceLocation: SourceLocation?) : GraphQLValidationIssue

Expand Down
18 changes: 13 additions & 5 deletions libraries/apollo-compiler/api/apollo-compiler.api
Original file line number Diff line number Diff line change
Expand Up @@ -286,16 +286,15 @@ public final class com/apollographql/apollo/compiler/InputFile {

public final class com/apollographql/apollo/compiler/IrOptions {
public static final field Companion Lcom/apollographql/apollo/compiler/IrOptions$Companion;
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Set;Ljava/lang/String;)V
public fun <init> (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/util/Map;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Set;Ljava/lang/String;)V
public final fun getAddTypename ()Ljava/lang/String;
public final fun getAlwaysGenerateTypesMatching ()Ljava/util/Set;
public final fun getCodegenModels ()Ljava/lang/String;
public final fun getDecapitalizeFields ()Ljava/lang/Boolean;
public final fun getFailOnWarnings ()Ljava/lang/Boolean;
public final fun getFieldsOnDisjointTypesMustMerge ()Ljava/lang/Boolean;
public final fun getFlattenModels ()Ljava/lang/Boolean;
public final fun getGenerateOptionalOperationVariables ()Ljava/lang/Boolean;
public final fun getWarnOnDeprecatedUsages ()Ljava/lang/Boolean;
public final fun getIssueSeverities ()Ljava/util/Map;
}

public final class com/apollographql/apollo/compiler/IrOptions$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
Expand All @@ -312,6 +311,15 @@ public final class com/apollographql/apollo/compiler/IrOptions$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class com/apollographql/apollo/compiler/IssueSeverity : java/lang/Enum {
public static final field Error Lcom/apollographql/apollo/compiler/IssueSeverity;
public static final field Ignore Lcom/apollographql/apollo/compiler/IssueSeverity;
public static final field Warn Lcom/apollographql/apollo/compiler/IssueSeverity;
public static fun getEntries ()Lkotlin/enums/EnumEntries;
public static fun valueOf (Ljava/lang/String;)Lcom/apollographql/apollo/compiler/IssueSeverity;
public static fun values ()[Lcom/apollographql/apollo/compiler/IssueSeverity;
}

public abstract interface class com/apollographql/apollo/compiler/JavaCodegenOpt {
public abstract fun getClassesForEnumsMatching ()Ljava/util/List;
public abstract fun getGenerateModelBuilders ()Ljava/lang/Boolean;
Expand Down Expand Up @@ -391,8 +399,8 @@ public final class com/apollographql/apollo/compiler/OptionsKt {
public static final field MODELS_RESPONSE_BASED Ljava/lang/String;
public static final fun buildCodegenOptions (Lcom/apollographql/apollo/compiler/TargetLanguage;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/util/List;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/util/List;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/util/List;Ljava/lang/Boolean;Lcom/apollographql/apollo/compiler/JavaNullable;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;)Lcom/apollographql/apollo/compiler/CodegenOptions;
public static synthetic fun buildCodegenOptions$default (Lcom/apollographql/apollo/compiler/TargetLanguage;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/util/List;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/String;Ljava/util/List;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/util/List;Ljava/lang/Boolean;Lcom/apollographql/apollo/compiler/JavaNullable;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/apollographql/apollo/compiler/CodegenOptions;
public static final fun buildIrOptions (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Set;Ljava/lang/String;)Lcom/apollographql/apollo/compiler/IrOptions;
public static synthetic fun buildIrOptions$default (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Set;Ljava/lang/String;ILjava/lang/Object;)Lcom/apollographql/apollo/compiler/IrOptions;
public static final fun buildIrOptions (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Set;Ljava/lang/String;Ljava/util/Map;)Lcom/apollographql/apollo/compiler/IrOptions;
public static synthetic fun buildIrOptions$default (Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/Boolean;Ljava/util/Set;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Lcom/apollographql/apollo/compiler/IrOptions;
public static final fun validate (Lcom/apollographql/apollo/compiler/CodegenOptions;)V
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
package com.apollographql.apollo.compiler

import com.apollographql.apollo.annotations.ApolloDeprecatedSince
import com.apollographql.apollo.ast.DeprecatedUsage
import com.apollographql.apollo.ast.DifferentShape
import com.apollographql.apollo.ast.DirectiveRedefinition
import com.apollographql.apollo.ast.ForeignSchema
import com.apollographql.apollo.ast.GQLDefinition
import com.apollographql.apollo.ast.GQLDocument
import com.apollographql.apollo.ast.GQLFragmentDefinition
import com.apollographql.apollo.ast.GQLOperationDefinition
import com.apollographql.apollo.ast.GQLSchemaDefinition
import com.apollographql.apollo.ast.GQLTypeDefinition
import com.apollographql.apollo.ast.IncompatibleDefinition
import com.apollographql.apollo.ast.Issue
import com.apollographql.apollo.ast.ParserOptions
import com.apollographql.apollo.ast.QueryDocumentMinifier
import com.apollographql.apollo.ast.UnusedFragment
import com.apollographql.apollo.ast.UnusedVariable
import com.apollographql.apollo.ast.builtinForeignSchemas
import com.apollographql.apollo.ast.checkEmpty
import com.apollographql.apollo.ast.internal.SchemaValidationOptions
Expand Down Expand Up @@ -55,11 +49,13 @@ object ApolloCompiler {
fun debug(message: String)
fun info(message: String)
fun warning(message: String)

@Deprecated("use warning instead", replaceWith = ReplaceWith("warning(message)"))
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v5_0_0)
fun warn(message: String) {
warning(message)
}

fun error(message: String)
}

Expand Down Expand Up @@ -106,7 +102,7 @@ object ApolloCompiler {
}
val mainSchemaDocument = mainSchemaDocuments.single()

// Sort the other schema document as type extensions are order sensitive, and we want this to be under the user control
// Sort the other schema document as type extensions are order-sensitive, and we want this to be under the user control
val otherSchemaDocumentSorted = otherSchemaDocuments.sortedBy { it.sourceLocation?.filePath?.substringAfterLast(File.pathSeparator) }

val schemaDefinitions = (listOf(mainSchemaDocument) + otherSchemaDocumentSorted).flatMap { it.definitions }
Expand Down Expand Up @@ -154,7 +150,8 @@ object ApolloCompiler {
)
)

val issueGroup = result.issues.group(warnOnDeprecatedUsages = true, fieldsOnDisjointTypesMustMerge = true)
// TODO: allow the user to override the severities for schema validation
val issueGroup = result.issues.group(defaultIssueSeverities)

issueGroup.errors.checkEmpty()
issueGroup.warnings.forEach {
Expand Down Expand Up @@ -232,6 +229,7 @@ object ApolloCompiler {
val name = it.name ?: ""
operationNameToNormalizedPath[name] = normalizedFile.normalizedPath
}

is GQLFragmentDefinition -> fragmentNameToNormalizedPath[it.name] = normalizedFile.normalizedPath
else -> Unit
}
Expand All @@ -251,9 +249,9 @@ object ApolloCompiler {
}

var document = ApolloExecutableDocumentTransform(options.addTypename ?: defaultAddTypename, !hasCacheCompilerPlugin).transform(
schema = schema,
document = GQLDocument(userDefinitions, sourceLocation = null),
upstreamFragmentDefinitions
schema = schema,
document = GQLDocument(userDefinitions, sourceLocation = null),
upstreamFragmentDefinitions
)

if (documentTransform != null) {
Expand Down Expand Up @@ -282,9 +280,16 @@ object ApolloCompiler {

val flattenModels = options.flattenModels ?: flattenModels(codegenModels)
val decapitalizeFields = options.decapitalizeFields ?: defaultDecapitalizeFields
val warnOnDeprecatedUsages = options.warnOnDeprecatedUsages ?: defaultWarnOnDeprecatedUsages
val issueSeverities = defaultIssueSeverities.let {
if (options.issueSeverities != null) {
it.toMutableMap().apply {
putAll(options.issueSeverities)
}
} else {
it
}
}
val failOnWarnings = options.failOnWarnings ?: defaultFailOnWarnings
val fieldsOnDisjointTypesMustMerge = options.fieldsOnDisjointTypesMustMerge ?: defaultFieldsOnDisjointTypesMustMerge
val generateOptionalOperationVariables = options.generateOptionalOperationVariables ?: defaultGenerateOptionalOperationVariables
val alwaysGenerateTypesMatching = options.alwaysGenerateTypesMatching ?: defaultAlwaysGenerateTypesMatching

Expand All @@ -293,10 +298,7 @@ object ApolloCompiler {
allIssues.addAll(checkCapitalizedFields(userDefinitions, checkFragmentsOnly = flattenModels))
}

val issueGroup = allIssues.group(
warnOnDeprecatedUsages,
fieldsOnDisjointTypesMustMerge,
)
val issueGroup = allIssues.group(issueSeverities)

issueGroup.errors.checkEmpty()

Expand All @@ -315,7 +317,7 @@ object ApolloCompiler {
val operations = mutableListOf<GQLOperationDefinition>()
val fragments = mutableListOf<GQLFragmentDefinition>()
document.definitions.forEach {
when(it) {
when (it) {
is GQLOperationDefinition -> operations.add(it)
is GQLFragmentDefinition -> fragments.add(it)
else -> Unit
Expand Down Expand Up @@ -602,41 +604,27 @@ object ApolloCompiler {
}
}

private enum class Severity {
None,
Warning,
Error
}

internal class IssueGroup(
val ignored: List<Issue>,
val warnings: List<Issue>,
val errors: List<Issue>,
)

internal fun List<Issue>.group(
warnOnDeprecatedUsages: Boolean,
fieldsOnDisjointTypesMustMerge: Boolean,
issueSeverities: Map<String, IssueSeverity>,
): IssueGroup {
val ignored = mutableListOf<Issue>()
val warnings = mutableListOf<Issue>()
val errors = mutableListOf<Issue>()

forEach {
val severity = when (it) {
is DeprecatedUsage -> if (warnOnDeprecatedUsages) Severity.Warning else Severity.None
is DifferentShape -> if (fieldsOnDisjointTypesMustMerge) Severity.Error else Severity.Warning
is UnusedVariable -> Severity.Warning
is UnusedFragment -> Severity.Warning
is IncompatibleDefinition -> Severity.Warning // This should probably be an error
is DirectiveRedefinition -> Severity.Warning
else -> Severity.Error
}
val name = it.javaClass.simpleName
val severity = issueSeverities.get(name) ?: IssueSeverity.Error

when (severity) {
Severity.None -> ignored.add(it)
Severity.Warning -> warnings.add(it)
Severity.Error -> errors.add(it)
IssueSeverity.Ignore -> ignored.add(it)
IssueSeverity.Warn -> warnings.add(it)
IssueSeverity.Error -> errors.add(it)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ package com.apollographql.apollo.compiler

import com.apollographql.apollo.annotations.ApolloDeprecatedSince
import com.apollographql.apollo.annotations.ApolloExperimental
import com.apollographql.apollo.ast.DeprecatedUsage
import com.apollographql.apollo.ast.DirectiveRedefinition
import com.apollographql.apollo.ast.IncompatibleDefinition
import com.apollographql.apollo.ast.UnusedFragment
import com.apollographql.apollo.ast.UnusedVariable
import com.apollographql.apollo.compiler.internal.sha256
import com.apollographql.apollo.compiler.operationoutput.OperationId
import com.apollographql.apollo.compiler.operationoutput.OperationOutput
Expand Down Expand Up @@ -156,20 +161,14 @@ class CodegenSchemaOptions(
constructor(): this(emptyMap(), emptyMap(), false)
}

enum class IssueSeverity {
Ignore,
Warn,
Error
}

@Serializable
class IrOptions(
/**
* Whether fields with different shape are disallowed to be merged in disjoint types.
*
* Note: setting this to `false` relaxes the standard GraphQL [FieldsInSetCanMerge](https://spec.graphql.org/draft/#FieldsInSetCanMerge()) validation which may still be
* run on the backend.
*
* See also [issue 4320](https://github.com/apollographql/apollo-kotlin/issues/4320)
*
* Default: true.
*/
val fieldsOnDisjointTypesMustMerge: Boolean?,

/**
* Whether to decapitalize field names in the generated models (for instance `FooBar` -> `fooBar`).
*
Expand All @@ -179,7 +178,7 @@ class IrOptions(

val flattenModels: Boolean?,

val warnOnDeprecatedUsages: Boolean?,
val issueSeverities: Map<String, IssueSeverity>?,
val failOnWarnings: Boolean?,
val addTypename: String?,

Expand All @@ -203,25 +202,23 @@ class IrOptions(
)

fun buildIrOptions(
fieldsOnDisjointTypesMustMerge: Boolean? = null,
decapitalizeFields: Boolean? = null,
flattenModels: Boolean? = null,
warnOnDeprecatedUsages: Boolean? = null,
failOnWarnings: Boolean? = null,
addTypename: String? = null,
generateOptionalOperationVariables: Boolean? = null,
alwaysGenerateTypesMatching: Set<String>? = null,
codegenModels: String? = null,
issueSeverity: Map<String, IssueSeverity>? = null,
): IrOptions = IrOptions(
fieldsOnDisjointTypesMustMerge = fieldsOnDisjointTypesMustMerge,
decapitalizeFields = decapitalizeFields,
flattenModels = flattenModels,
warnOnDeprecatedUsages = warnOnDeprecatedUsages,
failOnWarnings = failOnWarnings,
addTypename = addTypename,
generateOptionalOperationVariables = generateOptionalOperationVariables,
alwaysGenerateTypesMatching = alwaysGenerateTypesMatching,
codegenModels = codegenModels,
issueSeverities = issueSeverity
)

interface CommonCodegenOpt {
Expand Down Expand Up @@ -685,7 +682,13 @@ internal val defaultOperationIdsGenerator = OperationIdsGenerator { operationDes

internal val defaultLogger = NoOpLogger
internal const val defaultUseSemanticNaming = true
internal const val defaultWarnOnDeprecatedUsages = true
internal val defaultIssueSeverities = mapOf(
DeprecatedUsage::class.simpleName!! to IssueSeverity.Warn,
UnusedVariable::class.simpleName!! to IssueSeverity.Warn,
UnusedFragment::class.simpleName!! to IssueSeverity.Warn,
DirectiveRedefinition::class.simpleName!! to IssueSeverity.Warn,
IncompatibleDefinition::class.simpleName!! to IssueSeverity.Warn, // This should probably be an error by default
)
internal const val defaultFailOnWarnings = false
internal const val defaultGenerateAsInternal = false
internal const val defaultGenerateFilterNotNull = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ internal object TestUtils {
}

/**
* This allows to run a specific test from the command line by using something like:
* This allows running a specific test from the command line by using something like:
*
* ./gradlew :apollo-compiler:test -testFilter="fragments_with_type_condition" --tests '*Codegen*'
*/
Expand All @@ -71,10 +71,6 @@ internal object TestUtils {
.map { arrayOf(it.nameWithoutExtension, it) }
}

private fun File.replaceExtension(newExtension: String): File {
return File(parentFile, "$nameWithoutExtension.$newExtension")
}

fun findSchema(dir: File): Schema? {
return listOf("graphqls", "sdl", "json").map { File(dir, "schema.$it") }
.firstOrNull { it.exists() }
Expand All @@ -86,7 +82,7 @@ internal object TestUtils {
/**
* run the block and checks the result against the .expected file
*
* @param block: the callback to produce the result. [checkExpected] will try to find a schema
* @param block the callback to produce the result. [checkExpected] will try to find a schema
* for [graphQLFile] by either looking for a schema with the same name or testing the first
* schema.[json|sdl|graphqls] in the hierarchy
*/
Expand All @@ -112,7 +108,7 @@ internal object TestUtils {
val expectedFile = File(graphQLFile.parent, graphQLFile.nameWithoutExtension + ".expected")
val expected = try {
expectedFile.readText()
} catch (e: Exception) {
} catch (_: Exception) {
null
}

Expand All @@ -131,7 +127,7 @@ internal object TestUtils {
internal fun String.buffer() = Buffer().writeUtf8(this)

internal fun <V : Any> GQLResult<V>.apolloGetOrThrow(): V {
val groups = issues.group(false, true)
val groups = issues.group(defaultIssueSeverities)

groups.errors.firstOrNull()?.let {
throw SourceAwareException(it.message, it.sourceLocation)
Expand Down
Loading
Loading