Skip to content

Commit 55c11b5

Browse files
committed
Merge branch 'jeniffer9-master'
2 parents 776e3d3 + 3bd939f commit 55c11b5

File tree

4 files changed

+177
-39
lines changed

4 files changed

+177
-39
lines changed

README.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Supported Options:
3131
* Support for Enum values via [`EnumType`](#query-with-enum-values)
3232
* Support for variables via [`__variables`](#query-with-variables)
3333
* Support for simple directives (such as `@client`) via [`__directives`](#query-with-directives)
34+
* Support for inline fragments via [`__on.__fragmentName`](#query-with-inline-fragments)
3435

3536
## Recent Changes
3637

@@ -341,6 +342,75 @@ query {
341342
}
342343
```
343344

345+
### Query with Inline Fragments
346+
347+
```typescript
348+
import { jsonToGraphQLQuery } from 'json-to-graphql-query';
349+
350+
const query = {
351+
query: {
352+
Posts: {
353+
title: true
354+
__on: {
355+
__fragmentName: "ConfigurablePost",
356+
id: true
357+
}
358+
}
359+
}
360+
};
361+
const graphql_query = jsonToGraphQLQuery(query, { pretty: true });
362+
```
363+
364+
Resulting `graphql_query`
365+
366+
```graphql
367+
query {
368+
Posts {
369+
title
370+
... on ConfigurablePost {
371+
id
372+
}
373+
}
374+
}
375+
```
376+
377+
### Query with multiple Inline Fragments
378+
```typescript
379+
import { jsonToGraphQLQuery } from 'json-to-graphql-query';
380+
381+
const query = {
382+
query: {
383+
Posts: {
384+
__on: [
385+
{
386+
__fragmentName: "ConfigurablePost",
387+
id: true
388+
},
389+
{
390+
__fragmentName: "UnconfigurablePost",
391+
name: true
392+
}]
393+
}
394+
}
395+
};
396+
const graphql_query = jsonToGraphQLQuery(query, { pretty: true });
397+
```
398+
Resulting `graphql_query`
399+
400+
```graphql
401+
query {
402+
Posts {
403+
title
404+
... on ConfigurablePost {
405+
id
406+
}
407+
... on UnconfigurablePost {
408+
name
409+
}
410+
}
411+
}
412+
```
413+
344414
## TO-DO List
345415

346416
* Support Named Queries / Mutations

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "json-to-graphql-query",
3-
"version": "1.7.0",
3+
"version": "1.8.0",
44
"main": "lib/index.js",
55
"license": "MIT",
66
"scripts": {

src/__tests__/jsonToGraphQLQuery.tests.ts

Lines changed: 82 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ describe('jsonToGraphQL()', () => {
3333
}
3434
};
3535
expect(jsonToGraphQLQuery(query, { pretty: true })).to.equal(
36-
`query {
36+
`query {
3737
Posts {
3838
id
3939
title
@@ -57,7 +57,7 @@ describe('jsonToGraphQL()', () => {
5757
}
5858
};
5959
expect(jsonToGraphQLQuery(query, { pretty: true })).to.equal(
60-
`query {
60+
`query {
6161
Posts (orderBy: "post_date", userId: 12) {
6262
id
6363
title
@@ -84,7 +84,7 @@ describe('jsonToGraphQL()', () => {
8484
}
8585
};
8686
expect(jsonToGraphQLQuery(query, { pretty: true })).to.equal(
87-
`query {
87+
`query {
8888
Posts (where: {published: true, rating: {_gt: 3}}, orderBy: "post_date") {
8989
id
9090
title
@@ -111,7 +111,7 @@ describe('jsonToGraphQL()', () => {
111111
}
112112
};
113113
expect(jsonToGraphQLQuery(query, { pretty: true })).to.equal(
114-
`query {
114+
`query {
115115
Posts (or: [{published: true}, {rating: [{_gt: 3}]}], orderBy: "post_date") {
116116
id
117117
title
@@ -137,7 +137,7 @@ describe('jsonToGraphQL()', () => {
137137
}
138138
} as any;
139139
expect(jsonToGraphQLQuery(query, { pretty: true })).to.equal(
140-
`query {
140+
`query {
141141
Posts (where: {id: null}, orderBy: null) {
142142
id
143143
title
@@ -166,7 +166,7 @@ describe('jsonToGraphQL()', () => {
166166
}
167167
} as any;
168168
expect(jsonToGraphQLQuery(query, { pretty: true })).to.equal(
169-
`query {
169+
`query {
170170
Posts @client (where: {id: 10}, orderBy: "flibble") {
171171
id
172172
title
@@ -209,8 +209,8 @@ describe('jsonToGraphQL()', () => {
209209
}
210210
};
211211
const expected = 'query { diet @client { id options { ' +
212-
'mood { category id selected } weight { category icon id text } } ' +
213-
'title } someOtherAbritraryKey @client { arb1 arb2 } }';
212+
'mood { category id selected } weight { category icon id text } } ' +
213+
'title } someOtherAbritraryKey @client { arb1 arb2 } }';
214214
expect(jsonToGraphQLQuery(query)).to.equal(expected);
215215
});
216216

@@ -260,7 +260,7 @@ describe('jsonToGraphQL()', () => {
260260
}
261261
};
262262
expect(jsonToGraphQLQuery(query, { pretty: true })).to.equal(
263-
`query {
263+
`query {
264264
Posts {
265265
id
266266
title
@@ -295,7 +295,7 @@ describe('jsonToGraphQL()', () => {
295295
}
296296
};
297297
expect(jsonToGraphQLQuery(query, { pretty: true })).to.equal(
298-
`query {
298+
`query {
299299
Posts (arg1: 20, arg2: "flibble") {
300300
id
301301
title
@@ -320,7 +320,7 @@ describe('jsonToGraphQL()', () => {
320320
}
321321
};
322322
expect(jsonToGraphQLQuery(query, { pretty: true })).to.equal(
323-
`mutation {
323+
`mutation {
324324
create_post (title: "My Awesome Post", body: "This post is awesome!")
325325
}`);
326326
});
@@ -385,24 +385,24 @@ describe('jsonToGraphQL()', () => {
385385
});
386386

387387
it('does not include fields which value is false', () => {
388-
const query = {
389-
query: {
390-
Posts: {
391-
__args: {
392-
a: false
388+
const query = {
389+
query: {
390+
Posts: {
391+
__args: {
392+
a: false
393+
},
394+
id: true,
395+
name: false
393396
},
394-
id: true,
395-
name: false
396-
},
397-
Lorem: {
398-
id: true
399-
},
400-
Ipsum: false
401-
}
402-
};
403-
expect(jsonToGraphQLQuery(query)).to.equal(
404-
'query { Posts (a: false) { id } Lorem { id } }'
405-
);
397+
Lorem: {
398+
id: true
399+
},
400+
Ipsum: false
401+
}
402+
};
403+
expect(jsonToGraphQLQuery(query)).to.equal(
404+
'query { Posts (a: false) { id } Lorem { id } }'
405+
);
406406
});
407407

408408
it('ignores a field that exists in the initial object', () => {
@@ -431,6 +431,60 @@ describe('jsonToGraphQL()', () => {
431431
}`);
432432
});
433433

434+
it('supports inline fragments', () => {
435+
const query = {
436+
query: {
437+
Posts: {
438+
__on: {
439+
__fragmentName: 'ConfigurablePost',
440+
id: true
441+
}
442+
}
443+
}
444+
};
445+
expect(jsonToGraphQLQuery(query)).to.equal(
446+
'query { Posts { ... on ConfigurablePost { id } } }'
447+
);
448+
});
449+
450+
it('supports inline fragments with subfields on same level', () => {
451+
const query = {
452+
query: {
453+
Posts: {
454+
title: true,
455+
__on: {
456+
__fragmentName: 'ConfigurablePost',
457+
id: true
458+
}
459+
}
460+
}
461+
};
462+
expect(jsonToGraphQLQuery(query)).to.equal(
463+
'query { Posts { title ... on ConfigurablePost { id } } }'
464+
);
465+
});
466+
467+
it('supports multiple inline fragments', () => {
468+
const query = {
469+
query: {
470+
Posts: {
471+
__on: [
472+
{
473+
__fragmentName: 'ConfigurablePost',
474+
id: true
475+
},
476+
{
477+
__fragmentName: 'UnconfigurablePost',
478+
name: true
479+
}]
480+
}
481+
}
482+
};
483+
expect(jsonToGraphQLQuery(query)).to.equal(
484+
'query { Posts { ... on ConfigurablePost { id } ... on UnconfigurablePost { name } } }'
485+
);
486+
});
487+
434488
it('we can ignore apollo __typename keys', () => {
435489
const query = {
436490
query: {

src/jsonToGraphQLQuery.ts

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { EnumType } from './types/EnumType';
22
import { VariableType } from './types/VariableType';
33

4-
export const configFields = ['__args', '__alias', '__aliasFor', '__variables', '__directives'];
4+
export const configFields = [
5+
'__args', '__alias', '__aliasFor', '__variables', '__directives', '__on', '__fragmentName'
6+
];
57

68
function stringify(obj_from_json: any): string {
79
if (obj_from_json instanceof EnumType) {
@@ -61,7 +63,7 @@ function buildDirectives(dirsObj: any): string {
6163
}
6264
else {
6365
throw new Error(`Unsupported type for directive: ${typeof directiveValue}. Types allowed: object, boolean.\n` +
64-
`Offending object: ${JSON.stringify(dirsObj)}`);
66+
`Offending object: ${JSON.stringify(dirsObj)}`);
6567
}
6668
}
6769

@@ -74,7 +76,7 @@ function filterNonConfigFields(fieldName: string, ignoreFields: string[]) {
7476
return configFields.indexOf(fieldName) == -1 && ignoreFields.indexOf(fieldName) == -1;
7577
}
7678

77-
function convertQuery(node: any, level: number, output: Array<[ string, number ]>, options: IJsonToGraphQLOptions) {
79+
function convertQuery(node: any, level: number, output: Array<[string, number]>, options: IJsonToGraphQLOptions) {
7880
Object.keys(node)
7981
.filter((key) => filterNonConfigFields(key, options.ignoreFields))
8082
.forEach((key) => {
@@ -86,6 +88,7 @@ function convertQuery(node: any, level: number, output: Array<[ string, number ]
8688
const subFields = fieldCount > 0;
8789
const argsExist = typeof node[key].__args === 'object';
8890
const directivesExist = typeof node[key].__directives === 'object';
91+
const inlineFragmentsExist = typeof node[key].__on === 'object';
8992

9093
let token = `${key}`;
9194

@@ -104,8 +107,8 @@ function convertQuery(node: any, level: number, output: Array<[ string, number ]
104107
const numDirectives = Object.keys(node[key].__directives).length;
105108
if (numDirectives > 1) {
106109
throw new Error(`Too many directives. The object/key ` +
107-
`'${Object.keys(node[key])[0]}' had ${numDirectives} directives, ` +
108-
`but only 1 directive per object/key is supported at this time.`);
110+
`'${Object.keys(node[key])[0]}' had ${numDirectives} directives, ` +
111+
`but only 1 directive per object/key is supported at this time.`);
109112
}
110113
dirsStr = `@${buildDirectives(node[key].__directives)}`;
111114
}
@@ -121,17 +124,28 @@ function convertQuery(node: any, level: number, output: Array<[ string, number ]
121124
token = `${node[key].__alias}: ${token}`;
122125
}
123126

124-
output.push([ token + (fieldCount > 0 ? ' {' : ''), level ]);
127+
output.push([token + (subFields || inlineFragmentsExist ? ' {' : ''), level]);
125128
convertQuery(node[key], level + 1, output, options);
126129

127-
if (subFields) {
128-
output.push([ '}', level ]);
130+
if (inlineFragmentsExist) {
131+
const inlineFragments: Array<{ __fragmentName: string }>
132+
= node[key].__on instanceof Array ? node[key].__on : [node[key].__on];
133+
inlineFragments.forEach((inlineFragment) => {
134+
const name = inlineFragment.__fragmentName;
135+
output.push([`... on ${name} {`, level + 1]);
136+
convertQuery(inlineFragment, level + 2, output, options);
137+
output.push(['}', level + 1]);
138+
});
139+
}
140+
141+
if (subFields || inlineFragmentsExist) {
142+
output.push(['}', level]);
129143
}
130144

131145
} else if (node[key]) {
132-
output.push([ `${key}`, level ]);
146+
output.push([`${key}`, level]);
133147
}
134-
});
148+
});
135149
}
136150

137151
export interface IJsonToGraphQLOptions {

0 commit comments

Comments
 (0)