1 | import {
|
2 | DocumentNode,
|
3 | OperationDefinitionNode,
|
4 | FragmentDefinitionNode,
|
5 | ValueNode,
|
6 | } from 'graphql';
|
7 |
|
8 | import { invariant, InvariantError } from 'ts-invariant';
|
9 |
|
10 | import { assign } from './util/assign';
|
11 |
|
12 | import { valueToObjectRepresentation, JsonValue } from './storeUtils';
|
13 |
|
14 | export function getMutationDefinition(
|
15 | doc: DocumentNode,
|
16 | ): OperationDefinitionNode {
|
17 | checkDocument(doc);
|
18 |
|
19 | let mutationDef: OperationDefinitionNode | null = doc.definitions.filter(
|
20 | definition =>
|
21 | definition.kind === 'OperationDefinition' &&
|
22 | definition.operation === 'mutation',
|
23 | )[0] as OperationDefinitionNode;
|
24 |
|
25 | invariant(mutationDef, 'Must contain a mutation definition.');
|
26 |
|
27 | return mutationDef;
|
28 | }
|
29 |
|
30 |
|
31 | export function checkDocument(doc: DocumentNode) {
|
32 | invariant(
|
33 | doc && doc.kind === 'Document',
|
34 | `Expecting a parsed GraphQL document. Perhaps you need to wrap the query \
|
35 | string in a "gql" tag? http://docs.apollostack.com/apollo-client/core.html#gql`,
|
36 | );
|
37 |
|
38 | const operations = doc.definitions
|
39 | .filter(d => d.kind !== 'FragmentDefinition')
|
40 | .map(definition => {
|
41 | if (definition.kind !== 'OperationDefinition') {
|
42 | throw new InvariantError(
|
43 | `Schema type definitions not allowed in queries. Found: "${
|
44 | definition.kind
|
45 | }"`,
|
46 | );
|
47 | }
|
48 | return definition;
|
49 | });
|
50 |
|
51 | invariant(
|
52 | operations.length <= 1,
|
53 | `Ambiguous GraphQL document: contains ${operations.length} operations`,
|
54 | );
|
55 |
|
56 | return doc;
|
57 | }
|
58 |
|
59 | export function getOperationDefinition(
|
60 | doc: DocumentNode,
|
61 | ): OperationDefinitionNode | undefined {
|
62 | checkDocument(doc);
|
63 | return doc.definitions.filter(
|
64 | definition => definition.kind === 'OperationDefinition',
|
65 | )[0] as OperationDefinitionNode;
|
66 | }
|
67 |
|
68 | export function getOperationDefinitionOrDie(
|
69 | document: DocumentNode,
|
70 | ): OperationDefinitionNode {
|
71 | const def = getOperationDefinition(document);
|
72 | invariant(def, `GraphQL document is missing an operation`);
|
73 | return def;
|
74 | }
|
75 |
|
76 | export function getOperationName(doc: DocumentNode): string | null {
|
77 | return (
|
78 | doc.definitions
|
79 | .filter(
|
80 | definition =>
|
81 | definition.kind === 'OperationDefinition' && definition.name,
|
82 | )
|
83 | .map((x: OperationDefinitionNode) => x.name.value)[0] || null
|
84 | );
|
85 | }
|
86 |
|
87 |
|
88 | export function getFragmentDefinitions(
|
89 | doc: DocumentNode,
|
90 | ): FragmentDefinitionNode[] {
|
91 | return doc.definitions.filter(
|
92 | definition => definition.kind === 'FragmentDefinition',
|
93 | ) as FragmentDefinitionNode[];
|
94 | }
|
95 |
|
96 | export function getQueryDefinition(doc: DocumentNode): OperationDefinitionNode {
|
97 | const queryDef = getOperationDefinition(doc) as OperationDefinitionNode;
|
98 |
|
99 | invariant(
|
100 | queryDef && queryDef.operation === 'query',
|
101 | 'Must contain a query definition.',
|
102 | );
|
103 |
|
104 | return queryDef;
|
105 | }
|
106 |
|
107 | export function getFragmentDefinition(
|
108 | doc: DocumentNode,
|
109 | ): FragmentDefinitionNode {
|
110 | invariant(
|
111 | doc.kind === 'Document',
|
112 | `Expecting a parsed GraphQL document. Perhaps you need to wrap the query \
|
113 | string in a "gql" tag? http://docs.apollostack.com/apollo-client/core.html#gql`,
|
114 | );
|
115 |
|
116 | invariant(
|
117 | doc.definitions.length <= 1,
|
118 | 'Fragment must have exactly one definition.',
|
119 | );
|
120 |
|
121 | const fragmentDef = doc.definitions[0] as FragmentDefinitionNode;
|
122 |
|
123 | invariant(
|
124 | fragmentDef.kind === 'FragmentDefinition',
|
125 | 'Must be a fragment definition.',
|
126 | );
|
127 |
|
128 | return fragmentDef as FragmentDefinitionNode;
|
129 | }
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 | export function getMainDefinition(
|
137 | queryDoc: DocumentNode,
|
138 | ): OperationDefinitionNode | FragmentDefinitionNode {
|
139 | checkDocument(queryDoc);
|
140 |
|
141 | let fragmentDefinition;
|
142 |
|
143 | for (let definition of queryDoc.definitions) {
|
144 | if (definition.kind === 'OperationDefinition') {
|
145 | const operation = (definition as OperationDefinitionNode).operation;
|
146 | if (
|
147 | operation === 'query' ||
|
148 | operation === 'mutation' ||
|
149 | operation === 'subscription'
|
150 | ) {
|
151 | return definition as OperationDefinitionNode;
|
152 | }
|
153 | }
|
154 | if (definition.kind === 'FragmentDefinition' && !fragmentDefinition) {
|
155 |
|
156 |
|
157 | fragmentDefinition = definition as FragmentDefinitionNode;
|
158 | }
|
159 | }
|
160 |
|
161 | if (fragmentDefinition) {
|
162 | return fragmentDefinition;
|
163 | }
|
164 |
|
165 | throw new InvariantError(
|
166 | 'Expected a parsed GraphQL query with a query, mutation, subscription, or a fragment.',
|
167 | );
|
168 | }
|
169 |
|
170 |
|
171 |
|
172 |
|
173 | export interface FragmentMap {
|
174 | [fragmentName: string]: FragmentDefinitionNode;
|
175 | }
|
176 |
|
177 |
|
178 |
|
179 | export function createFragmentMap(
|
180 | fragments: FragmentDefinitionNode[] = [],
|
181 | ): FragmentMap {
|
182 | const symTable: FragmentMap = {};
|
183 | fragments.forEach(fragment => {
|
184 | symTable[fragment.name.value] = fragment;
|
185 | });
|
186 |
|
187 | return symTable;
|
188 | }
|
189 |
|
190 | export function getDefaultValues(
|
191 | definition: OperationDefinitionNode | undefined,
|
192 | ): { [key: string]: JsonValue } {
|
193 | if (
|
194 | definition &&
|
195 | definition.variableDefinitions &&
|
196 | definition.variableDefinitions.length
|
197 | ) {
|
198 | const defaultValues = definition.variableDefinitions
|
199 | .filter(({ defaultValue }) => defaultValue)
|
200 | .map(
|
201 | ({ variable, defaultValue }): { [key: string]: JsonValue } => {
|
202 | const defaultValueObj: { [key: string]: JsonValue } = {};
|
203 | valueToObjectRepresentation(
|
204 | defaultValueObj,
|
205 | variable.name,
|
206 | defaultValue as ValueNode,
|
207 | );
|
208 |
|
209 | return defaultValueObj;
|
210 | },
|
211 | );
|
212 |
|
213 | return assign({}, ...defaultValues);
|
214 | }
|
215 |
|
216 | return {};
|
217 | }
|
218 |
|
219 |
|
220 |
|
221 |
|
222 | export function variablesInOperation(
|
223 | operation: OperationDefinitionNode,
|
224 | ): Set<string> {
|
225 | const names = new Set<string>();
|
226 | if (operation.variableDefinitions) {
|
227 | for (const definition of operation.variableDefinitions) {
|
228 | names.add(definition.variable.name.value);
|
229 | }
|
230 | }
|
231 |
|
232 | return names;
|
233 | }
|