Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion src/ast.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { ASTNode, DocumentNode, FragmentDefinitionNode, VariableDefinitionNode } from 'graphql';
import {
ASTNode,
DocumentNode,
FragmentDefinitionNode, Kind,
ObjectValueNode, ValueNode,
VariableDefinitionNode
} from 'graphql';
import { pushToArrayAtKey } from './utils';

const ignoreKeys = new Set(['loc']);
Expand Down Expand Up @@ -294,3 +300,24 @@ export const rewriteResultsAtPath = (

return newResults;
};

export const getByPath = (node: ObjectValueNode, path: ReadonlyArray<string>): ValueNode | undefined => {
const [firstSegment, ...theRestOfSegments] = path;

if (!firstSegment) return undefined;

const theField = node.fields
.find(field => field.name.value === firstSegment);

if (!theField) return undefined;

if (theRestOfSegments.length === 0) {
return theField.value;
}

if (theField.value.kind === Kind.OBJECT) {
return getByPath(theField.value, theRestOfSegments)
}

return undefined;
}
19 changes: 15 additions & 4 deletions src/rewriters/FieldArgTypeRewriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import {
parseType,
TypeNode,
ValueNode,
VariableNode
} from 'graphql';
import Maybe from 'graphql/tsutils/Maybe';
import { NodeAndVarDefs, nodesMatch } from '../ast';
import { getByPath, NodeAndVarDefs, nodesMatch } from '../ast';
import { identifyFunc } from '../utils';
import Rewriter, { RewriterOpts, Variables } from './Rewriter';

interface FieldArgTypeRewriterOpts extends RewriterOpts {
argName: string;
objectPath?: ReadonlyArray<string>;
oldType: string;
newType: string;
coerceVariable?: (variable: any, context: { variables: Variables; args: ArgumentNode[] }) => any;
Expand All @@ -37,6 +37,7 @@ interface FieldArgTypeRewriterOpts extends RewriterOpts {
*/
class FieldArgTypeRewriter extends Rewriter {
protected argName: string;
protected objectPath: ReadonlyArray<string>;
protected oldTypeNode: TypeNode;
protected newTypeNode: TypeNode;
// Passes context with rest of arguments and variables.
Expand All @@ -56,6 +57,7 @@ class FieldArgTypeRewriter extends Rewriter {
constructor(options: FieldArgTypeRewriterOpts) {
super(options);
this.argName = options.argName;
this.objectPath = options.objectPath || [];
this.oldTypeNode = parseType(options.oldType);
this.newTypeNode = parseType(options.newType);
this.coerceVariable = options.coerceVariable || identifyFunc;
Expand Down Expand Up @@ -154,8 +156,17 @@ class FieldArgTypeRewriter extends Rewriter {
const matchingArgument = (node.arguments || []).find(
arg => arg.name.value === this.argName
) as ArgumentNode;
const variableNode = matchingArgument.value as VariableNode;
return variableNode.kind === Kind.VARIABLE && variableNode.name.value;

const valueNode =
this.objectPath && matchingArgument.value.kind === Kind.OBJECT
? getByPath(matchingArgument.value, this.objectPath)
: matchingArgument.value;

if (!valueNode) {
return false;
}

return valueNode.kind === Kind.VARIABLE && valueNode.name.value;
}
}

Expand Down
86 changes: 86 additions & 0 deletions test/functional/rewriteFieldArgType.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,90 @@ describe('Rewrite field arg type', () => {
}
});
});

it('recognizes and considers object path in nested objects', () => {
const handler = new RewriteHandler([
new FieldArgTypeRewriter({
fieldName: 'things',
argName: 'input',
objectPath: ['identifier', 'level_2', 'level_3'],
oldType: 'String!',
newType: 'Int!',
})
]);

const query = gqlFmt`
mutation doTheThings($arg1: String!, $arg2: Int!, $arg3: String!) {
things(input: {identifier: {level_2: {level_3: $arg1}}, otherArg: $arg2}) {
cat
dog {
catdog
}
}
otherThing(arg3: $arg3) {
otherThingField
}
}
`;
const expectedRewritenQuery = gqlFmt`
mutation doTheThings($arg1: Int!, $arg2: Int!, $arg3: String!) {
things(input: {identifier: {level_2: {level_3: $arg1}}, otherArg: $arg2}) {
cat
dog {
catdog
}
}
otherThing(arg3: $arg3) {
otherThingField
}
}
`;
expect(handler.rewriteRequest(query)).toEqual({
query: expectedRewritenQuery,
variables: undefined
});
});

it('recognizes and considers single key path in an object', () => {
const handler = new RewriteHandler([
new FieldArgTypeRewriter({
fieldName: 'things',
argName: 'input',
objectPath: ['identifier'],
oldType: 'String!',
newType: 'Int!',
})
]);

const query = gqlFmt`
mutation doTheThings($arg1: String!, $arg2: Int!, $arg3: String!) {
things(input: {identifier: $arg1, otherArg: $arg2}) {
cat
dog {
catdog
}
}
otherThing(arg3: $arg3) {
otherThingField
}
}
`;
const expectedRewritenQuery = gqlFmt`
mutation doTheThings($arg1: Int!, $arg2: Int!, $arg3: String!) {
things(input: {identifier: $arg1, otherArg: $arg2}) {
cat
dog {
catdog
}
}
otherThing(arg3: $arg3) {
otherThingField
}
}
`;
expect(handler.rewriteRequest(query)).toEqual({
query: expectedRewritenQuery,
variables: undefined
});
});
});