Skip to content

Commit

Permalink
feat: Make encapsulate transform outer type names parameterizable (#5337
Browse files Browse the repository at this point in the history
)

* feat: Make encapsulate transform outer type names parameterizable

* chore: Define changeset

* No need for extra configuration

---------

Co-authored-by: Arda TANRIKULU <[email protected]>
  • Loading branch information
cweckesser and ardatan authored Apr 20, 2023
1 parent 14883d5 commit a597261
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 25 deletions.
10 changes: 10 additions & 0 deletions .changeset/young-books-pull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@graphql-mesh/transform-encapsulate': minor
'@graphql-mesh/types': minor
---

If a schema defines root types which names are not the standard/expected ones ("Query", "Mutation"
and "Subscription") when using the encapsulation transform, the encapsulation will not succeed. The
proposed solution is to allow parameterizing the root type names, for the cases where they don't
follow the standard/expected ones. The new parameters are optional and have default values (the
"standard" values).
2 changes: 1 addition & 1 deletion examples/neo4j-example/.meshrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ sources:
- name: Neo4j
handler:
neo4j:
endpoint: bolt+s://demo.neo4jlabs.com:7687
endpoint: bolt://demo.neo4jlabs.com:7687
username: movies
password: movies
database: movies
Expand Down
60 changes: 36 additions & 24 deletions packages/transforms/encapsulate/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,66 @@
import { GraphQLSchema } from 'graphql';
import { GraphQLSchema, OperationTypeNode } from 'graphql';
import { MeshTransform, MeshTransformOptions, YamlConfig } from '@graphql-mesh/types';
import {
applyRequestTransforms,
applyResultTransforms,
applySchemaTransforms,
} from '@graphql-mesh/utils';
import { DelegationContext, SubschemaConfig, Transform } from '@graphql-tools/delegate';
import { ExecutionRequest, ExecutionResult, selectObjectFields } from '@graphql-tools/utils';
import {
ExecutionRequest,
ExecutionResult,
getRootTypeMap,
selectObjectFields,
} from '@graphql-tools/utils';
import { WrapType } from '@graphql-tools/wrap';

const DEFUALT_APPLY_TO = {
const OPERATION_TYPE_SUFFIX_MAP = {
query: 'Query',
mutation: 'Mutation',
subscription: 'Subscription',
};

const DEFAULT_APPLY_TO = {
query: true,
mutation: true,
subscription: true,
};

type RootType = 'Query' | 'Mutation' | 'Subscription';

export default class EncapsulateTransform implements MeshTransform {
private transformMap: Partial<Record<RootType, Transform>> = {};
private transformMap: Partial<Record<string, Transform>> = {};
private transforms: Transform[] = [];
private config: YamlConfig.Transform['encapsulate'];
private name: string;

constructor(options: MeshTransformOptions<YamlConfig.Transform['encapsulate']>) {
const config = options.config;
const name = config?.name || options.apiName;
this.config = options.config;
this.name = this.config?.name || options.apiName;

if (!name) {
if (!this.name) {
throw new Error(
`Unable to execute encapsulate transform without a name. Please make sure to use it over a specific schema, or specify a name in your configuration!`,
);
}
}

const applyTo = { ...DEFUALT_APPLY_TO, ...(config?.applyTo || {}) };
*generateSchemaTransforms(originalWrappingSchema: GraphQLSchema) {
const applyTo = { ...DEFAULT_APPLY_TO, ...(this.config?.applyTo || {}) } as Record<
OperationTypeNode,
boolean
>;
const outerTypeNames = getRootTypeMap(originalWrappingSchema);

if (applyTo.query) {
this.transformMap.Query = new WrapType('Query', `${name}Query`, name) as any;
}
if (applyTo.mutation) {
this.transformMap.Mutation = new WrapType('Mutation', `${name}Mutation`, name) as any;
}
if (applyTo.subscription) {
this.transformMap.Subscription = new WrapType(
'Subscription',
`${name}Subscription`,
name,
) as any;
for (const operationType in applyTo) {
const outerTypeName = outerTypeNames.get(operationType as OperationTypeNode)?.name;
if (outerTypeName) {
this.transformMap[outerTypeName] = new WrapType(
outerTypeName,
`${this.name}${OPERATION_TYPE_SUFFIX_MAP[operationType as OperationTypeNode]}`,
this.name,
) as any;
}
}
}

*generateSchemaTransforms(originalWrappingSchema: GraphQLSchema) {
for (const typeName of Object.keys(this.transformMap)) {
const fieldConfigMap = selectObjectFields(originalWrappingSchema, typeName, () => true);
if (Object.keys(fieldConfigMap).length) {
Expand Down
77 changes: 77 additions & 0 deletions packages/transforms/encapsulate/test/encapsulate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,81 @@ describe('encapsulate', () => {

expect(data).not.toBeNull();
});

const customSchema = makeExecutableSchema({
typeDefs: /* GraphQL */ `
schema {
query: CustomQuery
mutation: CustomMutation
subscription: CustomSubscription
}
type CustomQuery {
retrieveSomething: String!
}
type CustomMutation {
doSomething: String!
}
type CustomSubscription {
notifySomething: String!
}
`,
resolvers: {
CustomQuery: {
retrieveSomething: () => 'something retrieved',
},
CustomMutation: {
doSomething: () => 'something done',
},
CustomSubscription: {
notifySomething: () => 'something notified',
},
},
});

it('should be able to encapsulate schema root types with custom names', async () => {
const newCustomSchema = wrapSchema({
schema: customSchema,
transforms: [
new Transform({
config: {
name: 'MyCustomSchema',
},
cache,
pubsub,
baseDir,
apiName: undefined,
importFn,
logger: new DefaultLogger(),
}),
],
});

expect(newCustomSchema.getQueryType().getFields().MyCustomSchema).toBeDefined();
expect(newCustomSchema.getQueryType().getFields().MyCustomSchema.type.toString()).toBe(
'MyCustomSchemaQuery!',
);
expect(
(newCustomSchema.getQueryType().getFields().MyCustomSchema.type as any).ofType._fields
.retrieveSomething,
).toBeDefined();
expect(newCustomSchema.getMutationType().getFields().MyCustomSchema).toBeDefined();
expect(newCustomSchema.getMutationType().getFields().MyCustomSchema.type.toString()).toBe(
'MyCustomSchemaMutation!',
);
expect(
(newCustomSchema.getMutationType().getFields().MyCustomSchema.type as any).ofType._fields
.doSomething,
).toBeDefined();
expect(newCustomSchema.getSubscriptionType().getFields().MyCustomSchema).toBeDefined();
expect(newCustomSchema.getSubscriptionType().getFields().MyCustomSchema.type.toString()).toBe(
'MyCustomSchemaSubscription!',
);
expect(
(newCustomSchema.getSubscriptionType().getFields().MyCustomSchema.type as any).ofType._fields
.notifySomething,
).toBeDefined();
});
});

0 comments on commit a597261

Please sign in to comment.