Skip to content

Commit 5a1876c

Browse files
authored
Merge pull request #6 from joeflack4/feature_apollo
Support for directives (1x/node atm)
2 parents 2aeb944 + f3a8775 commit 5a1876c

File tree

4 files changed

+180
-8
lines changed

4 files changed

+180
-8
lines changed

src/__tests__/jsonToGraphQLQuery.tests.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,105 @@ describe('jsonToGraphQL()', () => {
146146
}`);
147147
});
148148

149+
it('converts a simple query with args and directives with no arguments', () => {
150+
const query = {
151+
query: {
152+
Posts: {
153+
__args: {
154+
where: {
155+
id: 10,
156+
},
157+
orderBy: 'flibble'
158+
},
159+
__directives: {
160+
client: true
161+
},
162+
id: true,
163+
title: true,
164+
post_date: true
165+
}
166+
}
167+
} as any;
168+
expect(jsonToGraphQLQuery(query, { pretty: true })).to.equal(
169+
`query {
170+
Posts @client (where: {id: 10}, orderBy: "flibble") {
171+
id
172+
title
173+
post_date
174+
}
175+
}`);
176+
});
177+
178+
it('converts a complex query with directives with no arguments', () => {
179+
const query = {
180+
query: {
181+
diet: {
182+
__directives: {
183+
client: true
184+
},
185+
id: 'diet',
186+
options: {
187+
mood: {
188+
category: 'Diet',
189+
id: 'mood',
190+
selected: true,
191+
},
192+
weight: {
193+
category: 'Diet',
194+
icon: 'fa fa-question-circle',
195+
id: 'weight',
196+
selected: false,
197+
text: 'Weight'
198+
},
199+
},
200+
title: 'Diet'
201+
},
202+
someOtherAbritraryKey: {
203+
__directives: {
204+
client: true
205+
},
206+
arb1: 'arbitrary value',
207+
arb2: 'some other arbitrary value'
208+
}
209+
}
210+
};
211+
const expected = 'query { diet @client { id options { ' +
212+
'mood { category id selected } weight { category icon id text } } ' +
213+
'title } someOtherAbritraryKey @client { arb1 arb2 } }';
214+
expect(jsonToGraphQLQuery(query)).to.equal(expected);
215+
});
216+
217+
// TODO: Need this test still? How to handle variables unless $ declared explicitly?
218+
// it('converts a JavaScript object into a valid query, including single directives ' +
219+
// 'with args, so long as any variables used are enclosed in a string with "$" included', () => {
220+
// interface ILooseObject { [key: string]: any; }
221+
// let input: ILooseObject = {
222+
// someOtherAbritraryKey: {
223+
// __typename: 'someArbitraryObjType',
224+
// arb1: 'arbitrary value',
225+
// arb2: 'some other arbitrary value'
226+
// }
227+
// };
228+
// Object.keys(input)
229+
// .filter(filterNonConfigFields)
230+
// .forEach((key) => {
231+
// input[key]['__directives'] = { include: {if: '$isAwesome'}, };
232+
// });
233+
// input = {query: input};
234+
// const expected = 'query { someOtherAbritraryKey @include(if: $isAwesome) { arb1 arb2 } }';
235+
// expect(jsonToGraphQLQuery(input)).to.equal(expected);
236+
// });
237+
238+
// TODO
239+
// it('converts a JavaScript object into a valid query, including *multiple* directives ' +
240+
// 'with args, so long as any variables used are enclosed in a string with "$" included', () => {
241+
// });
242+
243+
// TODO
244+
// it('creates a query, stripping/ignoring certain, specified keys', () => {
245+
// // Example usage: jsonToGraphqlQuery(preInput, { keysToStrip: ['__typename'] });
246+
// });
247+
149248
it('converts a query with nested objects', () => {
150249
const query = {
151250
query: {
@@ -308,4 +407,35 @@ describe('jsonToGraphQL()', () => {
308407
}`);
309408
});
310409

410+
it('we can ignore apollo __typename keys', () => {
411+
const query = {
412+
query: {
413+
Posts: {
414+
__typename: 'Posts',
415+
id: true,
416+
title: true,
417+
post_date: true,
418+
subObject: {
419+
__typename: 'subObject',
420+
test: 'a value'
421+
},
422+
}
423+
}
424+
};
425+
expect(jsonToGraphQLQuery(query, {
426+
pretty: true,
427+
ignoreFields: ['__typename']
428+
})).to.equal(
429+
`query {
430+
Posts {
431+
id
432+
title
433+
post_date
434+
subObject {
435+
test
436+
}
437+
}
438+
}`);
439+
});
440+
311441
});

src/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
export * from './jsonToGraphQLQuery';
2-
export {EnumType} from './types/EnumType';
3-
export {VariableType} from './types/VariableType';
1+
2+
export * from './jsonToGraphQLQuery';
3+
export {EnumType} from './types/EnumType';
4+
export {VariableType} from './types/VariableType';

src/jsonToGraphQLQuery.ts

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,32 @@ function buildVariables(varsObj: any): string {
4545
return args.join(', ');
4646
}
4747

48+
function buildDirectives(dirsObj: any): string {
49+
const directiveName = Object.keys(dirsObj)[0];
50+
const directiveValue = dirsObj[directiveName];
51+
if (typeof directiveValue === 'boolean') {
52+
return directiveName;
53+
}
54+
else if (typeof directiveValue === 'object') {
55+
const args = [];
56+
for (const argName in directiveValue) {
57+
const argVal = stringify(directiveValue[argName]).replace(/"/g, '');
58+
args.push(`${argName}: ${argVal}`);
59+
}
60+
return `${directiveName}(${args.join(', ')})`;
61+
}
62+
else {
63+
throw new Error(`Unsupported type for directive: ${typeof directiveValue}. Types allowed: object, boolean.\n` +
64+
`Offending object: ${JSON.stringify(dirsObj)}`);
65+
}
66+
}
67+
4868
function getIndent(level: number): string {
4969
return Array((level * 4) + 1).join(' ');
5070
}
5171

5272
function filterNonConfigFields(fieldName: string, ignoreFields: string[]) {
73+
// Returns true if fieldName is not a 'configField'.
5374
return configFields.indexOf(fieldName) == -1 && ignoreFields.indexOf(fieldName) == -1;
5475
}
5576

@@ -61,14 +82,33 @@ function convertQuery(node: any, level: number, output: Array<[ string, number ]
6182
const fieldCount = Object.keys(node[key])
6283
.filter((keyCount) => filterNonConfigFields(keyCount, options.ignoreFields)).length;
6384
const subFields = fieldCount > 0;
85+
const argsExist = typeof node[key].__args === 'object';
86+
const directivesExist = typeof node[key].__directives === 'object';
6487
let token: string;
65-
66-
if (typeof node[key].__args === 'object') {
67-
token = `${key} (${buildArgs(node[key].__args)})`;
68-
}
69-
else if (typeof node[key].__variables === 'object') {
88+
if (typeof node[key].__variables === 'object') {
7089
token = `${key} (${buildVariables(node[key].__variables)})`;
7190
}
91+
else if (argsExist || directivesExist) {
92+
let argsStr: string;
93+
let dirsStr: string;
94+
if (directivesExist) {
95+
// TODO: Add support for multiple directives on one node. Then condense blocks into terniary lines.
96+
// const directives = Object.keys(node[key].__directives);
97+
const numDirectives = Object.keys(node[key].__directives).length;
98+
if (numDirectives > 1) {
99+
throw new Error(`Too many directives. The object/key ` +
100+
`'${Object.keys(node[key])[0]}' had ${numDirectives} directives, ` +
101+
`but only 1 directive per object/key is supported at this time.`);
102+
}
103+
// directives.map(((x) => { dirsStr += ` @${buildDirectives(node[key].__directives)}`; }));
104+
dirsStr = `@${buildDirectives(node[key].__directives)}`;
105+
}
106+
if (argsExist) {
107+
argsStr = `(${buildArgs(node[key].__args)})`;
108+
}
109+
const spacer = directivesExist && argsExist ? ' ' : '';
110+
token = `${key} ${dirsStr ? dirsStr : ''}${spacer}${argsStr ? argsStr : ''}`;
111+
}
72112
else {
73113
token = `${key}`;
74114
}

tslint.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"rules": {
66
"trailing-comma": [false],
77
"object-literal-sort-keys": false,
8+
"object-literal-key-quotes": [true, "as-needed"],
89
"ordered-imports": false,
910
"no-console": [false],
1011
"no-unused-new": false,

0 commit comments

Comments
 (0)