Skip to content

Commit

Permalink
proof of concept
Browse files Browse the repository at this point in the history
mapSchema can support copying graphql object annotations, but annotations will have to be copied for every graphql object whenever a new schema, type, enumValue, field, argument, is created.

this change just covers type annotations with symbols and property annotations for object types
  • Loading branch information
yaacovCR committed May 20, 2020
1 parent ab926bb commit ba6791c
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 20 deletions.
19 changes: 19 additions & 0 deletions packages/utils/src/annotate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { GraphQLNamedType, isObjectType } from 'graphql';

export function annotateType<T extends GraphQLNamedType>(newType: T, originalType: T): T {
const symbols = Object.getOwnPropertySymbols(originalType);
symbols.forEach(symbol => {
newType[symbol] = originalType[symbol];
});

if (isObjectType(newType)) {
const names = Object.getOwnPropertyNames(originalType);
const objectPropertyNames = ['name', 'description', 'isTypeOf', 'extensions', 'astNode', 'extensionASTNodes'];
names.forEach(name => {
if (!objectPropertyNames.includes(name) && !name.startsWith('_')) {
newType[name] = originalType[name];
}
});
}
return newType;
}
1 change: 1 addition & 0 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@ export * from './updateArgument';
export * from './implementsAbstractType';
export * from './errors';
export * from './toConfig';
export * from './annotate';
52 changes: 34 additions & 18 deletions packages/utils/src/mapSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {

import { rewireTypes } from './rewire';
import { serializeInputValue, parseInputValue } from './transformInputValue';
import { annotateType } from './annotate';

export function mapSchema(schema: GraphQLSchema, schemaMapper: SchemaMapper = {}): GraphQLSchema {
const originalTypeMap = schema.getTypeMap();
Expand Down Expand Up @@ -98,18 +99,27 @@ function mapTypes(
Object.keys(originalTypeMap).forEach(typeName => {
if (!typeName.startsWith('__')) {
const originalType = originalTypeMap[typeName];
if (originalType != null && testFn(originalType)) {
const typeMapper = getTypeMapper(schema, schemaMapper, typeName);

if (typeMapper != null) {
const maybeNewType = typeMapper(originalType, schema);
newTypeMap[typeName] = maybeNewType !== undefined ? maybeNewType : originalType;
} else {
newTypeMap[typeName] = originalType;
}
} else {

if (originalType == null || !testFn(originalType)) {
newTypeMap[typeName] = originalType;
return;
}

const typeMapper = getTypeMapper(schema, schemaMapper, typeName);

if (typeMapper == null) {
newTypeMap[typeName] = originalType;
return;
}

const maybeNewType = typeMapper(originalType, schema);

if (maybeNewType === undefined) {
newTypeMap[typeName] = originalType;
return;
}

newTypeMap[typeName] = annotateType(maybeNewType, originalType);
}
});

Expand Down Expand Up @@ -237,10 +247,13 @@ function mapFields(originalTypeMap: TypeMap, schema: GraphQLSchema, schemaMapper
});

if (isObjectType(originalType)) {
newTypeMap[typeName] = new GraphQLObjectType({
...((config as unknown) as GraphQLObjectTypeConfig<any, any>),
fields: newFieldConfigMap,
});
newTypeMap[typeName] = annotateType(
new GraphQLObjectType({
...((config as unknown) as GraphQLObjectTypeConfig<any, any>),
fields: newFieldConfigMap,
}),
originalType
);
} else if (isInterfaceType(originalType)) {
newTypeMap[typeName] = new GraphQLInterfaceType({
...((config as unknown) as GraphQLInterfaceTypeConfig<any, any>),
Expand Down Expand Up @@ -319,10 +332,13 @@ function mapArguments(originalTypeMap: TypeMap, schema: GraphQLSchema, schemaMap
});

if (isObjectType(originalType)) {
newTypeMap[typeName] = new GraphQLObjectType({
...((config as unknown) as GraphQLObjectTypeConfig<any, any>),
fields: newFieldConfigMap,
});
newTypeMap[typeName] = annotateType(
new GraphQLObjectType({
...((config as unknown) as GraphQLObjectTypeConfig<any, any>),
fields: newFieldConfigMap,
}),
originalType
);
} else if (isInterfaceType(originalType)) {
newTypeMap[typeName] = new GraphQLInterfaceType({
...((config as unknown) as GraphQLInterfaceTypeConfig<any, any>),
Expand Down
3 changes: 2 additions & 1 deletion packages/utils/src/rewire.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {

import { getBuiltInForStub, isNamedStub } from './stub';
import { TypeMap } from './Interfaces';
import { annotateType } from './annotate';

export function rewireTypes(
originalTypeMap: Record<string, GraphQLNamedType | null>,
Expand Down Expand Up @@ -62,7 +63,7 @@ export function rewireTypes(
});

Object.keys(newTypeMap).forEach(typeName => {
newTypeMap[typeName] = rewireNamedType(newTypeMap[typeName]);
newTypeMap[typeName] = annotateType(rewireNamedType(newTypeMap[typeName]), newTypeMap[typeName]);
});

const newDirectives = directives.map(directive => rewireDirective(directive));
Expand Down
43 changes: 42 additions & 1 deletion packages/utils/tests/mapSchema.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GraphQLObjectType, GraphQLSchema, graphqlSync } from 'graphql';
import { GraphQLObjectType, GraphQLSchema, graphqlSync, GraphQLString } from 'graphql';

import { makeExecutableSchema } from '@graphql-tools/schema';
import { MapperKind, mapSchema } from '@graphql-tools/utils';
Expand Down Expand Up @@ -60,4 +60,45 @@ describe('mapSchema', () => {
expect(newSchema).toBeInstanceOf(GraphQLSchema);
expect(newSchema.getQueryType().name).toBe('RootQuery');
});

test('can copy nonstandard properties', () => {
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
'test': {
type: GraphQLString
}
}
})
});

const symbol = Symbol('symbol');

Object.defineProperty(
schema.getQueryType(),
symbol,
{
enumerable: false,
configurable: false,
value: symbol,
}
);

Object.defineProperty(
schema.getQueryType(),
'value',
{
enumerable: false,
configurable: false,
value: 'value',
}
);

const newSchema = mapSchema(schema, {});

expect(newSchema).toBeInstanceOf(GraphQLSchema);
expect((newSchema.getQueryType() as unknown as { symbol: symbol })[symbol]).toBe(symbol);
expect((newSchema.getQueryType() as unknown as { value: string }).value).toBe('value');
});
});

0 comments on commit ba6791c

Please sign in to comment.