@@ -6,12 +6,13 @@ import gql from 'graphql-tag';
6
6
import { Data , ActionParams , Field , Arguments , ORMModel } from './interfaces' ;
7
7
import Logger from './logger' ;
8
8
import { FetchResult } from "apollo-link" ;
9
+ import QueryBuilder from './queryBuilder' ;
10
+ import { capitalizeFirstLetter } from './utils' ;
9
11
10
12
const inflection = require ( 'inflection' ) ;
11
13
12
14
/**
13
15
* Plugin class
14
- * TODO: Refactor to smaller classes
15
16
*/
16
17
export default class VuexORMApollo {
17
18
private readonly httpLink : HttpLink ;
@@ -22,6 +23,7 @@ export default class VuexORMApollo {
22
23
private readonly models : Map < string , Model > = new Map ( ) ;
23
24
private readonly debugMode : boolean = false ;
24
25
private readonly logger : Logger ;
26
+ private readonly queryBuilder : QueryBuilder ;
25
27
26
28
/**
27
29
* Constructor
@@ -53,6 +55,8 @@ export default class VuexORMApollo {
53
55
cache : new InMemoryCache ( ) ,
54
56
connectToDevTools : true
55
57
} ) ;
58
+
59
+ this . queryBuilder = new QueryBuilder ( this . logger , this . getModel . bind ( this ) ) ;
56
60
}
57
61
58
62
/**
@@ -102,7 +106,7 @@ export default class VuexORMApollo {
102
106
if ( filter && Object . keys ( filter ) . length === 0 ) filter = undefined ;
103
107
104
108
// Send the request to the GraphQL API
105
- const query = this . buildQuery ( state . $name , filter ) ;
109
+ const query = this . queryBuilder . buildQuery ( state . $name , filter ) ;
106
110
const data = await this . apolloRequest ( query ) ;
107
111
108
112
// Insert incoming data into the store
@@ -119,17 +123,17 @@ export default class VuexORMApollo {
119
123
*/
120
124
private async persist ( { state, dispatch } : ActionParams , { id } : ActionParams ) : Promise < FetchResult > {
121
125
const model = this . getModel ( state . $name ) ;
122
- const name = `create${ VuexORMApollo . capitalizeFirstLetter ( model . singularName ) } ` ;
126
+ const name = `create${ capitalizeFirstLetter ( model . singularName ) } ` ;
123
127
124
128
125
129
const data = model . baseModel . getters ( 'find' , { id } ) ( ) ;
126
130
127
131
// Send the request to the GraphQL API
128
- const signature = VuexORMApollo . buildArguments ( { contract : { __type : 'Contract' } } , true ) ;
132
+ const signature = this . queryBuilder . buildArguments ( { contract : { __type : 'Contract' } } , true ) ;
129
133
130
134
const query = `
131
135
mutation ${ name } ${ signature } {
132
- ${ this . buildField ( model , false , { contract : { __type : 'Contract' } } , true , undefined , name ) }
136
+ ${ this . queryBuilder . buildField ( model , false , { contract : { __type : 'Contract' } } , true , undefined , name ) }
133
137
}
134
138
` ;
135
139
@@ -141,7 +145,7 @@ export default class VuexORMApollo {
141
145
const newData = await this . apolloClient . mutate ( {
142
146
"mutation" : gql ( query ) ,
143
147
"variables" : {
144
- [ model . singularName ] : this . transformOutgoingData ( data )
148
+ [ model . singularName ] : this . queryBuilder . transformOutgoingData ( data )
145
149
}
146
150
} ) ;
147
151
@@ -157,135 +161,6 @@ export default class VuexORMApollo {
157
161
158
162
}
159
163
160
-
161
- private transformOutgoingData ( data : Data ) : Data {
162
- const returnValue : Data = { } ;
163
-
164
- Object . keys ( data ) . forEach ( ( key ) => {
165
- const value = data [ key ] ;
166
-
167
- // Ignore IDs and connections
168
- if ( ! ( value instanceof Array || key === 'id' ) ) {
169
- returnValue [ key ] = value ;
170
- }
171
- } ) ;
172
-
173
- return returnValue ;
174
- }
175
-
176
-
177
- /**
178
- * Transforms a set of incoming data to the format vuex-orm requires.
179
- *
180
- * @param {Data | Array<Data> } data
181
- * @param {boolean } recursiveCall
182
- * @returns {Data }
183
- */
184
- private transformIncomingData ( data : Data | Array < Data > , recursiveCall : boolean = false ) : Data {
185
- let result : Data = { } ;
186
-
187
- if ( ! recursiveCall ) {
188
- this . logger . group ( 'Transforming incoming data' ) ;
189
- this . logger . log ( 'Raw data:' , data ) ;
190
- }
191
-
192
- if ( data instanceof Array ) {
193
- result = data . map ( d => this . transformIncomingData ( d , true ) ) ;
194
- } else {
195
- Object . keys ( data ) . forEach ( ( key ) => {
196
- if ( data [ key ] ) {
197
- if ( data [ key ] instanceof Object ) {
198
- if ( data [ key ] . nodes ) {
199
- result [ inflection . pluralize ( key ) ] = this . transformIncomingData ( data [ key ] . nodes , true ) ;
200
- } else {
201
- result [ inflection . singularize ( key ) ] = this . transformIncomingData ( data [ key ] , true ) ;
202
- }
203
- } else if ( key === 'id' ) {
204
- result [ key ] = parseInt ( data [ key ] , 0 ) ;
205
- } else {
206
- result [ key ] = data [ key ] ;
207
- }
208
- }
209
- } ) ;
210
- }
211
-
212
- if ( ! recursiveCall ) {
213
- this . logger . log ( 'Transformed data:' , result ) ;
214
- this . logger . groupEnd ( ) ;
215
- }
216
-
217
- return result ;
218
- }
219
-
220
- /**
221
- *
222
- * @param {Model } model
223
- * @param {Model } rootModel
224
- * @returns {Array<String> }
225
- */
226
- private buildRelationsQuery ( model : Model , rootModel ?: Model ) {
227
- const relationQueries : Array < string > = [ ] ;
228
-
229
- model . getRelations ( ) . forEach ( ( field : Field , name : string ) => {
230
- if ( ! rootModel || name !== rootModel . singularName && name !== rootModel . pluralName ) {
231
- const multiple : boolean = field . constructor . name !== 'BelongsTo' ;
232
- relationQueries . push ( this . buildField ( name , multiple , undefined , false , rootModel || model ) ) ;
233
- }
234
- } ) ;
235
-
236
- return relationQueries ;
237
- }
238
-
239
- /**
240
- * Builds a field for the GraphQL query and a specific model
241
- * @param {Model|string } model
242
- * @param {boolean } multiple
243
- * @param {Arguments } args
244
- * @param {boolean } withVars
245
- * @param {Model } rootModel
246
- * @param {string } name
247
- * @returns {string }
248
- */
249
- private buildField ( model : Model | string , multiple : boolean = true , args ?: Arguments , withVars : boolean = false , rootModel ?: Model , name ?: string ) : string {
250
- model = this . getModel ( model ) ;
251
-
252
- let params : string = VuexORMApollo . buildArguments ( args , false , withVars ) ;
253
-
254
- const fields = `
255
- ${ model . getQueryFields ( ) . join ( ' ' ) }
256
- ${ this . buildRelationsQuery ( model , rootModel ) }
257
- ` ;
258
-
259
- if ( multiple ) {
260
- return `
261
- ${ name ? name : model . pluralName } ${ params } {
262
- nodes {
263
- ${ fields }
264
- }
265
- }
266
- ` ;
267
- } else {
268
- return `
269
- ${ name ? name : model . singularName } ${ params } {
270
- ${ fields }
271
- }
272
- ` ;
273
- }
274
- }
275
-
276
- /**
277
- * Create a GraphQL query for the given model and arguments.
278
- *
279
- * @param {string } modelName
280
- * @param {Arguments } args
281
- * @returns {any }
282
- */
283
- private buildQuery ( modelName : string , args ?: Arguments ) : any {
284
- const multiple = ! ( args && args . get ( 'id' ) ) ;
285
- const query = `{ ${ this . buildField ( modelName , multiple , args ) } }` ;
286
- return gql ( query ) ;
287
- }
288
-
289
164
/**
290
165
* Sends a query to the GraphQL API via apollo
291
166
* @param query
@@ -295,7 +170,7 @@ export default class VuexORMApollo {
295
170
const response = await ( this . apolloClient ) . query ( { query } ) ;
296
171
297
172
// Transform incoming data into something useful
298
- return this . transformIncomingData ( response . data ) ;
173
+ return this . queryBuilder . transformIncomingData ( response . data ) ;
299
174
}
300
175
301
176
/**
@@ -324,87 +199,4 @@ export default class VuexORMApollo {
324
199
325
200
return model ;
326
201
}
327
-
328
- /**
329
- * Capitalizes the first letter of the given string.
330
- *
331
- * @param {string } string
332
- * @returns {string }
333
- */
334
- private static capitalizeFirstLetter ( string : string ) {
335
- return string . charAt ( 0 ) . toUpperCase ( ) + string . slice ( 1 ) ;
336
- }
337
-
338
-
339
- /**
340
- * Generates the arguments string for a graphql query based on a given map.
341
- *
342
- * There are three types of arguments:
343
- *
344
- * 1) Signatures with attributes (signature = true)
345
- * mutation createUser($name: String!)
346
- *
347
- * 2) Signatures with object (signature = true, args = { user: { __type: 'User' }})
348
- * mutation createUser($user: User!)
349
- *
350
- * 3) Field with values (signature = false, valuesAsVariables = false)
351
- * user(id: 15)
352
- *
353
- * 4) Field with variables (signature = false, valuesAsVariables = true)
354
- * user(id: $id)
355
- *
356
- * 5) Field with object value (signature = false, valuesAsVariables = false, args = { user: { __type: 'User' }})
357
- * createUser(user: {...})
358
- *
359
- * @param {Arguments | undefined } args
360
- * @param {boolean } signature When true, then this method generates a query signature instead of key/value pairs
361
- * @param {boolean } valuesAsVariables When true and abstract = false, then this method generates filter arguments with
362
- * variables instead of values
363
- * TODO: Query with variables too?
364
- * @returns {String }
365
- */
366
- private static buildArguments ( args : Arguments | undefined , signature : boolean = false ,
367
- valuesAsVariables : boolean = false ) : string {
368
- let returnValue : string = '' ;
369
- let any : boolean = false ;
370
-
371
- if ( args ) {
372
- Object . keys ( args ) . forEach ( ( key : string ) => {
373
- let value : any = args [ key ] ;
374
-
375
- // Ignore ids and connections
376
- if ( ! ( value instanceof Array || key === 'id' ) ) {
377
- any = true ;
378
- let typeOrValue : any = '' ;
379
-
380
- if ( signature ) {
381
- if ( typeof value === 'object' && value . __type ) {
382
- // Case 2 (User!)
383
- typeOrValue = value . __type + 'Input!' ;
384
- } else {
385
- // Case 1 (String!)
386
- typeOrValue = typeof value === 'number' ? 'Number!' : 'String!' ;
387
- }
388
- } else if ( valuesAsVariables ) {
389
- // Case 6 (user: $user)
390
- typeOrValue = `$${ key } ` ;
391
- } else {
392
- if ( typeof value === 'object' && value . __type ) {
393
- // Case 3 ({name: 'Helga Hufflepuff"})
394
- typeOrValue = value ;
395
- } else {
396
- // Case 3 ("someValue")
397
- typeOrValue = typeof value === 'number' ? value : `"${ value } "` ;
398
- }
399
- }
400
-
401
- returnValue = `${ returnValue } ${ ( signature ? '$' : '' ) + key } : ${ typeOrValue } ` ;
402
- }
403
- } ) ;
404
-
405
- if ( any ) returnValue = `(${ returnValue } )` ;
406
- }
407
-
408
- return returnValue ;
409
- }
410
202
}
0 commit comments