Skip to content

Commit

Permalink
Introduce new composition printer / implementation
Browse files Browse the repository at this point in the history
This forks from the newly updated schema printer. However, the goal
of this printer is to print the FINAL composition, rather than the
schema for a single federated service.

By peeking at the existing annotations in the `extensions` on the
composed `GraphQLSchema`, the composition printer can gather
important metadata like type and field ownership which is then
annotated in the final SDL.
  • Loading branch information
trevor-scheer committed Jul 24, 2020
1 parent c415707 commit 0e1c6d1
Show file tree
Hide file tree
Showing 2 changed files with 761 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
import { lexicographicSortSchema } from 'graphql';
import { fixtures } from 'apollo-federation-integration-testsuite';
import { composeAndValidate } from '../../composition';
import { printComposedSdl } from '../printComposedSdl';

describe('printComposedSdl', () => {
const { schema, errors } = composeAndValidate(fixtures);
const sorted = lexicographicSortSchema(schema);

it('composes without errors', () => {
expect(errors).toHaveLength(0);
});

it('prints a fully composed schema correctly', () => {
expect(printComposedSdl(sorted, fixtures)).toMatchInlineSnapshot(`
"schema
@graph(name: \\"accounts\\", url: \\"undefined\\")
@graph(name: \\"books\\", url: \\"undefined\\")
@graph(name: \\"documents\\", url: \\"undefined\\")
@graph(name: \\"inventory\\", url: \\"undefined\\")
@graph(name: \\"product\\", url: \\"undefined\\")
@graph(name: \\"reviews\\", url: \\"undefined\\")
@composedGraph(version: 1)
{
query: Query
mutation: Mutation
}
directive @stream on FIELD
directive @transform(from: String!) on FIELD
type Query {
body: Body! @resolve(graph: \\"documents\\")
book(isbn: String!): Book @resolve(graph: \\"books\\")
books: [Book] @resolve(graph: \\"books\\")
library(id: ID!): Library @resolve(graph: \\"books\\")
me: User @resolve(graph: \\"accounts\\")
product(upc: String!): Product @resolve(graph: \\"product\\")
topCars(first: Int = 5): [Car] @resolve(graph: \\"product\\")
topProducts(first: Int = 5): [Product] @resolve(graph: \\"product\\")
topReviews(first: Int = 5): [Review] @resolve(graph: \\"reviews\\")
user(id: ID!): User @resolve(graph: \\"accounts\\")
vehicle(id: String!): Vehicle @resolve(graph: \\"product\\")
}
union Body = Image | Text
type Image {
attributes: ImageAttributes!
name: String!
}
type ImageAttributes {
url: String!
}
type Text {
attributes: TextAttributes!
name: String!
}
type TextAttributes {
bold: Boolean
text: String
}
type Book implements Product
@owner(graph: \\"books\\")
@key(fields: \\"isbn\\", graph: \\"books\\")
@key(fields: \\"isbn\\", graph: \\"inventory\\")
@key(fields: \\"isbn\\", graph: \\"product\\")
@key(fields: \\"isbn\\", graph: \\"reviews\\")
{
details: ProductDetailsBook @resolve(graph: \\"product\\")
inStock: Boolean @resolve(graph: \\"inventory\\")
isbn: String!
isCheckedOut: Boolean @resolve(graph: \\"inventory\\")
metadata: [MetadataOrError]
name(delimeter: String = \\" \\"): String @resolve(graph: \\"product\\") @requires(fields: \\"title year\\")
price: String @resolve(graph: \\"product\\")
relatedReviews: [Review!]! @resolve(graph: \\"reviews\\") @requires(fields: \\"similarBooks { isbn }\\")
reviews: [Review] @resolve(graph: \\"reviews\\")
similarBooks: [Book]!
sku: String! @resolve(graph: \\"product\\")
title: String
upc: String! @resolve(graph: \\"product\\")
year: Int
}
interface Product {
details: ProductDetails
inStock: Boolean
name: String
price: String
reviews: [Review]
sku: String!
upc: String!
}
interface ProductDetails {
country: String
}
type Review
@owner(graph: \\"reviews\\")
@key(fields: \\"id\\", graph: \\"reviews\\")
{
author: User @provides(fields: \\"username\\")
body(format: Boolean = false): String
id: ID!
metadata: [MetadataOrError]
product: Product
}
type User
@owner(graph: \\"accounts\\")
@key(fields: \\"id\\", graph: \\"accounts\\")
@key(fields: \\"id\\", graph: \\"inventory\\")
@key(fields: \\"id\\", graph: \\"product\\")
@key(fields: \\"id\\", graph: \\"reviews\\")
{
account: AccountType
birthDate(locale: String): String
goodAddress: Boolean @resolve(graph: \\"reviews\\") @requires(fields: \\"metadata { address }\\")
goodDescription: Boolean @resolve(graph: \\"inventory\\") @requires(fields: \\"metadata { description }\\")
id: ID!
metadata: [UserMetadata]
name: String
numberOfReviews: Int! @resolve(graph: \\"reviews\\")
reviews: [Review] @resolve(graph: \\"reviews\\")
thing: Thing @resolve(graph: \\"product\\")
username: String
vehicle: Vehicle @resolve(graph: \\"product\\")
}
union AccountType = PasswordAccount | SMSAccount
type PasswordAccount
@owner(graph: \\"accounts\\")
@key(fields: \\"email\\", graph: \\"accounts\\")
{
email: String!
}
type SMSAccount
@owner(graph: \\"accounts\\")
@key(fields: \\"number\\", graph: \\"accounts\\")
{
number: String
}
type UserMetadata {
address: String
description: String
name: String
}
union Thing = Car | Ikea
type Car implements Vehicle
@owner(graph: \\"product\\")
@key(fields: \\"id\\", graph: \\"product\\")
@key(fields: \\"id\\", graph: \\"reviews\\")
{
description: String
id: String!
price: String
retailPrice: String @resolve(graph: \\"reviews\\") @requires(fields: \\"price\\")
}
interface Vehicle {
description: String
id: String!
price: String
retailPrice: String
}
type Ikea {
asile: Int
}
union MetadataOrError = Error | KeyValue
type Error {
code: Int
message: String
}
type KeyValue {
key: String!
value: String!
}
type ProductDetailsBook implements ProductDetails {
country: String
pages: Int
}
type Library
@owner(graph: \\"books\\")
@key(fields: \\"id\\", graph: \\"books\\")
@key(fields: \\"id\\", graph: \\"accounts\\")
{
id: ID!
name: String
userAccount(id: ID! = 1): User @resolve(graph: \\"accounts\\") @requires(fields: \\"name\\")
}
type Mutation {
deleteReview(id: ID!): Boolean @resolve(graph: \\"reviews\\")
login(password: String!, username: String!): User @resolve(graph: \\"accounts\\")
reviewProduct(body: String!, upc: String!): Product @resolve(graph: \\"reviews\\")
updateReview(review: UpdateReviewInput!): Review @resolve(graph: \\"reviews\\")
}
input UpdateReviewInput {
body: String
id: ID!
}
type Amazon {
referrer: String
}
union Brand = Amazon | Ikea
type Furniture implements Product
@owner(graph: \\"product\\")
@key(fields: \\"upc\\", graph: \\"product\\")
@key(fields: \\"sku\\", graph: \\"product\\")
@key(fields: \\"sku\\", graph: \\"inventory\\")
@key(fields: \\"upc\\", graph: \\"reviews\\")
{
brand: Brand
details: ProductDetailsFurniture
inStock: Boolean @resolve(graph: \\"inventory\\")
isHeavy: Boolean @resolve(graph: \\"inventory\\")
metadata: [MetadataOrError]
name: String
price: String
reviews: [Review] @resolve(graph: \\"reviews\\")
sku: String!
upc: String!
}
type ProductDetailsFurniture implements ProductDetails {
color: String
country: String
}
type Van implements Vehicle
@owner(graph: \\"product\\")
@key(fields: \\"id\\", graph: \\"product\\")
@key(fields: \\"id\\", graph: \\"reviews\\")
{
description: String
id: String!
price: String
retailPrice: String @resolve(graph: \\"reviews\\") @requires(fields: \\"price\\")
}
"
`);
});
});
Loading

0 comments on commit 0e1c6d1

Please sign in to comment.