Skip to content

Commit

Permalink
fix: apply schema transforms from inContextSDK (#8176)
Browse files Browse the repository at this point in the history
* test(e2e): add e2e tests

* fix: apply schema transforms from inContextSDK

* chore: changeset
  • Loading branch information
iamchanii authored Dec 24, 2024
1 parent 88acc96 commit b750a41
Show file tree
Hide file tree
Showing 9 changed files with 461 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/wet-timers-decide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphql-mesh/utils': patch
---

Fix to invoke `applySchemaTransforms` when creating `inContextSDK`
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should compose the appropriate schema 1`] = `
"schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) @link(url: "https://the-guild.dev/graphql/mesh/spec/v1.0", import: ["@merge", "@source"]) {
query: Query
}
directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
directive @join__graph(name: String!, url: String!) on ENUM_VALUE
directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE
directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR
directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
directive @merge(subgraph: String, argsExpr: String, keyArg: String, keyField: String, key: [String!], additionalArgs: String) repeatable on FIELD_DEFINITION
directive @source(name: String!, type: String, subgraph: String!) repeatable on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
directive @additionalField on FIELD_DEFINITION
scalar join__FieldSet
scalar link__Import
enum link__Purpose {
"""
\`SECURITY\` features provide metadata necessary to securely resolve fields.
"""
SECURITY
"""
\`EXECUTION\` features provide metadata necessary for operation execution.
"""
EXECUTION
}
enum join__Graph {
AUTHORS @join__graph(name: "authors", url: "http://localhost:<authors_port>/graphql")
BOOKS @join__graph(name: "books", url: "http://localhost:<books_port>/graphql")
}
scalar TransportOptions @join__type(graph: AUTHORS) @join__type(graph: BOOKS)
type Query @join__type(graph: AUTHORS) @join__type(graph: BOOKS) {
author(id: ID!): Author @merge(subgraph: "authors", keyField: "id", keyArg: "id") @merge(subgraph: "books", keyField: "id", keyArg: "id") @source(name: "authorWithBooks", type: "AuthorWithBooks", subgraph: "books")
writers(ids: [ID]): [Author] @source(name: "authors", type: "[Author]", subgraph: "authors") @join__field(graph: AUTHORS)
book(id: ID!): Book @merge(subgraph: "books", keyField: "id", keyArg: "id") @join__field(graph: BOOKS)
books(ids: [ID]): [Book!]! @merge(subgraph: "books", keyField: "id", keyArg: "ids") @join__field(graph: BOOKS)
}
type Author @source(name: "AuthorWithBooks", subgraph: "books") @join__type(graph: AUTHORS, key: "id") @join__type(graph: BOOKS, key: "id") {
id: ID!
name: String! @join__field(graph: AUTHORS)
books: [Book!]! @join__field(graph: BOOKS)
}
type Book @join__type(graph: BOOKS, key: "id") {
id: ID!
title: String!
authorId: ID!
author: Author @resolveTo(sourceName: "authors", sourceTypeName: "Query", sourceFieldName: "writers", keyField: "authorId", keysArg: "ids") @additionalField
}
"
`;

exports[`should execute Author 1`] = `
{
"data": {
"author": {
"books": [
{
"author": {
"id": "1",
"name": "Jane Doe",
},
"id": "0",
"title": "Lorem Ipsum",
},
],
"id": "1",
"name": "Jane Doe",
},
},
}
`;

exports[`should execute Authors 1`] = `
{
"data": {
"writers": [
{
"books": [
{
"author": {
"id": "0",
"name": "John Doe",
},
"id": "1",
"title": "Dolor Sit Amet",
},
],
"id": "0",
"name": "John Doe",
},
{
"books": [
{
"author": {
"id": "1",
"name": "Jane Doe",
},
"id": "0",
"title": "Lorem Ipsum",
},
],
"id": "1",
"name": "Jane Doe",
},
],
},
}
`;

exports[`should execute Book 1`] = `
{
"data": {
"book": {
"author": {
"books": [
{
"id": "1",
"title": "Dolor Sit Amet",
},
],
"id": "0",
"name": "John Doe",
},
"id": "1",
"title": "Dolor Sit Amet",
},
},
}
`;

exports[`should execute Books 1`] = `
{
"data": {
"books": [
{
"author": {
"books": [
{
"id": "0",
"title": "Lorem Ipsum",
},
],
"id": "1",
"name": "Jane Doe",
},
"id": "0",
"title": "Lorem Ipsum",
},
{
"author": {
"books": [
{
"id": "1",
"title": "Dolor Sit Amet",
},
],
"id": "0",
"name": "John Doe",
},
"id": "1",
"title": "Dolor Sit Amet",
},
],
},
}
`;
49 changes: 49 additions & 0 deletions e2e/type-merging-with-transforms/mesh.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Opts } from '@e2e/opts';
import {
createRenameFieldTransform,
createRenameTypeTransform,
defineConfig,
loadGraphQLHTTPSubgraph,
} from '@graphql-mesh/compose-cli';

const opts = Opts(process.argv);

export const composeConfig = defineConfig({
subgraphs: [
{
sourceHandler: loadGraphQLHTTPSubgraph('authors', {
endpoint: `http://localhost:${opts.getServicePort('authors')}/graphql`,
}),
transforms: [
createRenameFieldTransform(({ fieldName, typeName }) =>
typeName === 'Query' && fieldName === 'authors' ? 'writers' : fieldName,
),
],
},
{
sourceHandler: loadGraphQLHTTPSubgraph('books', {
endpoint: `http://localhost:${opts.getServicePort('books')}/graphql`,
}),
transforms: [
createRenameFieldTransform(({ fieldName, typeName }) =>
typeName === 'Query' && fieldName === 'authorWithBooks' ? 'author' : fieldName,
),
createRenameTypeTransform(({ typeName }) =>
typeName === 'AuthorWithBooks' ? 'Author' : typeName,
),
],
},
],
additionalTypeDefs: /* GraphQL */ `
extend type Book {
author: Author
@resolveTo(
sourceName: "authors"
sourceTypeName: "Query"
sourceFieldName: "writers"
keyField: "authorId"
keysArg: "ids"
)
}
`,
});
9 changes: 9 additions & 0 deletions e2e/type-merging-with-transforms/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "@e2e/type-merging-with-transforms",
"private": true,
"dependencies": {
"@graphql-hive/gateway": "^1.5.8",
"graphql": "^16.9.0",
"graphql-yoga": "^5.7.0"
}
}
43 changes: 43 additions & 0 deletions e2e/type-merging-with-transforms/services/authors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { createServer } from 'http';
import { createSchema, createYoga } from 'graphql-yoga';
import { Opts } from '@e2e/opts';

const authors = [
{
id: '0',
name: 'John Doe',
},
{
id: '1',
name: 'Jane Doe',
},
];

const yoga = createYoga({
schema: createSchema({
typeDefs: /* GraphQL */ `
type Query {
author(id: ID!): Author
authors(ids: [ID]): [Author]
}
type Author {
id: ID!
name: String!
}
`,
resolvers: {
Query: {
author: (_, { id }) => authors.find(author => author.id === id),
authors: (_, { ids }) =>
ids ? ids.map((id: string) => authors.find(author => author.id === id)) : authors,
},
},
}),
});

const port = Opts(process.argv).getServicePort('authors', true);

createServer(yoga).listen(port, () => {
console.log(`Authors service listening on http://localhost:${port}`);
});
56 changes: 56 additions & 0 deletions e2e/type-merging-with-transforms/services/books.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { createServer } from 'http';
import { createSchema, createYoga } from 'graphql-yoga';
import { Opts } from '@e2e/opts';

const books = [
{
id: '0',
title: 'Lorem Ipsum',
authorId: '1',
},
{
id: '1',
title: 'Dolor Sit Amet',
authorId: '0',
},
];

const yoga = createYoga({
schema: createSchema({
typeDefs: /* GraphQL */ `
type Query {
authorWithBooks(id: ID!): AuthorWithBooks
book(id: ID!): Book
books(ids: [ID]): [Book!]!
}
type AuthorWithBooks {
id: ID!
books: [Book!]!
}
type Book {
id: ID!
title: String!
authorId: ID!
}
`,
resolvers: {
Query: {
authorWithBooks: (_, { id }) => ({ id }),
book: (_, { id }) => books.find(book => book.id === id),
books: (_, { ids }) =>
ids ? ids.map((id: string) => books.find(book => book.id === id)) : books,
},
AuthorWithBooks: {
books: ({ id }) => books.filter(book => book.authorId === id),
},
},
}),
});

const port = Opts(process.argv).getServicePort('books', true);

createServer(yoga).listen(port, () => {
console.log(`Books service listening on http://localhost:${port}`);
});
Loading

0 comments on commit b750a41

Please sign in to comment.