Skip to content

Commit

Permalink
fix(runtime): choose correct root type name for the source (#4571)
Browse files Browse the repository at this point in the history
* fix(runtime): choose correct root type name for the source

* ..

* Add more tests

* Go

* Update get-mesh.ts

* chore(dependencies): updated changesets for modified dependencies

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
ardatan and github-actions[bot] authored Sep 24, 2022
1 parent 6df0993 commit 1a8e808
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 4 deletions.
7 changes: 7 additions & 0 deletions .changeset/@graphql-mesh_cli-4571-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@graphql-mesh/cli": patch
---

dependencies updates:

- Updated dependency [`[email protected]` ↗︎](https://www.npmjs.com/package/ws/v/8.9.0) (from `8.8.1`, in `dependencies`)
7 changes: 7 additions & 0 deletions .changeset/@graphql-mesh_grpc-4571-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@graphql-mesh/grpc": patch
---

dependencies updates:

- Updated dependency [`[email protected]` ↗︎](https://www.npmjs.com/package/protobufjs/v/7.1.2) (from `7.1.1`, in `dependencies`)
7 changes: 7 additions & 0 deletions .changeset/@graphql-mesh_http-4571-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@graphql-mesh/http": patch
---

dependencies updates:

- Updated dependency [`[email protected]` ↗︎](https://www.npmjs.com/package/graphql-yoga/v/3.0.0-next.1) (from `3.0.0-next.0`, in `dependencies`)
5 changes: 5 additions & 0 deletions .changeset/ninety-pots-guess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphql-mesh/runtime': patch
---

Choose the root type name for a specific operation type from the source schema not from the gateway schema, because source schema might have a different like `QueryType` instead of `Query`.
12 changes: 8 additions & 4 deletions packages/runtime/src/get-mesh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,14 @@ export function wrapFetchWithPlugins(plugins: MeshPlugin<any>[]): MeshFetch {
}

// Use in-context-sdk for tracing
function createProxyingResolverFactory(apiName: string): CreateProxyingResolverFn {
return function createProxyingResolver() {
function createProxyingResolverFactory(apiName: string, apiSchema: GraphQLSchema): CreateProxyingResolverFn {
return function createProxyingResolver({ operation, fieldName, subschemaConfig }) {
const rootType = apiSchema.getRootType(operation);
return function proxyingResolver(root, args, context, info) {
return context[apiName][info.parentType.name][info.fieldName]({ root, args, context, info });
if (!context[apiName][rootType.name][info.fieldName]) {
throw new Error(`${info.fieldName} couldn't find in ${rootType.name} of ${apiName} as a ${operation}`);
}
return context[apiName][rootType.name][info.fieldName]({ root, args, context, info });
};
};
}
Expand Down Expand Up @@ -197,7 +201,7 @@ export async function getMesh(options: GetMeshOptions): Promise<MeshInstance> {
handler: apiSource.handler,
batch: 'batch' in source ? source.batch : true,
merge: source.merge,
createProxyingResolver: createProxyingResolverFactory(apiName),
createProxyingResolver: createProxyingResolverFactory(apiName, apiSchema),
});
} catch (e: any) {
sourceLogger.error(`Failed to generate the schema`, e);
Expand Down
211 changes: 211 additions & 0 deletions packages/runtime/test/getMesh.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/* eslint-disable import/no-extraneous-dependencies */
import LocalforageCache from '@graphql-mesh/cache-localforage';
import GraphQLHandler from '@graphql-mesh/graphql';
import StitchingMerger from '@graphql-mesh/merger-stitching';
import { InMemoryStoreStorageAdapter, MeshStore } from '@graphql-mesh/store';
import { defaultImportFn, DefaultLogger, PubSub } from '@graphql-mesh/utils';
import { makeExecutableSchema } from '@graphql-tools/schema';
import { printSchemaWithDirectives } from '@graphql-tools/utils';
import { parse } from 'graphql';
import { getMesh } from '../src/get-mesh';
import { MeshResolvedSource } from '../src/types';

describe('getMesh', () => {
const baseDir = __dirname;
let cache: LocalforageCache;
let pubsub: PubSub;
let store: MeshStore;
let logger: DefaultLogger;
let merger: StitchingMerger;
beforeEach(() => {
cache = new LocalforageCache();
pubsub = new PubSub();
store = new MeshStore('test', new InMemoryStoreStorageAdapter(), {
readonly: false,
validate: false,
});
logger = new DefaultLogger('Mesh Test');
merger = new StitchingMerger({
store,
cache,
pubsub,
logger,
});
});

interface CreateSchemaConfiguration {
suffix: string;
suffixRootTypeNames: boolean;
suffixFieldNames: boolean;
suffixResponses: boolean;
}

function createGraphQLSchema(config: CreateSchemaConfiguration) {
const queryTypeName = config.suffixRootTypeNames ? `Query${config.suffix}` : 'Query';
const mutationTypeName = config.suffixRootTypeNames ? `Mutation${config.suffix}` : 'Mutation';
const subscriptionTypeName = config.suffixRootTypeNames ? `Subscription${config.suffix}` : 'Subscription';

return makeExecutableSchema({
typeDefs: `
type ${queryTypeName} {
hello${config.suffixFieldNames ? config.suffix : ''}: String
}
type ${mutationTypeName} {
bye${config.suffixFieldNames ? config.suffix : ''}: String
}
type ${subscriptionTypeName} {
wave${config.suffixFieldNames ? config.suffix : ''}: String
}
schema {
query: ${queryTypeName}
mutation: ${mutationTypeName}
subscription: ${subscriptionTypeName}
}
`,
resolvers: {
[queryTypeName]: {
[`hello${config.suffixFieldNames ? config.suffix : ''}`]: () =>
`Hello from service${config.suffixResponses ? config.suffix : ''}`,
},
[mutationTypeName]: {
[`bye${config.suffixFieldNames ? config.suffix : ''}`]: () =>
`Bye from service${config.suffixResponses ? config.suffix : ''}schema`,
},
},
});
}

function createGraphQLSource(config: CreateSchemaConfiguration): MeshResolvedSource {
const name = `service${config.suffix}`;
return {
name,
handler: new GraphQLHandler({
baseDir,
cache,
pubsub,
name,
config: {
schema: `./schema${config.suffix}.ts`,
},
store,
logger,
async importFn(moduleId) {
if (moduleId.endsWith(`schema${config.suffix}.ts`)) {
return createGraphQLSchema(config);
}
return defaultImportFn(moduleId);
},
}),
transforms: [],
};
}

it('handle sources with different query type names', async () => {
const mesh = await getMesh({
cache,
pubsub,
logger,
sources: new Array(3).fill(0).map((_, i) =>
createGraphQLSource({
suffix: i.toString(),
suffixRootTypeNames: true,
suffixFieldNames: false,
suffixResponses: false,
})
),
merger,
});

expect(printSchemaWithDirectives(mesh.schema)).toMatchInlineSnapshot(`
"schema {
query: Query
mutation: Mutation
subscription: Subscription
}
type Query {
hello: String
}
type Mutation {
bye: String
}
type Subscription {
wave: String
}"
`);

const result = await mesh.execute(
`
{
hello0
hello1
hello2
}
`,
{}
);

expect(result).toMatchInlineSnapshot(`
{
"data": {},
}
`);
});

it('can stitch a mutation field to a query field', async () => {
const mesh = await getMesh({
cache,
pubsub,
logger,
merger,
sources: [
createGraphQLSource({
suffix: 'Foo',
suffixRootTypeNames: false,
suffixFieldNames: true,
suffixResponses: true,
}),
createGraphQLSource({
suffix: 'Bar',
suffixRootTypeNames: false,
suffixFieldNames: true,
suffixResponses: true,
}),
],
additionalTypeDefs: [
parse(/* GraphQL */ `
extend type Mutation {
strikeBack: String
}
`),
],
additionalResolvers: {
Mutation: {
strikeBack: (root, args, context, info) => context.serviceFoo.Query.helloFoo({ root, args, context, info }),
},
},
});

const result = await mesh.execute(
/* GraphQL */ `
mutation {
strikeBack
}
`,
{}
);

expect(result).toMatchInlineSnapshot(`
{
"data": {
"strikeBack": "Hello from serviceFoo",
},
}
`);
});
});

0 comments on commit 1a8e808

Please sign in to comment.