Skip to content

Commit

Permalink
feat(ApolloFederation): Add "_service" resolver (#7260)
Browse files Browse the repository at this point in the history
Fixes GRAPHQL-934.
This PR adds `_service` resolver according to the federation specs which return generated schema string without @apollo directives to be used by the gateway.
The query is:
```
query {
   _service {
      sdl
   }
}
```
which returns the schema string.
  • Loading branch information
minhaj-shakeel authored Jan 11, 2021
1 parent 95803ba commit b674991
Show file tree
Hide file tree
Showing 5 changed files with 456 additions and 0 deletions.
17 changes: 17 additions & 0 deletions graphql/admin/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,23 @@ func (as *adminServer) resetSchema(gqlSchema schema.Schema) {
} else {
resolverFactory = resolverFactoryWithErrorMsg(errResolverNotFound).
WithConventionResolvers(gqlSchema, as.fns)
// If the schema is a Federated Schema then attach "_service" resolver
if gqlSchema.IsFederated() {
resolverFactory.WithQueryResolver("_service", func(s schema.Query) resolve.QueryResolver {
return resolve.QueryResolverFunc(func(ctx context.Context, query schema.Query) *resolve.Resolved {
as.mux.RLock()
defer as.mux.RUnlock()
sch := as.schema.Schema
handler, _ := schema.NewHandler(sch, false)
data := handler.GQLSchemaWithoutApolloExtras()
return &resolve.Resolved{
Data: map[string]interface{}{"_service": map[string]interface{}{"sdl": data}},
Field: query,
}
})
})
}

if as.withIntrospection {
resolverFactory.WithSchemaIntrospection()
}
Expand Down
368 changes: 368 additions & 0 deletions graphql/e2e/schema/apolloServiceResponse.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,368 @@
#######################
# Input Schema
#######################

type Todo @key(fields: "id") {
id: ID!
title: String!
topic: String
}

#######################
# Extended Definitions
#######################

"""
The Int64 scalar type represents a signed 64‐bit numeric non‐fractional value.
Int64 can represent values in range [-(2^63),(2^63 - 1)].
"""
scalar Int64

"""
The DateTime scalar type represents date and time as a string in RFC3339 format.
For example: "1985-04-12T23:20:50.52Z" represents 20 minutes and 50.52 seconds after the 23rd hour of April 12th, 1985 in UTC.
"""
scalar DateTime

input IntRange{
min: Int!
max: Int!
}

input FloatRange{
min: Float!
max: Float!
}

input Int64Range{
min: Int64!
max: Int64!
}

input DateTimeRange{
min: DateTime!
max: DateTime!
}

input StringRange{
min: String!
max: String!
}

enum DgraphIndex {
int
int64
float
bool
hash
exact
term
fulltext
trigram
regexp
year
month
day
hour
geo
}

input AuthRule {
and: [AuthRule]
or: [AuthRule]
not: AuthRule
rule: String
}

enum HTTPMethod {
GET
POST
PUT
PATCH
DELETE
}

enum Mode {
BATCH
SINGLE
}

input CustomHTTP {
url: String!
method: HTTPMethod!
body: String
graphql: String
mode: Mode
forwardHeaders: [String!]
secretHeaders: [String!]
introspectionHeaders: [String!]
skipIntrospection: Boolean
}

type Point {
longitude: Float!
latitude: Float!
}

input PointRef {
longitude: Float!
latitude: Float!
}

input NearFilter {
distance: Float!
coordinate: PointRef!
}

input PointGeoFilter {
near: NearFilter
within: WithinFilter
}

type PointList {
points: [Point!]!
}

input PointListRef {
points: [PointRef!]!
}

type Polygon {
coordinates: [PointList!]!
}

input PolygonRef {
coordinates: [PointListRef!]!
}

type MultiPolygon {
polygons: [Polygon!]!
}

input MultiPolygonRef {
polygons: [PolygonRef!]!
}

input WithinFilter {
polygon: PolygonRef!
}

input ContainsFilter {
point: PointRef
polygon: PolygonRef
}

input IntersectsFilter {
polygon: PolygonRef
multiPolygon: MultiPolygonRef
}

input PolygonGeoFilter {
near: NearFilter
within: WithinFilter
contains: ContainsFilter
intersects: IntersectsFilter
}

input GenerateQueryParams {
get: Boolean
query: Boolean
password: Boolean
aggregate: Boolean
}

input GenerateMutationParams {
add: Boolean
update: Boolean
delete: Boolean
}

directive @hasInverse(field: String!) on FIELD_DEFINITION
directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION
directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION
directive @id on FIELD_DEFINITION
directive @withSubscription on OBJECT | INTERFACE
directive @secret(field: String!, pred: String) on OBJECT | INTERFACE
directive @auth(
password: AuthRule
query: AuthRule,
add: AuthRule,
update: AuthRule,
delete: AuthRule) on OBJECT | INTERFACE
directive @custom(http: CustomHTTP, dql: String) on FIELD_DEFINITION
directive @remote on OBJECT | INTERFACE | UNION | INPUT_OBJECT | ENUM
directive @cascade(fields: [String]) on FIELD
directive @lambda on FIELD_DEFINITION
directive @cacheControl(maxAge: Int!) on QUERY
directive @generate(
query: GenerateQueryParams,
mutation: GenerateMutationParams,
subscription: Boolean) on OBJECT | INTERFACE

input IntFilter {
eq: Int
le: Int
lt: Int
ge: Int
gt: Int
between: IntRange
}

input Int64Filter {
eq: Int64
le: Int64
lt: Int64
ge: Int64
gt: Int64
between: Int64Range
}

input FloatFilter {
eq: Float
le: Float
lt: Float
ge: Float
gt: Float
between: FloatRange
}

input DateTimeFilter {
eq: DateTime
le: DateTime
lt: DateTime
ge: DateTime
gt: DateTime
between: DateTimeRange
}

input StringTermFilter {
allofterms: String
anyofterms: String
}

input StringRegExpFilter {
regexp: String
}

input StringFullTextFilter {
alloftext: String
anyoftext: String
}

input StringExactFilter {
eq: String
in: [String]
le: String
lt: String
ge: String
gt: String
between: StringRange
}

input StringHashFilter {
eq: String
in: [String]
}

#######################
# Generated Types
#######################

type AddTodoPayload {
todo(filter: TodoFilter, order: TodoOrder, first: Int, offset: Int): [Todo]
numUids: Int
}

type DeleteTodoPayload {
todo(filter: TodoFilter, order: TodoOrder, first: Int, offset: Int): [Todo]
msg: String
numUids: Int
}

type TodoAggregateResult {
count: Int
titleMin: String
titleMax: String
topicMin: String
topicMax: String
}

type UpdateTodoPayload {
todo(filter: TodoFilter, order: TodoOrder, first: Int, offset: Int): [Todo]
numUids: Int
}

#######################
# Generated Enums
#######################

enum TodoHasFilter {
title
topic
}

enum TodoOrderable {
title
topic
}

#######################
# Generated Inputs
#######################

input AddTodoInput {
title: String!
topic: String
}

input TodoFilter {
id: [ID!]
has: TodoHasFilter
and: [TodoFilter]
or: [TodoFilter]
not: TodoFilter
}

input TodoOrder {
asc: TodoOrderable
desc: TodoOrderable
then: TodoOrder
}

input TodoPatch {
title: String
topic: String
}

input TodoRef {
id: ID
title: String
topic: String
}

input UpdateTodoInput {
filter: TodoFilter!
set: TodoPatch
remove: TodoPatch
}

#######################
# Generated Query
#######################

type Query {
getTodo(id: ID!): Todo
queryTodo(filter: TodoFilter, order: TodoOrder, first: Int, offset: Int): [Todo]
aggregateTodo(filter: TodoFilter): TodoAggregateResult
}

#######################
# Generated Mutations
#######################

type Mutation {
addTodo(input: [AddTodoInput!]!): AddTodoPayload
updateTodo(input: UpdateTodoInput!): UpdateTodoPayload
deleteTodo(filter: TodoFilter!): DeleteTodoPayload
}

Loading

0 comments on commit b674991

Please sign in to comment.