diff --git a/experiments/graphql-tools-mocking.md b/experiments/graphql-tools-mocking.md index bc42e6c..206dc79 100644 --- a/experiments/graphql-tools-mocking.md +++ b/experiments/graphql-tools-mocking.md @@ -2,18 +2,10 @@ ## Setup -This assumes we are using volta. - -Install `ts-node`: - -```sh -$ volta install ts-node -``` - Run the demo (that includes a server) by running: ```sh -$ ts-node graphql-tools-mocking/index.ts +$ yarn ts-node graphql-tools-mocking/index.ts ``` You should be able to see the result of the query that was executed directly. diff --git a/graphql-tools-mocking/index.ts b/graphql-tools-mocking/index.ts index 4f7ac44..2568154 100644 --- a/graphql-tools-mocking/index.ts +++ b/graphql-tools-mocking/index.ts @@ -1,6 +1,7 @@ import { makeExecutableSchema } from '@graphql-tools/schema'; import { addMocksToSchema } from '@graphql-tools/mock'; -import { graphql } from 'graphql'; +import { mapSchema, getDirective, MapperKind } from '@graphql-tools/utils'; +import { graphql, GraphQLSchema, defaultFieldResolver } from 'graphql'; import { faker } from '@faker-js/faker'; import { createServer } from '@graphql-yoga/node'; @@ -15,6 +16,7 @@ const schemaString = ` description: String date: Date author: BookAuthor + urn: String @urn(type: "foo", fields: [{ name: "first" }, { name: "last" }]) } type BookAuthor { @@ -35,8 +37,68 @@ const schemaString = ` } `; +let id = 1; + +function idGenerator() { + return id++; +} + +function urnDirective(schema: GraphQLSchema): GraphQLSchema { + const directiveName = 'urn'; + + return mapSchema(schema, { + [MapperKind.OBJECT_FIELD]: (fieldConfig) => { + const urnDirectiveArgs = getDirective( + schema, + fieldConfig, + directiveName + )?.[0] as + | { + type: string; + fields?: []; + } + | undefined; + + if (urnDirectiveArgs) { + const { resolve = defaultFieldResolver } = fieldConfig; + + return { + ...fieldConfig, + resolve: async function (source, args, context, info) { + const result = await resolve(source, args, context, info); + + if (typeof result === 'string') { + const { type, fields = [] } = urnDirectiveArgs; + + const baseUrn = `unique_urn_with_parts_${type}`; + + if (fields.length) { + return `${baseUrn}:(${fields.map(() => idGenerator())})`; + } + + return baseUrn; + } + + return result; + }, + }; + } + }, + }); +} + +const urnDirectiveTypeDef = () => ` + directive @urn(type: String!, fields: [FieldMetadata!]) on FIELD_DEFINITION + + input FieldMetadata { + name: String + } +`; + // Make a GraphQL schema with no resolvers -const schema = makeExecutableSchema({ typeDefs: schemaString }); +const schema = makeExecutableSchema({ + typeDefs: [schemaString, urnDirectiveTypeDef()], +}); // Create a new schema with mocks const schemaWithMocks = addMocksToSchema({ @@ -58,9 +120,11 @@ const schemaWithMocks = addMocksToSchema({ }, }); +const schemaWithUrnDirective = urnDirective(schemaWithMocks); + // optionally we can serve the schema in the network const server = createServer({ - schema: schemaWithMocks, + schema: schemaWithUrnDirective, }); server.start(); @@ -71,6 +135,7 @@ const query = /* GraphQL */ ` id description date + urn author { id firstName @@ -82,7 +147,7 @@ const query = /* GraphQL */ ` `; graphql({ - schema: schemaWithMocks, + schema: schemaWithUrnDirective, source: query, }).then((result) => { console.log('Got result %o', result); diff --git a/package.json b/package.json index 36f6e6e..3f752e8 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ "graphql": "^16.5.0", "graphql-faker": "^2.0.0-rc.25", "graphql-mocks": "^0.8.4", - "prettier": "^2.7.1" + "prettier": "^2.7.1", + "ts-node": "^10.9.1" }, "dependencies": { "typescript": "^4.8.2" diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..f170be1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "es2016", + "module": "commonjs", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true + } +} diff --git a/yarn.lock b/yarn.lock index 71dd217..e87a302 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3268,7 +3268,7 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -ts-node@^10.5.0: +ts-node@^10.5.0, ts-node@^10.9.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==