Add field aliases to structured GraphQL queries (#116)

Summary:
For pagination, we’ll want to query against multiple entities of the
same type. GraphQL uses aliases to facilitate this. This commit adds
support for aliases to our GraphQL query DSL.

Test Plan:
Inspect snapshot changes, and note that `yarn flow` and `yarn test`
pass.

wchargin-branch: graphql-aliases
This commit is contained in:
William Chargin 2018-03-26 20:54:16 -07:00 committed by GitHub
parent 2f50aa7364
commit e6f401df30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 95 additions and 6 deletions

View File

@ -200,6 +200,7 @@ Array [
], ],
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object { "args": Object {
"id": Object { "id": Object {
"data": 12345, "data": 12345,
@ -213,6 +214,7 @@ Array [
"name": "thing", "name": "thing",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object { "args": Object {
"tasty": Object { "tasty": Object {
"data": true, "data": true,
@ -235,16 +237,19 @@ Array [
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "more", "name": "more",
"selections": Array [ "selections": Array [
Object { Object {
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "mcguffins", "name": "mcguffins",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "quantity", "name": "quantity",
"selections": Array [], "selections": Array [],
@ -254,6 +259,7 @@ Array [
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": "goo",
"args": Object { "args": Object {
"state": Object { "state": Object {
"data": "SLIMY", "data": "SLIMY",
@ -263,6 +269,7 @@ Array [
"name": "slime", "name": "slime",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "availability", "name": "availability",
"selections": Array [], "selections": Array [],
@ -278,6 +285,7 @@ Array [
Object { Object {
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object { "args": Object {
"attributes": Object { "attributes": Object {
"data": Object { "data": Object {
@ -325,10 +333,12 @@ Array [
"params": Array [], "params": Array [],
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "rateLimit", "name": "rateLimit",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "remaining", "name": "remaining",
"selections": Array [], "selections": Array [],
@ -344,12 +354,14 @@ Array [
"name": "otherThings", "name": "otherThings",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "__typename", "name": "__typename",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "quality", "name": "quality",
"selections": Array [], "selections": Array [],
@ -362,7 +374,7 @@ Array [
] ]
`; `;
exports[`queries end-to-end-test cases for a query using lots of features should stringify as inline 1`] = `"query QueryWithParameters($qp1: String! $qp2: String!) { thing(id: 12345 name: $qp1) { fruit(type: APPLE tasty: true) ...otherThings } more { ... on Widget { mcguffins { quantity } slime(state: SLIMY) { availability } } ... on Gizmo { cogs(attributes: { teeth: [ 12 14 16 ] shaft: null }) } } } query QueryWithoutParameters { rateLimit { remaining } } fragment otherThings on Thing { __typename quality }"`; exports[`queries end-to-end-test cases for a query using lots of features should stringify as inline 1`] = `"query QueryWithParameters($qp1: String! $qp2: String!) { thing(id: 12345 name: $qp1) { fruit(type: APPLE tasty: true) ...otherThings } more { ... on Widget { mcguffins { quantity } goo: slime(state: SLIMY) { availability } } ... on Gizmo { cogs(attributes: { teeth: [ 12 14 16 ] shaft: null }) } } } query QueryWithoutParameters { rateLimit { remaining } } fragment otherThings on Thing { __typename quality }"`;
exports[`queries end-to-end-test cases for a query using lots of features should stringify as multiline 1`] = ` exports[`queries end-to-end-test cases for a query using lots of features should stringify as multiline 1`] = `
"query QueryWithParameters($qp1: String! $qp2: String!) { "query QueryWithParameters($qp1: String! $qp2: String!) {
@ -375,7 +387,7 @@ exports[`queries end-to-end-test cases for a query using lots of features should
mcguffins { mcguffins {
quantity quantity
} }
slime(state: SLIMY) { goo: slime(state: SLIMY) {
availability availability
} }
} }
@ -411,6 +423,7 @@ Array [
], ],
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object { "args": Object {
"name": Object { "name": Object {
"data": "repoName", "data": "repoName",
@ -424,6 +437,7 @@ Array [
"name": "repository", "name": "repository",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object { "args": Object {
"first": Object { "first": Object {
"data": 100, "data": 100,
@ -433,10 +447,12 @@ Array [
"name": "issues", "name": "issues",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "pageInfo", "name": "pageInfo",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "hasNextPage", "name": "hasNextPage",
"selections": Array [], "selections": Array [],
@ -446,34 +462,40 @@ Array [
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "nodes", "name": "nodes",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "id", "name": "id",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "title", "name": "title",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "body", "name": "body",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "number", "name": "number",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "author", "name": "author",
"selections": Array [ "selections": Array [
@ -485,6 +507,7 @@ Array [
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object { "args": Object {
"first": Object { "first": Object {
"data": 20, "data": 20,
@ -494,10 +517,12 @@ Array [
"name": "comments", "name": "comments",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "pageInfo", "name": "pageInfo",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "hasNextPage", "name": "hasNextPage",
"selections": Array [], "selections": Array [],
@ -507,16 +532,19 @@ Array [
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "nodes", "name": "nodes",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "id", "name": "id",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "author", "name": "author",
"selections": Array [ "selections": Array [
@ -528,12 +556,14 @@ Array [
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "body", "name": "body",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "url", "name": "url",
"selections": Array [], "selections": Array [],
@ -552,6 +582,7 @@ Array [
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object { "args": Object {
"first": Object { "first": Object {
"data": 100, "data": 100,
@ -561,10 +592,12 @@ Array [
"name": "pullRequests", "name": "pullRequests",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "pageInfo", "name": "pageInfo",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "hasNextPage", "name": "hasNextPage",
"selections": Array [], "selections": Array [],
@ -574,34 +607,40 @@ Array [
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "nodes", "name": "nodes",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "id", "name": "id",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "title", "name": "title",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "body", "name": "body",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "number", "name": "number",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "author", "name": "author",
"selections": Array [ "selections": Array [
@ -613,6 +652,7 @@ Array [
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object { "args": Object {
"first": Object { "first": Object {
"data": 20, "data": 20,
@ -622,10 +662,12 @@ Array [
"name": "comments", "name": "comments",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "pageInfo", "name": "pageInfo",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "hasNextPage", "name": "hasNextPage",
"selections": Array [], "selections": Array [],
@ -635,16 +677,19 @@ Array [
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "nodes", "name": "nodes",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "id", "name": "id",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "author", "name": "author",
"selections": Array [ "selections": Array [
@ -656,12 +701,14 @@ Array [
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "body", "name": "body",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "url", "name": "url",
"selections": Array [], "selections": Array [],
@ -674,6 +721,7 @@ Array [
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object { "args": Object {
"first": Object { "first": Object {
"data": 10, "data": 10,
@ -683,10 +731,12 @@ Array [
"name": "reviews", "name": "reviews",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "pageInfo", "name": "pageInfo",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "hasNextPage", "name": "hasNextPage",
"selections": Array [], "selections": Array [],
@ -696,22 +746,26 @@ Array [
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "nodes", "name": "nodes",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "id", "name": "id",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "body", "name": "body",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "author", "name": "author",
"selections": Array [ "selections": Array [
@ -723,12 +777,14 @@ Array [
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "state", "name": "state",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object { "args": Object {
"first": Object { "first": Object {
"data": 10, "data": 10,
@ -738,10 +794,12 @@ Array [
"name": "comments", "name": "comments",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "pageInfo", "name": "pageInfo",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "hasNextPage", "name": "hasNextPage",
"selections": Array [], "selections": Array [],
@ -751,22 +809,26 @@ Array [
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "nodes", "name": "nodes",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "id", "name": "id",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "body", "name": "body",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "author", "name": "author",
"selections": Array [ "selections": Array [
@ -805,12 +867,14 @@ Array [
"name": "whoami", "name": "whoami",
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "__typename", "name": "__typename",
"selections": Array [], "selections": Array [],
"type": "FIELD", "type": "FIELD",
}, },
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "login", "name": "login",
"selections": Array [], "selections": Array [],
@ -819,6 +883,7 @@ Array [
Object { Object {
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "id", "name": "id",
"selections": Array [], "selections": Array [],
@ -831,6 +896,7 @@ Array [
Object { Object {
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "id", "name": "id",
"selections": Array [], "selections": Array [],
@ -843,6 +909,7 @@ Array [
Object { Object {
"selections": Array [ "selections": Array [
Object { Object {
"alias": null,
"args": Object {}, "args": Object {},
"name": "id", "name": "id",
"selections": Array [], "selections": Array [],

View File

@ -36,6 +36,7 @@ export type FragmentDefinition = {|
export type Selection = Field | FragmentSpread | InlineFragment; export type Selection = Field | FragmentSpread | InlineFragment;
export type Field = {| export type Field = {|
+type: "FIELD", +type: "FIELD",
+alias: ?string,
+name: string, +name: string,
+args: Arguments, +args: Arguments,
+selections: Selection[], +selections: Selection[],
@ -100,12 +101,23 @@ export const build = {
field(name: string, args: ?Arguments, selections: ?(Selection[])): Field { field(name: string, args: ?Arguments, selections: ?(Selection[])): Field {
return { return {
type: "FIELD", type: "FIELD",
alias: null,
name, name,
args: args || {}, args: args || {},
selections: selections || [], selections: selections || [],
}; };
}, },
alias(newAlias: string, field: Field): Field {
return {
type: "FIELD",
alias: newAlias,
name: field.name,
args: field.args,
selections: field.selections,
};
},
fragmentSpread(fragmentName: string): FragmentSpread { fragmentSpread(fragmentName: string): FragmentSpread {
return { return {
type: "FRAGMENT_SPREAD", type: "FRAGMENT_SPREAD",
@ -308,6 +320,13 @@ export const stringify = {
}, },
field(field: Field, ls: LayoutStrategy): string { field(field: Field, ls: LayoutStrategy): string {
const aliasPart = (() => {
if (field.alias == null) {
return "";
} else {
return `${field.alias}: `;
}
})();
const argsPart = (() => { const argsPart = (() => {
if (Object.keys(field.args).length === 0) { if (Object.keys(field.args).length === 0) {
return ""; return "";
@ -325,7 +344,7 @@ export const stringify = {
ls.next() ls.next()
); );
return ls.join([ return ls.join([
ls.atom(`${field.name}${argsPart} {`), ls.atom(`${aliasPart}${field.name}${argsPart} {`),
selectionsPart, selectionsPart,
ls.atom("}"), ls.atom("}"),
]); ]);

View File

@ -88,9 +88,12 @@ function featurefulQuery(): Body {
b.field("more", {}, [ b.field("more", {}, [
b.inlineFragment("Widget", [ b.inlineFragment("Widget", [
b.field("mcguffins", {}, [b.field("quantity")]), b.field("mcguffins", {}, [b.field("quantity")]),
b.field("slime", {state: b.enumLiteral("SLIMY")}, [ b.alias(
b.field("availability"), "goo",
]), b.field("slime", {state: b.enumLiteral("SLIMY")}, [
b.field("availability"),
])
),
]), ]),
b.inlineFragment("Gizmo", [ b.inlineFragment("Gizmo", [
b.field("cogs", { b.field("cogs", {