Skip to content

Commit

Permalink
Fix Merge Schemas behavior and remove legacy signature in addResolver…
Browse files Browse the repository at this point in the history
…sToSchema
  • Loading branch information
ardatan committed Aug 9, 2022
1 parent d8dc67a commit 1f5c69f
Show file tree
Hide file tree
Showing 13 changed files with 266 additions and 128 deletions.
68 changes: 68 additions & 0 deletions .changeset/fifty-experts-unite.md
@@ -0,0 +1,68 @@
---
'@graphql-tools/schema': major
---

Thanks @mattkrick and @borisno2!

`addResolversToSchema`;

If you are using the legacy parameters like below, you should update them to the new usage. Other than that, there is no functional change;

```ts
// From
addResolversToSchema(schema, resolvers, resolverValidationOptions)

// To
addResolversToSchema({
schema,
resolvers,
resolverValidationOptions
})
```

`mergeSchemas`;

The provided `resolver` overrides the resolvers in the `schema` with the same name;

The `hello` resolver in the `schema` would be overridden by the `hello` resolver in the `resolvers`. Before it was opposite which is not expected.

```ts
const schema = makeExecutableSchema({
typeDefs: `
type Query {
hello: String
}
`,
resolvers: {
Query: {
hello: () => 'Hello world!'
}
}
})

mergeSchemas({
schemas: [schema],
resolvers: {
Query: {
hello: () => 'New hello world'
}
}
})
```

`makeExecutableSchema` no longer takes `parseOptions` and you can pass those options directly;

```ts
makeExecutableSchema({
typeDefs: ``,
parseOptions: {
assumeValid: true
}
})

// After
makeExecutableSchema({
typeDefs: ``,
assumeValid: true
})
```
1 change: 1 addition & 0 deletions packages/load/package.json
Expand Up @@ -57,6 +57,7 @@
"dependencies": {
"@graphql-tools/utils": "8.9.0",
"@graphql-tools/schema": "8.5.1",
"@graphql-tools/merge": "8.3.1",
"p-limit": "3.1.0",
"tslib": "^2.4.0"
},
Expand Down
93 changes: 43 additions & 50 deletions packages/load/src/schema.ts
@@ -1,19 +1,13 @@
import { loadTypedefs, LoadTypedefsOptions, UnnormalizedTypeDefPointer, loadTypedefsSync } from './load-typedefs.js';
import {
GraphQLSchema,
BuildSchemaOptions,
DocumentNode,
Source as GraphQLSource,
print,
lexicographicSortSchema,
} from 'graphql';
import { OPERATION_KINDS } from './documents.js';
import { mergeSchemas, MergeSchemasConfig } from '@graphql-tools/schema';
import { Source } from '@graphql-tools/utils';
import { loadTypedefs, LoadTypedefsOptions, UnnormalizedTypeDefPointer, loadTypedefsSync } from './load-typedefs';
import { GraphQLSchema, BuildSchemaOptions, Source as GraphQLSource, print, lexicographicSortSchema } from 'graphql';
import { OPERATION_KINDS } from './documents';
import { IExecutableSchemaDefinition, mergeSchemas } from '@graphql-tools/schema';
import { getResolversFromSchema, IResolvers, Source, TypeSource } from '@graphql-tools/utils';
import { extractExtensionsFromSchema, SchemaExtensions } from '@graphql-tools/merge';

export type LoadSchemaOptions = BuildSchemaOptions &
LoadTypedefsOptions &
Partial<MergeSchemasConfig> & {
Partial<IExecutableSchemaDefinition> & {
/**
* Adds a list of Sources in to `extensions.sources`
*
Expand All @@ -35,22 +29,7 @@ export async function loadSchema(
...options,
filterKinds: OPERATION_KINDS,
});

const { schemas, typeDefs } = collectSchemasAndTypeDefs(sources);
schemas.push(...(options.schemas ?? []));
const mergeSchemasOptions: MergeSchemasConfig = {
...options,
schemas: schemas.concat(options.schemas ?? []),
typeDefs,
};

const schema = typeDefs?.length === 0 && schemas?.length === 1 ? schemas[0] : mergeSchemas(mergeSchemasOptions);

if (options?.includeSources) {
includeSources(schema, sources);
}

return options.sort ? lexicographicSortSchema(schema) : schema;
return getSchemaFromSources(sources, options);
}

/**
Expand All @@ -66,20 +45,7 @@ export function loadSchemaSync(
filterKinds: OPERATION_KINDS,
...options,
});

const { schemas, typeDefs } = collectSchemasAndTypeDefs(sources);

const schema = mergeSchemas({
schemas,
typeDefs,
...options,
});

if (options?.includeSources) {
includeSources(schema, sources);
}

return options.sort ? lexicographicSortSchema(schema) : schema;
return getSchemaFromSources(sources, options);
}

function includeSources(schema: GraphQLSchema, sources: Source[]) {
Expand All @@ -98,20 +64,47 @@ function includeSources(schema: GraphQLSchema, sources: Source[]) {
};
}

function collectSchemasAndTypeDefs(sources: Source[]) {
const schemas: GraphQLSchema[] = [];
const typeDefs: DocumentNode[] = [];
function getSchemaFromSources(sources: Source[], options: LoadSchemaOptions) {
if (sources.length === 1 && sources[0].schema != null && options.typeDefs == null && options.resolvers == null) {
return options.sort ? lexicographicSortSchema(sources[0].schema) : sources[0].schema;
}
const { typeDefs, resolvers, schemaExtensions } = collectSchemaParts(sources);

const schema = mergeSchemas({
...options,
typeDefs,
resolvers,
schemaExtensions,
});

if (options?.includeSources) {
includeSources(schema, sources);
}

return options.sort ? lexicographicSortSchema(schema) : schema;
}

function collectSchemaParts(sources: Source[]) {
const typeDefs: TypeSource[] = [];
const resolvers: IResolvers[] = [];
const schemaExtensions: SchemaExtensions[] = [];

for (const source of sources) {
if (source.schema) {
schemas.push(source.schema);
} else if (source.document) {
typeDefs.push(source.document);
typeDefs.push(source.schema);
resolvers.push(getResolversFromSchema(source.schema));
schemaExtensions.push(extractExtensionsFromSchema(source.schema));
} else {
const typeDef = source.document || source.rawSDL;
if (typeDef) {
typeDefs.push(typeDef);
}
}
}

return {
schemas,
typeDefs,
resolvers,
schemaExtensions,
};
}
7 changes: 6 additions & 1 deletion packages/mock/src/addMocksToSchema.ts
Expand Up @@ -245,5 +245,10 @@ export function addMocksToSchema<TResolvers = IResolvers>({
},
});

return resolvers ? addResolversToSchema(schemaWithMocks, resolvers as any) : schemaWithMocks;
return resolvers
? addResolversToSchema({
schema: schemaWithMocks,
resolvers: resolvers as any,
})
: schemaWithMocks;
}
50 changes: 40 additions & 10 deletions packages/mock/tests/mocking-compatibility.spec.ts
Expand Up @@ -181,7 +181,10 @@ describe('Mock retro-compatibility', () => {
returnString: () => 'someString',
},
};
jsSchema = addResolversToSchema(jsSchema, resolvers);
jsSchema = addResolversToSchema({
schema: jsSchema,
resolvers,
});
const testQuery = /* GraphQL */ `
{
returnInt
Expand Down Expand Up @@ -266,7 +269,10 @@ describe('Mock retro-compatibility', () => {
},
},
};
jsSchema = addResolversToSchema(jsSchema, resolvers);
jsSchema = addResolversToSchema({
schema: jsSchema,
resolvers,
});
jsSchema = addMocksToSchema({
schema: jsSchema,
mocks: {},
Expand Down Expand Up @@ -625,7 +631,10 @@ describe('Mock retro-compatibility', () => {
returnMockError: () => undefined,
},
};
jsSchema = addResolversToSchema(jsSchema, resolvers);
jsSchema = addResolversToSchema({
schema: jsSchema,
resolvers,
});

const mockMap = {};
jsSchema = addMocksToSchema({
Expand Down Expand Up @@ -656,7 +665,10 @@ describe('Mock retro-compatibility', () => {
returnMockError: () => '10-11-2012',
},
};
jsSchema = addResolversToSchema(jsSchema, resolvers);
jsSchema = addResolversToSchema({
schema: jsSchema,
resolvers,
});

const mockMap = {};
addMocksToSchema({
Expand Down Expand Up @@ -875,7 +887,10 @@ describe('Mock retro-compatibility', () => {
},
},
};
jsSchema = addResolversToSchema(jsSchema, resolvers);
jsSchema = addResolversToSchema({
schema: jsSchema,
resolvers,
});
const testQuery = /* GraphQL */ `
{
returnListOfListOfObject {
Expand Down Expand Up @@ -1064,7 +1079,10 @@ describe('Mock retro-compatibility', () => {
returnString: () => Promise.resolve('bar'), // see c)
},
};
jsSchema = addResolversToSchema(jsSchema, resolvers);
jsSchema = addResolversToSchema({
schema: jsSchema,
resolvers,
});
jsSchema = addMocksToSchema({
schema: jsSchema,
mocks: mockMap,
Expand Down Expand Up @@ -1129,7 +1147,10 @@ describe('Mock retro-compatibility', () => {
}),
},
};
jsSchema = addResolversToSchema(jsSchema, resolvers);
jsSchema = addResolversToSchema({
schema: jsSchema,
resolvers,
});
const mockMap = {
returnListOfInt: () => [5, 6, 7],
Bird: () => ({
Expand Down Expand Up @@ -1174,7 +1195,10 @@ describe('Mock retro-compatibility', () => {
}),
},
};
jsSchema = addResolversToSchema(jsSchema, resolvers);
jsSchema = addResolversToSchema({
schema: jsSchema,
resolvers,
});
const mockMap = {
Bird: () => ({
returnInt: 3, // see a)
Expand Down Expand Up @@ -1218,7 +1242,10 @@ describe('Mock retro-compatibility', () => {
returnObject: () => objProxy,
},
};
jsSchema = addResolversToSchema(jsSchema, resolvers);
jsSchema = addResolversToSchema({
schema: jsSchema,
resolvers,
});
const mockMap = {
Bird: () => ({
returnInt: 3, // see a)
Expand Down Expand Up @@ -1295,7 +1322,10 @@ describe('Mock retro-compatibility', () => {
returnString: () => null, // a) resolve of a string
},
};
jsSchema = addResolversToSchema(jsSchema, resolvers);
jsSchema = addResolversToSchema({
schema: jsSchema,
resolvers,
});
const mockMap = {
Int: () => 666, // b) mock of Int.
};
Expand Down
32 changes: 8 additions & 24 deletions packages/schema/src/addResolversToSchema.ts
@@ -1,7 +1,6 @@
import {
GraphQLEnumType,
GraphQLSchema,
isSchema,
GraphQLScalarType,
GraphQLUnionType,
GraphQLInterfaceType,
Expand All @@ -19,7 +18,6 @@ import {

import {
IResolvers,
IResolverValidationOptions,
IAddResolversToSchemaOptions,
mapSchema,
MapperKind,
Expand All @@ -33,28 +31,14 @@ import {
import { checkForResolveTypeResolver } from './checkForResolveTypeResolver.js';
import { extendResolversFromInterfaces } from './extendResolversFromInterfaces.js';

export function addResolversToSchema(
schemaOrOptions: GraphQLSchema | IAddResolversToSchemaOptions,
legacyInputResolvers?: IResolvers,
legacyInputValidationOptions?: IResolverValidationOptions
): GraphQLSchema {
const options: IAddResolversToSchemaOptions = isSchema(schemaOrOptions)
? {
schema: schemaOrOptions,
resolvers: legacyInputResolvers ?? {},
resolverValidationOptions: legacyInputValidationOptions,
}
: schemaOrOptions;

let {
schema,
resolvers: inputResolvers,
defaultFieldResolver,
resolverValidationOptions = {},
inheritResolversFromInterfaces = false,
updateResolversInPlace = false,
} = options;

export function addResolversToSchema({
schema,
resolvers: inputResolvers,
defaultFieldResolver,
resolverValidationOptions = {},
inheritResolversFromInterfaces = false,
updateResolversInPlace = false,
}: IAddResolversToSchemaOptions): GraphQLSchema {
const { requireResolversToMatchSchema = 'error', requireResolversForResolveType } = resolverValidationOptions;

const resolvers = inheritResolversFromInterfaces
Expand Down

0 comments on commit 1f5c69f

Please sign in to comment.