Skip to content

[federation] use federation-jvm ServiceSDLPrinter #1546

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

Merged
merged 1 commit into from
Sep 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
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@

package com.expediagroup.graphql.generator.federation

import com.apollographql.federation.graphqljava.printer.ServiceSDLPrinter.generateServiceSDL
import com.apollographql.federation.graphqljava.printer.ServiceSDLPrinter.generateServiceSDLV2
import com.expediagroup.graphql.generator.annotations.GraphQLName
import com.expediagroup.graphql.generator.directives.DEPRECATED_DIRECTIVE_NAME
import com.expediagroup.graphql.generator.directives.DirectiveMetaInformation
import com.expediagroup.graphql.generator.extensions.print
import com.expediagroup.graphql.generator.federation.directives.EXTENDS_DIRECTIVE_TYPE
import com.expediagroup.graphql.generator.federation.directives.EXTERNAL_DIRECTIVE_TYPE
import com.expediagroup.graphql.generator.federation.directives.FEDERATION_SPEC_URL
Expand Down Expand Up @@ -61,18 +61,13 @@ import graphql.schema.GraphQLDirective
import graphql.schema.GraphQLObjectType
import graphql.schema.GraphQLSchema
import graphql.schema.GraphQLType
import java.util.function.Predicate
import kotlin.reflect.KType
import kotlin.reflect.full.findAnnotation

/**
* Hooks for generating federated GraphQL schema.
*/
open class FederatedSchemaGeneratorHooks(private val resolvers: List<FederatedTypeResolver<*>>, private val optInFederationV2: Boolean = false) : SchemaGeneratorHooks {
private val scalarDefinitionRegex = "(^\".+\"$[\\r\\n])?^scalar (_FieldSet|_Any)$[\\r\\n]*".toRegex(setOf(RegexOption.MULTILINE, RegexOption.IGNORE_CASE))
private val emptyQueryRegex = "^type Query @extends \\s*\\{\\s*}\\s*".toRegex(setOf(RegexOption.MULTILINE, RegexOption.IGNORE_CASE))
private val serviceFieldRegex = "\\s*_service: _Service!".toRegex(setOf(RegexOption.MULTILINE, RegexOption.IGNORE_CASE))
private val serviceTypeRegex = "^type _Service\\s*\\{\\s*sdl: String!\\s*}\\s*".toRegex(setOf(RegexOption.MULTILINE, RegexOption.IGNORE_CASE))
private val validator = FederatedSchemaValidator()

private val federationV2OnlyDirectiveNames: Set<String> = setOf(
Expand Down Expand Up @@ -206,17 +201,11 @@ open class FederatedSchemaGeneratorHooks(private val resolvers: List<FederatedTy
* https://www.apollographql.com/docs/apollo-server/federation/federation-spec/#query_service
*/
private fun getFederatedServiceSdl(schema: GraphQLSchema): String {
val directivesToInclude: List<String> = federatedDirectiveList().map { it.name }.plus(DEPRECATED_DIRECTIVE_NAME)
val customDirectivePredicate: Predicate<String> = Predicate { directivesToInclude.contains(it) }
return schema.print(
includeDefaultSchemaDefinition = optInFederationV2,
includeDirectiveDefinitions = false,
includeDirectivesFilter = customDirectivePredicate
).replace(scalarDefinitionRegex, "")
.replace(serviceFieldRegex, "")
.replace(serviceTypeRegex, "")
.replace(emptyQueryRegex, "")
.trim()
return if (optInFederationV2) {
generateServiceSDLV2(schema)
} else {
generateServiceSDL(schema, false)
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ import kotlin.test.assertNotNull
// SDL is returned without _entity and _service queries
const val FEDERATED_SERVICE_SDL =
"""
schema {
query: Query
}

interface Product @extends @key(fields : "id") @key(fields : "upc") {
id: String! @external
reviews: [Review!]!
Expand All @@ -54,6 +58,8 @@ type Book implements Product @extends @key(fields : "id") @key(fields : "upc") {
weight: Float! @external
}

type Query @extends

type Review {
body: String!
content: String @deprecated(reason : "no longer supported, replace with use Review.body instead")
Expand All @@ -75,6 +81,10 @@ scalar CustomScalar"""

const val BASE_SERVICE_SDL =
"""
schema {
query: Query
}

type Query @extends {
getSimpleNestedObject: [SelfReferenceObject]!
hello(name: String!): String!
Expand All @@ -93,6 +103,23 @@ schema @link(url : "https://specs.apollo.dev/link/v1.0/") @link(import : ["exten
query: Query
}

directive @custom on SCHEMA | SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION

"Marks target object as extending part of the federated schema"
directive @extends on OBJECT | INTERFACE

"Marks target field as external meaning it will be resolved by federated schema"
directive @external on FIELD_DEFINITION

"Space separated list of primary keys needed to access federated object"
directive @key(fields: _FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE

"Specifies the base type field set that will be selectable by the gateway"
directive @provides(fields: _FieldSet!) on FIELD_DEFINITION

"Specifies required input field set from the base type for a resolver"
directive @requires(fields: _FieldSet!) on FIELD_DEFINITION

interface Product @extends @key(fields : "id", resolvable : true) @key(fields : "upc", resolvable : true) {
id: String! @external
reviews: [Review!]!
Expand All @@ -112,8 +139,12 @@ type CustomScalar {
value: String!
}

type Query @extends {
_service: _Service!
}

type Review {
body: String!
body: String! @custom
content: String @deprecated(reason : "no longer supported, replace with use Review.body instead")
customScalar: CustomScalar!
id: String!
Expand All @@ -123,6 +154,13 @@ type User @extends @key(fields : "userId", resolvable : true) {
name: String! @external
userId: Int! @external
}

type _Service {
sdl: String!
}

"Federation type representing set of fields"
scalar _FieldSet
"""

class ServiceQueryResolverTest {
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ kotlinxSerializationVersion = 1.3.2

androidPluginVersion = 7.1.2
classGraphVersion = 4.8.149
federationGraphQLVersion = 2.0.4
graphQLJavaVersion = 19.1
federationGraphQLVersion = 2.0.7
graphQLJavaVersion = 19.2
graphQLJavaDataLoaderVersion = 3.2.0
jacksonVersion = 2.13.3
# KotlinPoet v1.12.0+ requires Kotlin v1.7
Expand Down