1
- import { print } from 'graphql/language/printer' ;
2
- import { parse } from 'graphql/language/parser' ;
3
- import { Arguments , Data , Field } from './interfaces' ;
4
- import Model from './model' ;
5
- import gql from 'graphql-tag' ;
6
- import Logger from './logger' ;
7
- import { downcaseFirstLetter , upcaseFirstLetter } from './utils' ;
1
+ import { parse } from "graphql/language/parser" ;
2
+ import Logger from "./logger" ;
3
+ import Model from "./model" ;
4
+ import { print } from "graphql/language/printer" ;
5
+ import { Arguments , Data , Field } from "./interfaces" ;
6
+ import { downcaseFirstLetter , upcaseFirstLetter } from "./utils" ;
7
+ import gql from "graphql-tag" ;
8
+
8
9
const inflection = require ( 'inflection' ) ;
9
10
10
- /**
11
- * This class takes care of everything GraphQL query related, especially the generation of queries out of models
12
- */
13
11
export default class QueryBuilder {
14
12
private readonly logger : Logger ;
15
13
private readonly getModel : ( name : Model | string ) => Model ;
@@ -33,6 +31,7 @@ export default class QueryBuilder {
33
31
return print ( parse ( query ) ) ;
34
32
}
35
33
34
+
36
35
/**
37
36
* Generates the arguments string for a graphql query based on a given map.
38
37
*
@@ -44,25 +43,17 @@ export default class QueryBuilder {
44
43
* 2) Signatures with object types (signature = true, args = { user: { __type: 'User' }})
45
44
* mutation createUser($user: UserInput!)
46
45
*
47
- * 3) Fields with values (signature = false, valuesAsVariables = false)
48
- * query user(id: 15)
49
- *
50
- * 4) Fields with variables (signature = false, valuesAsVariables = true)
46
+ * 3) Fields with variables (signature = false, valuesAsVariables = true)
51
47
* query user(id: $id)
52
48
*
53
- * 5) Fields with object value (signature = false, valuesAsVariables = false, args = { user: { __type: 'User' }})
54
- * mutation createUser(user: {...})
55
- *
56
49
* @param {Arguments | undefined } args
57
50
* @param {boolean } signature When true, then this method generates a query signature instead of key/value pairs
58
- * @param {boolean } valuesAsVariables When true and abstract = false, then this method generates filter arguments with
59
- * variables instead of values
51
+ * @param {boolean } allowIdFields If true, ID fields will be included in the arguments list
60
52
* @returns {String }
61
53
*/
62
- public buildArguments ( args : Arguments | undefined ,
63
- signature : boolean = false ,
64
- valuesAsVariables : boolean = false ,
65
- allowIdFields : boolean = false ) : string {
54
+ private buildArguments ( args ?: Arguments , signature : boolean = false , allowIdFields : boolean = true ) : string {
55
+ if ( args === null ) return '' ;
56
+
66
57
let returnValue : string = '' ;
67
58
let first : boolean = true ;
68
59
@@ -85,17 +76,9 @@ export default class QueryBuilder {
85
76
// Case 1 (String!)
86
77
typeOrValue = typeof value === 'number' ? 'Number!' : 'String!' ;
87
78
}
88
- } else if ( valuesAsVariables ) {
89
- // Case 6 (user: $user)
90
- typeOrValue = `$${ key } ` ;
91
79
} else {
92
- if ( typeof value === 'object' && value . __type ) {
93
- // Case 3 ({name: 'Helga Hufflepuff"})
94
- typeOrValue = JSON . stringify ( value ) ;
95
- } else {
96
- // Case 3 ("someValue")
97
- typeOrValue = typeof value === 'number' ? value : `"${ value } "` ;
98
- }
80
+ // Case 3 (user: $user)
81
+ typeOrValue = `$${ key } ` ;
99
82
}
100
83
101
84
returnValue = `${ returnValue } ${ first ? '' : ', ' } ${ ( signature ? '$' : '' ) + key } : ${ typeOrValue } ` ;
@@ -109,6 +92,96 @@ export default class QueryBuilder {
109
92
return returnValue ;
110
93
}
111
94
95
+
96
+
97
+ /**
98
+ * Builds a field for the GraphQL query and a specific model
99
+ *
100
+ * @param {Model|string } model
101
+ * @param {boolean } multiple
102
+ * @param {Arguments } args
103
+ * @param {Model } rootModel
104
+ * @param {string } name
105
+ * @param allowIdFields
106
+ * @returns {string }
107
+ */
108
+ public buildField ( model : Model | string ,
109
+ multiple : boolean = true ,
110
+ args ?: Arguments ,
111
+ rootModel ?: Model ,
112
+ name ?: string ,
113
+ allowIdFields : boolean = false ) : string {
114
+ model = this . getModel ( model ) ;
115
+
116
+ let params : string = this . buildArguments ( args , false , allowIdFields ) ;
117
+
118
+ const fields = `
119
+ ${ model . getQueryFields ( ) . join ( ' ' ) }
120
+ ${ this . buildRelationsQuery ( model , rootModel ) }
121
+ ` ;
122
+
123
+ if ( multiple ) {
124
+ return `
125
+ ${ name ? name : model . pluralName } ${ params } {
126
+ nodes {
127
+ ${ fields }
128
+ }
129
+ }
130
+ ` ;
131
+ } else {
132
+ return `
133
+ ${ name ? name : model . singularName } ${ params } {
134
+ ${ fields }
135
+ }
136
+ ` ;
137
+ }
138
+ }
139
+
140
+
141
+ /**
142
+ *
143
+ * @param {Model } model
144
+ * @param {Model } rootModel
145
+ * @returns {Array<String> }
146
+ */
147
+ private buildRelationsQuery ( model : ( null | Model ) , rootModel ?: Model ) {
148
+ if ( model === null ) return '' ;
149
+
150
+ const relationQueries : Array < string > = [ ] ;
151
+
152
+ model . getRelations ( ) . forEach ( ( field : Field , name : string ) => {
153
+ if ( ! rootModel || ( name !== rootModel . singularName && name !== rootModel . pluralName ) ) {
154
+ const multiple : boolean = field . constructor . name !== 'BelongsTo' ;
155
+ relationQueries . push ( this . buildField ( name , multiple , undefined , rootModel || model ) ) ;
156
+ }
157
+ } ) ;
158
+
159
+ return relationQueries ;
160
+ }
161
+
162
+
163
+
164
+ public buildQuery ( type : string , name ?: string , args ?: Arguments , model ?: ( Model | null | string ) , fields ?: string , addModelToArgs :boolean = false , multiple ?: boolean ) {
165
+ model = model ? this . getModel ( model ) : null ;
166
+
167
+ if ( ! args ) args = { } ;
168
+ if ( addModelToArgs && model ) args [ model . singularName ] = { __type : upcaseFirstLetter ( model . singularName ) } ;
169
+
170
+ multiple = multiple === undefined ? ! args [ 'id' ] : multiple ;
171
+
172
+ if ( ! name && model ) name = ( multiple ? model . pluralName : model . singularName ) ;
173
+ if ( ! name ) throw new Error ( "Can't determine name for the query! Please provide either name or model" ) ;
174
+
175
+
176
+ const query :string =
177
+ `${ type } ${ upcaseFirstLetter ( name ) } ${ this . buildArguments ( args , true ) } {\n` +
178
+ ` ${ model ? this . buildField ( model , multiple , args , model , name , true ) : fields } \n` +
179
+ `}` ;
180
+
181
+ return gql ( query ) ;
182
+ }
183
+
184
+
112
185
/**
113
186
* Transforms outgoing data. Use for variables param.
114
187
*
@@ -183,114 +256,4 @@ export default class QueryBuilder {
183
256
184
257
return result ;
185
258
}
186
-
187
- /**
188
- *
189
- * @param {Model } model
190
- * @param {Model } rootModel
191
- * @returns {Array<String> }
192
- */
193
- public buildRelationsQuery ( model : Model , rootModel ?: Model ) {
194
- const relationQueries : Array < string > = [ ] ;
195
-
196
- model . getRelations ( ) . forEach ( ( field : Field , name : string ) => {
197
- if ( ! rootModel || ( name !== rootModel . singularName && name !== rootModel . pluralName ) ) {
198
- const multiple : boolean = field . constructor . name !== 'BelongsTo' ;
199
- relationQueries . push ( this . buildField ( name , multiple , undefined , false , rootModel || model ) ) ;
200
- }
201
- } ) ;
202
-
203
- return relationQueries ;
204
- }
205
-
206
- /**
207
- * Builds a field for the GraphQL query and a specific model
208
- * @param {Model|string } model
209
- * @param {boolean } multiple
210
- * @param {Arguments } args
211
- * @param {boolean } withVars
212
- * @param {Model } rootModel
213
- * @param {string } name
214
- * @returns {string }
215
- */
216
- public buildField ( model : Model | string ,
217
- multiple : boolean = true ,
218
- args ?: Arguments ,
219
- withVars : boolean = false ,
220
- rootModel ?: Model ,
221
- name ?: string ,
222
- allowIdFields : boolean = false ) : string {
223
- model = this . getModel ( model ) ;
224
-
225
- let params : string = this . buildArguments ( args , false , withVars , allowIdFields ) ;
226
-
227
- const fields = `
228
- ${ model . getQueryFields ( ) . join ( ' ' ) }
229
- ${ this . buildRelationsQuery ( model , rootModel ) }
230
- ` ;
231
-
232
- if ( multiple ) {
233
- return `
234
- ${ name ? name : model . pluralName } ${ params } {
235
- nodes {
236
- ${ fields }
237
- }
238
- }
239
- ` ;
240
- } else {
241
- return `
242
- ${ name ? name : model . singularName } ${ params } {
243
- ${ fields }
244
- }
245
- ` ;
246
- }
247
- }
248
-
249
- /**
250
- * Create a GraphQL query for the given model and arguments.
251
- *
252
- * @param {string } modelName
253
- * @param {Arguments } args
254
- * @returns {any }
255
- */
256
- public buildQuery ( modelName : string , args ?: Arguments ) : any {
257
- // Ignore empty args
258
- if ( args && Object . keys ( args ) . length === 0 ) args = undefined ;
259
-
260
- const multiple = ! ( args && args . get ( 'id' ) ) ;
261
- const query = `{ ${ this . buildField ( modelName , multiple , args ) } }` ;
262
- return gql ( query ) ;
263
- }
264
-
265
- /**
266
- * Generates a mutation query for a model.
267
- *
268
- * @param {Model } model
269
- * @param {string }prefix
270
- * @returns {any }
271
- *
272
- * TODO: Refactor to avoid prefix param
273
- */
274
- public buildMutation ( model : Model , id ?: number , prefix : string = 'create' ) {
275
- const name : string = `${ prefix } ${ upcaseFirstLetter ( model . singularName ) } ` ;
276
- let args : Data = { [ model . singularName ] : { __type : upcaseFirstLetter ( model . singularName ) } } ;
277
-
278
- if ( prefix === 'delete' ) {
279
- if ( ! id ) throw new Error ( 'No ID given.' ) ;
280
- args = { id } ;
281
- } else if ( prefix === 'update' ) {
282
- args [ 'id' ] = id ;
283
- }
284
-
285
- const signature : string = this . buildArguments ( args , true , false , true ) ;
286
- const field = this . buildField ( model , false , args , true , model , name , true ) ;
287
-
288
- const query = `
289
- mutation ${ name } ${ signature } {
290
- ${ field }
291
- }
292
- ` ;
293
-
294
- return gql ( query ) ;
295
- }
296
259
}
0 commit comments