Skip to content

Commit

Permalink
feat(FieldArgTypeRewriter): retrieve var name by object path
Browse files Browse the repository at this point in the history
  • Loading branch information
Dima Snisarenko committed Aug 20, 2024
1 parent 9dbcdea commit 0e5beb5
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 5 deletions.
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
});
});
});

0 comments on commit 0e5beb5

Please sign in to comment.