-
-
Notifications
You must be signed in to change notification settings - Fork 809
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Get arguments with directive (#4661)
* First pass: a functionning implementation * cleaner * add changeset * fix compilation * Move test file to correct location * empty to trigger ci
- Loading branch information
Showing
6 changed files
with
224 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@graphql-tools/utils': minor | ||
--- | ||
|
||
Add getArgumentsWithDirectives |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { DirectiveUsage } from './types.js'; | ||
|
||
import { ASTNode, DocumentNode, Kind, ObjectTypeDefinitionNode, valueFromASTUntyped } from 'graphql'; | ||
|
||
function isTypeWithFields(t: ASTNode): t is ObjectTypeDefinitionNode { | ||
return t.kind === Kind.OBJECT_TYPE_DEFINITION || t.kind === Kind.OBJECT_TYPE_EXTENSION; | ||
} | ||
|
||
export type ArgumentToDirectives = { | ||
[argumentName: string]: DirectiveUsage[]; | ||
}; | ||
export type TypeAndFieldToArgumentDirectives = { | ||
[typeAndField: string]: ArgumentToDirectives; | ||
}; | ||
|
||
export function getArgumentsWithDirectives(documentNode: DocumentNode): TypeAndFieldToArgumentDirectives { | ||
const result: TypeAndFieldToArgumentDirectives = {}; | ||
|
||
const allTypes = documentNode.definitions.filter(isTypeWithFields); | ||
|
||
for (const type of allTypes) { | ||
if (type.fields == null) { | ||
continue; | ||
} | ||
|
||
for (const field of type.fields) { | ||
const argsWithDirectives = field.arguments?.filter(arg => arg.directives?.length); | ||
|
||
if (!argsWithDirectives?.length) { | ||
continue; | ||
} | ||
|
||
const typeFieldResult = (result[`${type.name.value}.${field.name.value}`] = {}); | ||
|
||
for (const arg of argsWithDirectives) { | ||
const directives: DirectiveUsage[] = arg.directives!.map(d => ({ | ||
name: d.name.value, | ||
args: (d.arguments || []).reduce( | ||
(prev, dArg) => ({ ...prev, [dArg.name.value]: valueFromASTUntyped(dArg.value) }), | ||
{} | ||
), | ||
})); | ||
|
||
typeFieldResult[arg.name.value] = directives; | ||
} | ||
} | ||
} | ||
|
||
return result; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
162 changes: 162 additions & 0 deletions
162
packages/utils/tests/get-arguments-with-directives.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
import { parse } from 'graphql'; | ||
import { getArgumentsWithDirectives } from '../src/index.js'; | ||
|
||
describe('getArgumentsWithDirectives', () => { | ||
it('Should detect single basic directive', () => { | ||
const node = parse(/* GraphQL */ ` | ||
type A { | ||
f1(anArg: String @a): Int | ||
} | ||
`); | ||
|
||
const result = getArgumentsWithDirectives(node); | ||
expect(result['A.f1']).toEqual({ anArg: [{ name: 'a', args: {} }] }); | ||
}); | ||
|
||
it('Should detect single basic directive in a type extension', () => { | ||
const node = parse(/* GraphQL */ ` | ||
extend type A { | ||
f1(anArg: String @a): Int | ||
} | ||
`); | ||
|
||
const result = getArgumentsWithDirectives(node); | ||
expect(result['A.f1']).toEqual({ anArg: [{ name: 'a', args: {} }] }); | ||
}); | ||
|
||
it('Should parse string argument correctly', () => { | ||
const node = parse(/* GraphQL */ ` | ||
type A { | ||
f1(anArg: String @a(f: "1")): Int | ||
} | ||
`); | ||
|
||
const result = getArgumentsWithDirectives(node); | ||
expect(result['A.f1']).toEqual({ anArg: [{ name: 'a', args: { f: '1' } }] }); | ||
}); | ||
|
||
it('Should parse multiple arguments correctly', () => { | ||
const node = parse(/* GraphQL */ ` | ||
type A { | ||
f1(anArg: String @a(a1: "1", a2: 10)): Int | ||
} | ||
`); | ||
|
||
const result = getArgumentsWithDirectives(node); | ||
expect(result['A.f1']).toEqual({ anArg: [{ name: 'a', args: { a1: '1', a2: 10 } }] }); | ||
}); | ||
|
||
it('Should parse object arg correctly', () => { | ||
const node = parse(/* GraphQL */ ` | ||
type A { | ||
f1(anArg: String @a(a1: { foo: "bar" })): Int | ||
} | ||
`); | ||
|
||
const result = getArgumentsWithDirectives(node); | ||
expect(result['A.f1']).toEqual({ anArg: [{ name: 'a', args: { a1: { foo: 'bar' } } }] }); | ||
}); | ||
|
||
it('Should parse array arg correctly', () => { | ||
const node = parse(/* GraphQL */ ` | ||
type A { | ||
f1(anArg: String @a(a1: [1, 2, 3])): Int | ||
} | ||
`); | ||
|
||
const result = getArgumentsWithDirectives(node); | ||
expect(result['A.f1']).toEqual({ anArg: [{ name: 'a', args: { a1: [1, 2, 3] } }] }); | ||
}); | ||
|
||
it('Should parse complex array arg correctly', () => { | ||
const node = parse(/* GraphQL */ ` | ||
type A { | ||
f1(anArg: String @a(a1: ["a", 1, { c: 3, d: true }])): Int | ||
} | ||
`); | ||
|
||
const result = getArgumentsWithDirectives(node); | ||
expect(result['A.f1']).toEqual({ anArg: [{ name: 'a', args: { a1: ['a', 1, { c: 3, d: true }] } }] }); | ||
}); | ||
|
||
it('Should detect multiple directives', () => { | ||
const node = parse(/* GraphQL */ ` | ||
type A { | ||
f1(anArg: String @a @b): Int | ||
} | ||
`); | ||
|
||
const result = getArgumentsWithDirectives(node); | ||
expect(result['A.f1']).toEqual({ | ||
anArg: [ | ||
{ name: 'a', args: {} }, | ||
{ name: 'b', args: {} }, | ||
], | ||
}); | ||
}); | ||
|
||
it('Should detect multiple directives and multiple arguments', () => { | ||
const node = parse(/* GraphQL */ ` | ||
type A { | ||
f1(anArg: String @a @b, anotherArg: String @c): Int | ||
} | ||
`); | ||
|
||
const result = getArgumentsWithDirectives(node); | ||
expect(result['A.f1']).toEqual({ | ||
anArg: [ | ||
{ name: 'a', args: {} }, | ||
{ name: 'b', args: {} }, | ||
], | ||
anotherArg: [{ name: 'c', args: {} }], | ||
}); | ||
}); | ||
|
||
it('Should detect multiple directives and multiple fields', () => { | ||
const node = parse(/* GraphQL */ ` | ||
type A { | ||
f1(anArg: String @a @b): Int | ||
f2(anotherArg: String @c): Int | ||
} | ||
`); | ||
|
||
const result = getArgumentsWithDirectives(node); | ||
expect(result['A.f1']).toEqual({ | ||
anArg: [ | ||
{ name: 'a', args: {} }, | ||
{ name: 'b', args: {} }, | ||
], | ||
}); | ||
expect(result['A.f2']).toEqual({ anotherArg: [{ name: 'c', args: {} }] }); | ||
}); | ||
|
||
it('Should detect multiple types', () => { | ||
const node = parse(/* GraphQL */ ` | ||
type A { | ||
f1(anArg: String @a): Int | ||
} | ||
type B { | ||
f2(anArg: String @a): Int | ||
} | ||
`); | ||
|
||
const result = getArgumentsWithDirectives(node); | ||
expect(result['A.f1']).toEqual({ anArg: [{ name: 'a', args: {} }] }); | ||
expect(result['B.f2']).toEqual({ anArg: [{ name: 'a', args: {} }] }); | ||
}); | ||
|
||
it('Should include only fields with arguments with directives', () => { | ||
const node = parse(/* GraphQL */ ` | ||
type A { | ||
f1: String @a | ||
f2(anArg: Int): Int | ||
f3(anArg: String @a): Int | ||
} | ||
`); | ||
|
||
const result = getArgumentsWithDirectives(node); | ||
expect(result['A.f3']).toBeDefined(); | ||
expect(Object.keys(result).length).toBe(1); | ||
}); | ||
}); |
403ed45
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
graphql-tools – ./
graphql-tools-theguild.vercel.app
graphql-tools.vercel.app
www.graphql-tools.com
graphql-tools.com
graphql-tools-git-master-theguild.vercel.app