Skip to content

Commit

Permalink
fix(url-loader): pass variables correctly on HTTP GET (#1898)
Browse files Browse the repository at this point in the history
  • Loading branch information
ardatan committed Aug 10, 2020
1 parent a19b809 commit 9cec4f5
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 36 deletions.
4 changes: 3 additions & 1 deletion packages/loaders/url/src/index.ts
Expand Up @@ -109,7 +109,9 @@ export class UrlLoader implements DocumentLoader<LoadFromUrlOptions> {
case 'GET':
const urlObj = new URL(HTTP_URL);
urlObj.searchParams.set('query', query);
urlObj.searchParams.set(variables, JSON.stringify(variables));
if (variables && Object.keys(variables).length > 0) {
urlObj.searchParams.set('variables', JSON.stringify(variables));
}
const finalUrl = urlObj.toString();
fetchResult = await fetch(finalUrl, {
method: 'GET',
Expand Down
82 changes: 66 additions & 16 deletions packages/loaders/url/tests/url-loader.spec.ts
Expand Up @@ -4,6 +4,7 @@ import { printSchemaWithDirectives } from '@graphql-tools/utils';
import nock from 'nock';
import { mockGraphQLServer } from '../../../testing/utils';
import { cwd } from 'process';
import { execute, parse } from 'graphql';

const SHOULD_NOT_GET_HERE_ERROR = 'SHOULD_NOT_GET_HERE';

Expand All @@ -15,20 +16,29 @@ schema { query: CustomQuery }
"""Test type comment"""
type CustomQuery {
"""Test field comment"""
a: String
a(testVariable: String): String
}
`.trim();

const testSchema = makeExecutableSchema({ typeDefs: testTypeDefs });
const testResolvers = {
CustomQuery: {
a: (_: never, { testVariable }: { testVariable: string }) => testVariable || 'a',
}
}

const testSchema = makeExecutableSchema({ typeDefs: testTypeDefs, resolvers: testResolvers });

const testHost = `http://localhost:3000`;
const testPath = `/graphql`;
const testPath = '/graphql';
const testPathChecker = (path: string) => {
return path.startsWith(testPath);
};
const testUrl = `${testHost}${testPath}`;

describe('handle', () => {
it('Should throw an error when introspection is not valid', async () => {
const brokenData = {data: {}};
const scope = nock(testHost).post(testPath).reply(200, brokenData);
const brokenData = { data: {} };
const scope = nock(testHost).post(testPathChecker).reply(200, brokenData);

try {
await loader.load(testUrl, {});
Expand All @@ -42,7 +52,7 @@ type CustomQuery {
});

it('Should return a valid schema when request is valid', async () => {
const server = mockGraphQLServer({schema: testSchema, host: testHost, path: testPath});
const server = mockGraphQLServer({ schema: testSchema, host: testHost, path: testPathChecker });

const schema = await loader.load(testUrl, {});

Expand All @@ -55,9 +65,11 @@ type CustomQuery {
it('Should pass default headers', async () => {
let headers: Record<string, string> = {}

const server = mockGraphQLServer({schema: testSchema, host: testHost, path: testPath, intercept(ctx) {
headers = ctx.req.headers
}});
const server = mockGraphQLServer({
schema: testSchema, host: testHost, path: testPathChecker, intercept(ctx) {
headers = ctx.req.headers
}
});

const schema = await loader.load(testUrl, {});

Expand All @@ -73,9 +85,11 @@ type CustomQuery {

it('Should pass extra headers when they are specified as object', async () => {
let headers: Record<string, string> = {}
const server = mockGraphQLServer({schema: testSchema, host: testHost, path: testPath, intercept(ctx) {
headers = ctx.req.headers
}});
const server = mockGraphQLServer({
schema: testSchema, host: testHost, path: testPathChecker, intercept(ctx) {
headers = ctx.req.headers
}
});

const schema = await loader.load(testUrl, { headers: { Auth: '1' } });

Expand All @@ -92,9 +106,11 @@ type CustomQuery {

it('Should pass extra headers when they are specified as array', async () => {
let headers: Record<string, string> = {}
const server = mockGraphQLServer({schema: testSchema, host: testHost, path: testPath, intercept(ctx) {
headers = ctx.req.headers
}});
const server = mockGraphQLServer({
schema: testSchema, host: testHost, path: testPathChecker, intercept(ctx) {
headers = ctx.req.headers
}
});
const schema = await loader.load(testUrl, { headers: [{ A: '1' }, { B: '2', C: '3' }] });

server.done();
Expand All @@ -111,7 +127,7 @@ type CustomQuery {
});

it('Should utilize extra introspection options', async () => {
const server = mockGraphQLServer({schema: testSchema, host: testHost, path: testPath});
const server = mockGraphQLServer({ schema: testSchema, host: testHost, path: testPathChecker });
const source = await loader.load(testUrl, { descriptions: false });

server.done();
Expand All @@ -123,5 +139,39 @@ type CustomQuery {
it('Absolute file path should not be accepted as URL', async () => {
expect(await loader.canLoad(cwd(), {})).toBeFalsy();
});
it('should handle useGETForQueries correctly', async () => {

const server = mockGraphQLServer({ schema: testSchema, host: testHost, path: testPathChecker, method: 'GET' });

const source = await loader.load(testUrl, {
descriptions: false,
useGETForQueries: true
});

server.done();

const testVariableValue = 'A';

const server2 = mockGraphQLServer({ schema: testSchema, host: testHost, path: testPathChecker, method: 'GET' });

const result = await execute({
schema: source.schema,
document: parse(/* GraphQL */`
query TestQuery($testVariable: String) {
a(testVariable: $testVariable)
}
`),
variableValues: {
testVariable: testVariableValue
}
});

server2.done();

expect(result?.errors).toBeFalsy();

expect(result?.data?.a).toBe(testVariableValue);

})
});
});
62 changes: 43 additions & 19 deletions packages/testing/utils.ts
Expand Up @@ -86,29 +86,53 @@ export function mockGraphQLServer({
host,
path,
intercept,
method = 'POST',
}: {
schema: GraphQLSchema;
host: string;
path: string;
path: string | RegExp | ((path: string) => boolean);
intercept?: (obj: nock.ReplyFnContext) => void;
method?: string;
}) {
return nock(host)
.post(path)
.reply(async function (_: any, body: any) {
try {
if (intercept) {
intercept(this);
}

const result = await execute({
schema,
document: parse(body.query),
operationName: body.operationName,
variableValues: body.variables,
switch (method) {
case 'GET':
return nock(host)
.get(path)
.reply(async function (uri) {
const urlObj = new URL(host + uri);
try {
if (intercept) {
intercept(this);
}
const result = await execute({
schema,
document: parse(urlObj.searchParams.get('query')),
operationName: urlObj.searchParams.get('operationName'),
variableValues: JSON.parse(urlObj.searchParams.get('variables') || '{}'),
});
return [200, result];
} catch (error) {
return [500, error];
}
});
return [200, result];
} catch (error) {
return [500, error];
}
});
case 'POST':
return nock(host)
.post(path)
.reply(async function (_: string, body: any) {
try {
if (intercept) {
intercept(this);
}
const result = await execute({
schema,
document: parse(body.query),
operationName: body.operationName,
variableValues: body.variables,
});
return [200, result];
} catch (error) {
return [500, error];
}
});
}
}

0 comments on commit 9cec4f5

Please sign in to comment.