graphql_model_mapper 0.0.6 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +273 -272
- data/lib/graphql_model_mapper.rb +22 -10
- data/lib/graphql_model_mapper/custom_type.rb +4 -0
- data/lib/graphql_model_mapper/mapper_type.rb +195 -187
- data/lib/graphql_model_mapper/mutation.rb +78 -35
- data/lib/graphql_model_mapper/query.rb +126 -22
- data/lib/graphql_model_mapper/resolve.rb +256 -210
- data/lib/graphql_model_mapper/schema.rb +98 -20
- data/lib/graphql_model_mapper/utility.rb +20 -2
- data/lib/graphql_model_mapper/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: edf76e62fad8e31f3b2e1572544f170a5c151051
|
4
|
+
data.tar.gz: dee948ab9a1125e55e19b588d5ef4837dd576ea4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c930baf5c2c607361e43c43d377321a9f06278c927b3b8a486a2e3c8eb99c9c94dff808c00df050573979c2ac9aef7c07ac7549ab0496b32262ac4b3ada1052
|
7
|
+
data.tar.gz: 8cd16e8b313baab6b7608a6e39b6f8dbb2edd04b3f5bf9c9ec90f6bb2890756935b66e2d8f5ef3e5c338262d5de38bf072ee47324a79993650778527134ff109
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -30,265 +30,87 @@ Initially, you will not have any models exposed as GraphQL types. To expose a mo
|
|
30
30
|
graphql_delete # to generate a GraphQL delete mutation object (and its associated GraphQL input/output types) for the model
|
31
31
|
graphql_update # to generate a GraphQL update mutation object (and its associated GraphQL input/output types) for the model
|
32
32
|
|
33
|
+
|
34
|
+
## Type options
|
33
35
|
The default input/output types generated for the model are based on the default settings (which may be overriden by initializing GraphqlModelMapper::GRAPHQL_DEFAULT_TYPES in your own initializer
|
34
36
|
|
35
37
|
#config/initializers/grapqhql_model_mapper_init.rb
|
36
|
-
GraphqlModelMapper::GRAPHQL_DEFAULT_TYPES = {
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
type_sub_key: :input_type
|
49
|
-
},
|
50
|
-
output_type: {
|
51
|
-
required_attributes: [],
|
52
|
-
excluded_attributes: [],
|
53
|
-
allowed_attributes: [],
|
54
|
-
foreign_keys: true,
|
55
|
-
primary_keys: true,
|
56
|
-
validation_keys: false,
|
57
|
-
association_macro: nil,
|
58
|
-
source_nulls: false,
|
59
|
-
type_key: :query,
|
60
|
-
type_sub_key: :output_type
|
61
|
-
}
|
62
|
-
},
|
63
|
-
update: {
|
64
|
-
input_type: {
|
65
|
-
required_attributes: [],
|
66
|
-
excluded_attributes: [],
|
67
|
-
allowed_attributes: [],
|
68
|
-
foreign_keys: true,
|
69
|
-
primary_keys: true,
|
70
|
-
validation_keys: false,
|
71
|
-
association_macro: nil,
|
72
|
-
source_nulls: false,
|
73
|
-
type_key: :update,
|
74
|
-
type_sub_key: :input_type
|
75
|
-
},
|
76
|
-
output_type: {
|
77
|
-
required_attributes: [],
|
78
|
-
excluded_attributes: [],
|
79
|
-
allowed_attributes: [],
|
80
|
-
foreign_keys: true,
|
81
|
-
primary_keys: true,
|
82
|
-
validation_keys: false,
|
83
|
-
association_macro: nil,
|
84
|
-
source_nulls: true,
|
85
|
-
type_key: :update,
|
86
|
-
type_sub_key: :output_type
|
87
|
-
}
|
88
|
-
},
|
89
|
-
delete: {
|
90
|
-
input_type: {
|
91
|
-
required_attributes: [:id],
|
92
|
-
excluded_attributes: [],
|
93
|
-
allowed_attributes: [:id],
|
94
|
-
foreign_keys: false,
|
95
|
-
primary_keys: true,
|
96
|
-
validation_keys: false,
|
97
|
-
association_macro: nil,
|
98
|
-
source_nulls: false,
|
99
|
-
type_key: :delete,
|
100
|
-
type_sub_key: :input_type
|
101
|
-
},
|
102
|
-
output_type: {
|
103
|
-
required_attributes: [],
|
104
|
-
excluded_attributes: [],
|
105
|
-
allowed_attributes: [],
|
106
|
-
foreign_keys: false,
|
107
|
-
primary_keys: true,
|
108
|
-
validation_keys: false,
|
109
|
-
association_macro: nil,
|
110
|
-
source_nulls: true,
|
111
|
-
type_key: :delete,
|
112
|
-
type_sub_key: :output_type
|
113
|
-
}
|
38
|
+
GraphqlModelMapper::CustomType::GRAPHQL_DEFAULT_TYPES = {
|
39
|
+
input_type: {
|
40
|
+
required_attributes: [],
|
41
|
+
excluded_attributes: [],
|
42
|
+
allowed_attributes: [],
|
43
|
+
foreign_keys: true,
|
44
|
+
primary_keys: true,
|
45
|
+
validation_keys: false,
|
46
|
+
association_macro: nil,
|
47
|
+
source_nulls: false,
|
48
|
+
type_key: :query,
|
49
|
+
type_sub_key: :input_type
|
114
50
|
},
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
type_sub_key: :input_type
|
127
|
-
},
|
128
|
-
output_type: {
|
129
|
-
required_attributes: [],
|
130
|
-
excluded_attributes: [],
|
131
|
-
allowed_attributes: [],
|
132
|
-
foreign_keys: true,
|
133
|
-
primary_keys: true,
|
134
|
-
validation_keys: false,
|
135
|
-
association_macro: nil,
|
136
|
-
source_nulls: true,
|
137
|
-
type_key: :create,
|
138
|
-
type_sub_key: :output_type
|
139
|
-
}
|
51
|
+
output_type: {
|
52
|
+
required_attributes: [],
|
53
|
+
excluded_attributes: [],
|
54
|
+
allowed_attributes: [],
|
55
|
+
foreign_keys: true,
|
56
|
+
primary_keys: true,
|
57
|
+
validation_keys: false,
|
58
|
+
association_macro: nil,
|
59
|
+
source_nulls: false,
|
60
|
+
type_key: :query,
|
61
|
+
type_sub_key: :output_type
|
140
62
|
}
|
141
63
|
}
|
142
64
|
|
143
65
|
or individually by using the
|
144
66
|
|
145
|
-
|
67
|
+
graphql_types
|
146
68
|
|
147
69
|
macro attribute on the model, passing the individual settings that differ from the defaults. These will be merged into the default values. i.e.
|
148
70
|
|
149
71
|
|
150
|
-
graphql_types
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
},
|
163
|
-
create: {
|
164
|
-
input_type: {
|
165
|
-
excluded_attributes: [:crypted_password]
|
166
|
-
},
|
167
|
-
output_type: {
|
168
|
-
excluded_attributes: [:crypted_password]
|
169
|
-
}
|
170
|
-
},
|
171
|
-
delete: {
|
172
|
-
input_type: {
|
173
|
-
excluded_attributes: [:crypted_password]
|
174
|
-
},
|
175
|
-
output_type: {
|
176
|
-
excluded_attributes: [:crypted_password]
|
177
|
-
}
|
178
|
-
}
|
72
|
+
graphql_types output_type: {
|
73
|
+
excluded_attributes: [:crypted_password, :secret, :username],
|
74
|
+
association_macro: :none,
|
75
|
+
foreign_keys: false,
|
76
|
+
primary_keys: false
|
77
|
+
},
|
78
|
+
input_type: {
|
79
|
+
excluded_attributes: [:crypted_password, :secret, :username],
|
80
|
+
association_macro: :none,
|
81
|
+
foreign_keys: false,
|
82
|
+
primary_keys: false
|
83
|
+
}
|
179
84
|
|
180
|
-
or you can
|
85
|
+
or you can initialize your own GRAPHQL_DEFAULT_TYPES constant for the model in an initializer, these settings will not be merged into the default settings, so you will need to fully elucidate the types
|
181
86
|
|
182
87
|
#config/initializers/grapqhql_model_mapper_init.rb
|
183
|
-
GraphqlModelMapper::[YOUR_MODEL_NAME_CLASSIFIED_AND_CAPITALIZED]_GRAPHQL_DEFAULT_TYPES = {
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
source_nulls: false,
|
194
|
-
type_key: :query,
|
195
|
-
type_sub_key: :input_type
|
196
|
-
},
|
197
|
-
output_type: {
|
198
|
-
required_attributes: [],
|
199
|
-
excluded_attributes: [],
|
200
|
-
allowed_attributes: [],
|
201
|
-
foreign_keys: true,
|
202
|
-
primary_keys: true,
|
203
|
-
validation_keys: false,
|
204
|
-
association_macro: nil,
|
205
|
-
source_nulls: false,
|
206
|
-
type_key: :query,
|
207
|
-
type_sub_key: :output_type
|
208
|
-
}
|
209
|
-
},
|
210
|
-
update: {
|
211
|
-
input_type: {
|
212
|
-
required_attributes: [],
|
213
|
-
excluded_attributes: [],
|
214
|
-
allowed_attributes: [],
|
215
|
-
foreign_keys: true,
|
216
|
-
primary_keys: true,
|
217
|
-
validation_keys: false,
|
218
|
-
association_macro: nil,
|
219
|
-
source_nulls: false,
|
220
|
-
type_key: :update,
|
221
|
-
type_sub_key: :input_type
|
222
|
-
},
|
223
|
-
output_type: {
|
224
|
-
required_attributes: [],
|
225
|
-
excluded_attributes: [],
|
226
|
-
allowed_attributes: [],
|
227
|
-
foreign_keys: true,
|
228
|
-
primary_keys: true,
|
229
|
-
validation_keys: false,
|
230
|
-
association_macro: nil,
|
231
|
-
source_nulls: true,
|
232
|
-
type_key: :update,
|
233
|
-
type_sub_key: :output_type
|
234
|
-
}
|
88
|
+
GraphqlModelMapper::CustomType::[YOUR_MODEL_NAME_CLASSIFIED_AND_CAPITALIZED]_GRAPHQL_DEFAULT_TYPES = {
|
89
|
+
input_type: {
|
90
|
+
required_attributes: [],
|
91
|
+
excluded_attributes: [:crypted_password, :secret, :username],
|
92
|
+
allowed_attributes: [],
|
93
|
+
foreign_keys: false,
|
94
|
+
primary_keys: false,
|
95
|
+
validation_keys: false,
|
96
|
+
association_macro: :none,
|
97
|
+
source_nulls: false
|
235
98
|
},
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
source_nulls: false,
|
246
|
-
type_key: :delete,
|
247
|
-
type_sub_key: :input_type
|
248
|
-
},
|
249
|
-
output_type: {
|
250
|
-
required_attributes: [],
|
251
|
-
excluded_attributes: [],
|
252
|
-
allowed_attributes: [],
|
253
|
-
foreign_keys: false,
|
254
|
-
primary_keys: true,
|
255
|
-
validation_keys: false,
|
256
|
-
association_macro: nil,
|
257
|
-
source_nulls: true,
|
258
|
-
type_key: :delete,
|
259
|
-
type_sub_key: :output_type
|
260
|
-
}
|
261
|
-
},
|
262
|
-
create: {
|
263
|
-
input_type: {
|
264
|
-
required_attributes: [],
|
265
|
-
excluded_attributes: [],
|
266
|
-
allowed_attributes: [],
|
267
|
-
foreign_keys: true,
|
268
|
-
primary_keys: false,
|
269
|
-
validation_keys: false,
|
270
|
-
association_macro: :has_many,
|
271
|
-
source_nulls: false,
|
272
|
-
type_key: :create,
|
273
|
-
type_sub_key: :input_type
|
274
|
-
},
|
275
|
-
output_type: {
|
276
|
-
required_attributes: [],
|
277
|
-
excluded_attributes: [],
|
278
|
-
allowed_attributes: [],
|
279
|
-
foreign_keys: true,
|
280
|
-
primary_keys: true,
|
281
|
-
validation_keys: false,
|
282
|
-
association_macro: nil,
|
283
|
-
source_nulls: true,
|
284
|
-
type_key: :create,
|
285
|
-
type_sub_key: :output_type
|
286
|
-
}
|
99
|
+
output_type: {
|
100
|
+
required_attributes: [],
|
101
|
+
excluded_attributes: [:crypted_password, :secret, :username],
|
102
|
+
allowed_attributes: [],
|
103
|
+
foreign_keys: false,
|
104
|
+
primary_keys: false,
|
105
|
+
validation_keys: false,
|
106
|
+
association_macro: :none,
|
107
|
+
source_nulls: false
|
287
108
|
}
|
288
109
|
}
|
289
|
-
|
110
|
+
|
111
|
+
## Resolver Options
|
290
112
|
|
291
|
-
The query and mutation objects have a default resolver defined that may be sufficient for your needs (with the exception of the create mutation which
|
113
|
+
The query and mutation objects have a default resolver defined that may be sufficient for your needs (with the exception of the create mutation which simply validates the input and does not actually create the record).
|
292
114
|
|
293
115
|
def self.create_resolver(obj, inputs, ctx, model_name)
|
294
116
|
if !GraphqlModelMapper.authorized?(ctx, model_name, :create)
|
@@ -310,45 +132,83 @@ The query and mutation objects have a default resolver defined that may be suffi
|
|
310
132
|
|
311
133
|
If you want to assign your own resolvers for your type you can override the default resolver for the type on the macro attribute in the following way:
|
312
134
|
|
313
|
-
graphql_query resolver: -> (obj, inputs, ctx){
|
135
|
+
graphql_query resolver: -> (obj, inputs, ctx){ GraphqlModelMapper.log_resolve(ctx, args, generate_error: true) ) }
|
314
136
|
|
315
|
-
|
137
|
+
or create named methods on your model which will override the resolver (takes precedence over the default resolver AND the macro assigned resolver)
|
316
138
|
|
317
|
-
|
139
|
+
def self.graphql_query_resolver(obj,args,ctx)
|
140
|
+
# this method will log the info for the inputs(arguments)/outputs(fields) to the Rails logger as well as optionally generate an error containing the information
|
141
|
+
# it can be called from any resolve method
|
142
|
+
GraphqlModelMapper.log_resolve(ctx, args, generate_error: true)
|
143
|
+
end
|
318
144
|
|
319
|
-
|
320
|
-
|
321
|
-
{
|
322
|
-
total: items.length,
|
323
|
-
items: items
|
324
|
-
}
|
325
|
-
}
|
326
|
-
or
|
145
|
+
def self.graphql_create_resolver(obj,args,ctx)
|
146
|
+
end
|
327
147
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
148
|
+
def self.graphql_update_resolver(obj,args,ctx)
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.graphql_delete_resolver(obj,args,ctx)
|
152
|
+
end
|
153
|
+
|
154
|
+
The method that you assign to the resolver should either be self contained or call a class method that accepts and orchestrates the parameters passed from GraphQL in the resolve. In this example the query resolver is calling a GraphqlModelMapper utility function to log the input parameters (args) and output type(s) (context.fields).
|
334
155
|
|
156
|
+
Another resolver option is to provide a resolver wrapper. This will wrap the inner resolves for queries and mutations with a wrapper method that you can use to accomplish global methodologies or to format results before or after your resolve method is called. They inherit from GraphqlModelMapper::Resolve::ResolveWrapper and can be declared in your initializer in the following manner:
|
335
157
|
|
336
|
-
|
158
|
+
class GraphqlModelMapper::CustomType::QueryResolveWrapper < GraphqlModelMapper::Resolve::ResolveWrapper
|
159
|
+
# @resolve_func is original resolve, either default resolve or overriden from model
|
160
|
+
# you can insert other custom functionality required before or after the resolver is called
|
161
|
+
def call(obj, args, ctx)
|
162
|
+
puts "overidden query resolve wrapper"
|
163
|
+
|
164
|
+
# custom methods to call before the resolve
|
165
|
+
|
166
|
+
ret = @resolve_func.call(obj, args, ctx)
|
167
|
+
|
168
|
+
# custom methods to call after the resolve
|
169
|
+
|
170
|
+
# always return the result from the resolve or your custom formatted methods (complying with the expected return type) at the end of the wrapper call
|
171
|
+
ret
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class GraphqlModelMapper::CustomType::MutationResolveWrapper < GraphqlModelMapper::Resolve::ResolveWrapper
|
176
|
+
def call(obj, args, ctx)
|
177
|
+
puts "overidden mutation resolve wrapper"
|
178
|
+
@resolve_func.call(obj, args, ctx)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
These are then passed to your Schema arguments
|
183
|
+
|
184
|
+
GraphqlModelMapper.Schema(mutation_resolve_wrapper: GraphqlModelMapper::CustomType::MutationResolveWrapper, query_resolve_wrapper: GraphqlModelMapper::CustomType::QueryResolveWrapper)
|
185
|
+
|
186
|
+
Some other attributes that you can set on the macro functions in addition to the input/output types and resolver are
|
337
187
|
|
338
188
|
## graphql_query
|
339
189
|
|
340
190
|
description: # a short description of the query
|
341
|
-
scope_methods: # scope methods available to be used in the query, these should not be parameterized and must be written so that they
|
342
|
-
arguments: # a list of argument definitions to override the default arguments, if using your own arguments you will need to override the query resolver to act on those arguments, the default
|
191
|
+
scope_methods: # scope methods available to be used in the query, these should not be parameterized and must be written so that they valid in the presence of other tables which may be included in the associations
|
192
|
+
arguments: # a list of argument definitions to override the default arguments, if using your own arguments you will need to override the query resolver to act on those arguments, the default
|
193
|
+
|
194
|
+
Arguments should be a list of objects with the following attirbutes (*required)
|
195
|
+
*name - displayed name of the attribute
|
196
|
+
*type - GraphQL type of the attribute
|
197
|
+
default - default value
|
198
|
+
authorization - authorization level for the attribute (if GraphqlModelMapper.use_authorize = true this authorization will be compared to the authorized ability for the user on the model to which this attribute applies)
|
199
|
+
|
200
|
+
The default arguments handled by the default resolver and exposed on the query and delete mutations are:
|
343
201
|
|
344
202
|
default_arguments =
|
345
|
-
[{:name=>:explain,
|
346
|
-
{:name=>:id,
|
347
|
-
{:name=>:ids,
|
348
|
-
{:name=>:
|
349
|
-
{:name=>:
|
350
|
-
{:name=>:
|
351
|
-
{:name=>:
|
203
|
+
[{:name=>:explain, :type=>GraphQL::BOOLEAN_TYPE, :default=>nil, :authorization=>:manage}, # handled by the default resolver, outputs the top level sql for the operation
|
204
|
+
{:name=>:id, :type=>GraphQL::INT_TYPE, :default=>nil}, # allows input of an global id for top level record selection for the model
|
205
|
+
{:name=>:ids, :type=>GraphQL::INT_TYPE.to_list_type, :default=>nil}, # allows input of an array of global ids for top level records selection for the model
|
206
|
+
{:name=>:item_id, :type=>GraphQL::INT_TYPE, :default=>nil}, # allows input of a record id for top level record selection for the model
|
207
|
+
{:name=>:item_ids, :type=>GraphQL::INT_TYPE.to_list_type, :default=>nil} # allows input of an array of record ids for top level records selection for the model
|
208
|
+
{:name=>:limit, :type=>GraphQL::INT_TYPE, :default=>50}, # limits the number of records retuurned (defaults to 50 records)
|
209
|
+
{:name=>:offset, :type=>GraphQL::INT_TYPE, :default=>nil}, # specifies an offset for the start of the records returned
|
210
|
+
{:name=>:order, :type=>GraphQL::STRING_TYPE, :default=>nil, :authorization=>:manage}, # a string value that is passed to ActiveRecord query specifying the output order
|
211
|
+
{:name=>:where, :type=>GraphQL::STRING_TYPE.to_list_type, :default=>nil, :authorization=>:manage}] # a string array for use in ActiveRecord query, can be a string or a query/value array to be used by the query ["model.id =? and model.date is not nul]", "1"]
|
352
212
|
|
353
213
|
## graphql_delete
|
354
214
|
description:
|
@@ -423,7 +283,7 @@ Follow the setup for cancancan and create an app/model/ability.rb file to setup
|
|
423
283
|
end
|
424
284
|
|
425
285
|
|
426
|
-
GraphqlModelMapper requires an
|
286
|
+
GraphqlModelMapper requires an ability method on your current_user in order to check the context current_user's authorization to access a GraphQL objects model implementation.
|
427
287
|
|
428
288
|
class User < ActiveRecord::Base
|
429
289
|
def ability
|
@@ -441,11 +301,15 @@ Once you have your models decorated with the graphql_query/graphql_update/graphq
|
|
441
301
|
require 'graphql_model_mapper'
|
442
302
|
|
443
303
|
# these are options that can be passed to the schema initiation to enable query logging or for authorization setup
|
444
|
-
#
|
445
|
-
#
|
446
|
-
#
|
447
|
-
|
448
|
-
|
304
|
+
#
|
305
|
+
# nesting_strategy: can be :flat, :shallow or :deep
|
306
|
+
# type_case: can be :camelize, :underscore or :classify
|
307
|
+
# scan_for_polymorphic_associations: when true will automatically scan your tables for the types to use when it encounters polymorphic associations, this defaults to **false** because it is a high cost operation. It is recommended that you setup custom types to handle the polymorphic associations to avoid table scans during the schema build process. See the custom types section for additional guidance on this topic.
|
308
|
+
|
309
|
+
# default values are shown here
|
310
|
+
default_schema_options = {log_query_depth: false, log_query_complexity: false, use_backtrace: false, use_authorize: false, nesting_strategy: :deep, type_case: :camelize, max_page_size: 100, scan_for_polymorphic_associations: false, mutation_resolve_wrapper: nil, query_resolve_wrapper: nil, bidirectional_pagination: false, default_nodes_field: false}
|
311
|
+
|
312
|
+
GraphqlModelMapperSchema = GraphqlModelMapper.Schema(default_schema_options)
|
449
313
|
GraphQL::Errors.configure(GraphqlModelMapperSchema) do
|
450
314
|
|
451
315
|
rescue_from ActiveRecord::StatementInvalid do |exception|
|
@@ -534,6 +398,143 @@ you can then reference your previously assigned schema in app/controllers/graph
|
|
534
398
|
end
|
535
399
|
end
|
536
400
|
|
401
|
+
## Custom attribute types
|
402
|
+
The functionality included in the type generation uses the base type reported by ActiveRecord for the definition of the Input/Output model field/argument types. These base types include:
|
403
|
+
|
404
|
+
:integer -> GraphQL::INT_TYPE
|
405
|
+
:decimal, :float -> GraphQL::FLOAT_TYPE
|
406
|
+
:boolean -> GraphQL::BOOLEAN_TYPE
|
407
|
+
:date, :datetime -> GraphqlModelMapper::DATE_TYPE
|
408
|
+
:geometry, :multipolygon, :polygon -> GraphqlModelMapper::GEOMETRY_OBJECT_TYPE
|
409
|
+
:string -> GraphQL::STRING_TYPE
|
410
|
+
|
411
|
+
|
412
|
+
In some cases this is not sufficient. In the case that you are using ActiveRecord Enums (Rails >= 4.1) or you have stuffed formatted data into a field that you would like to display in a custom way there is an option for you to define a custom type for input/output of that specialized data.
|
413
|
+
|
414
|
+
In order to support this functionality you will need to create an initializer for creation of your custom types. The naming convention will allow the GraphqlModelMapper to pickup your custom types for use in the generated schema in place of the default ActiveRecord db type.
|
415
|
+
|
416
|
+
Use the form "#{model_name.classified}#{db_column_name.classified}Attribute#{Input/Output}" to name your custom type in the following manner.
|
417
|
+
|
418
|
+
If your model name is "Job" and the attribute that you want to override the type is named "status", you will want to create a GraphQL object constant like the following:
|
419
|
+
|
420
|
+
GraphqlModelMapper::CustomType::JobStatusAttributeInput
|
421
|
+
GraphqlModelMapper::CustomType::JobStatusAttributeOutput
|
422
|
+
|
423
|
+
in the following example I will show you how to create an override type for a Rails >=4.1 Enum value
|
424
|
+
|
425
|
+
given the following definition in a model named 'Job' with an enum type mapped to the 'status' attribute
|
426
|
+
|
427
|
+
class Job < ApplicationRecord
|
428
|
+
enum status: { applied:0, enrolled: 100, accepted: 200, rejected: 300, cancelled: 400}
|
429
|
+
end
|
430
|
+
|
431
|
+
to enable application of a custom type to handle the input/output of the AR enum value you would need to create custom types in an initilizer. In this case we will use config/initializers/graphql_model_mapper_init.rb to create those types.
|
432
|
+
|
433
|
+
If you do not need to intercept the values when the custom type is used in input/output you can simply assign a GraphQL enum to the custom type. (take note of the naming convention used in the last statement, since the custom type will be picked up by convention when the model types are built it is important that you follow the naming convention **exactly** to ensure your custom type is used, custom types should be defined and reside in the GraphqlModelMapper::CustomType namespace). Since we do not need to intercept the field/argument resolver/prepare for this type, both input and output can be directly assigned to the GraphQL enum type. **(this case is already handled by default in Rails >=4.1 so you will not need to establish a custom type for this built in support for Rails enums)**
|
434
|
+
|
435
|
+
**config/initializers/graphql_model_mapper_init.rb**
|
436
|
+
|
437
|
+
#config/initializers/graphql_model_mapper_init.rb
|
438
|
+
|
439
|
+
GraphqlModelMapper::CustomType::JobStatusAttributeEnum = GraphQL::EnumType.define do
|
440
|
+
name "JobStatusAttributeEnum"
|
441
|
+
value("Applied", "", value: 'applied')
|
442
|
+
value("Enrolled", "", value: 'enrolled')
|
443
|
+
value("Accepted", "", value: 'accepted')
|
444
|
+
value("Rejectd", "", value: 'rejected')
|
445
|
+
value("Cancelled", "", value: 'cancelled')
|
446
|
+
end
|
447
|
+
|
448
|
+
GraphqlModelMapper::CustomType::JobStatusAttributeOutputType = GraphqlModelMapper::CustomType::JobStatusAttributeInputType = GraphqlModelMapper::CustomType::JobStatusAttributeEnumType
|
449
|
+
|
450
|
+
In the event that you need to customize the way in which your custom types are used at runtime you will need to fully declare the field and argument that will be used with your custom type. In this example I am declaring the Input and Output fully so that I can use additional functionality in the prepare/resolve methods.
|
451
|
+
|
452
|
+
**config/initializers/graphql_model_mapper_init.rb**
|
453
|
+
|
454
|
+
#config/initializers/graphql_model_mapper_init.rb
|
455
|
+
GraphqlModelMapper::CustomType::JobStatusAttributeEnum = GraphQL::EnumType.define do
|
456
|
+
name "JobStatusAttributeEnum"
|
457
|
+
value("Applied", "", value: 'applied')
|
458
|
+
value("Enrolled", "", value: 'enrolled')
|
459
|
+
value("Accepted", "", value: 'accepted')
|
460
|
+
value("Rejectd", "", value: 'rejected')
|
461
|
+
value("Cancelled", "", value: 'cancelled')
|
462
|
+
end
|
463
|
+
|
464
|
+
GraphqlModelMapper::CustomType::JobStatusAttributeOutput = GraphQL::Field.define do
|
465
|
+
name "JobStatusAttributeOutput"
|
466
|
+
type(GraphqlModelMapper::CustomType::JobStatusAttributeEnum)
|
467
|
+
description("testing")
|
468
|
+
resolve ->(object, arguments, context) {
|
469
|
+
object.status
|
470
|
+
}
|
471
|
+
end
|
472
|
+
|
473
|
+
GraphqlModelMapper::CustomType::JobStatusAttributeInput = GraphQL::Argument.define do
|
474
|
+
name "JobStatusAttributeInput"
|
475
|
+
type (GraphqlModelMapper::CustomType::JobStatusAttributeEnum)
|
476
|
+
prepare ->(value, ctx) do
|
477
|
+
value
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
once you have these types defined and have restarted your server you should be able to see the mapping to the custom type in your __schema__ view and be able to use the GraphQL enums for query and update.
|
482
|
+
|
483
|
+
To establish a custom type for a polymorphic association attribute on your model you will follow the same naming convention, but establish a GraphQL UnionType with interfaces that match the possible types that the polymorphic relation represent. (UnionTypes are not valid on input types, so they are only applicable to the output type)
|
484
|
+
|
485
|
+
Assuming you have a relation in your models resembling:
|
486
|
+
|
487
|
+
class Car < ActiveRecord::Base
|
488
|
+
belongs_to :parent, :polymorphic => true
|
489
|
+
end
|
490
|
+
|
491
|
+
class Ford < ActiveRecord::Base
|
492
|
+
has_many :cars, :as => :parent
|
493
|
+
end
|
494
|
+
|
495
|
+
class Chevy < ActiveRecord::Base
|
496
|
+
has_many :cars, :as => :parent
|
497
|
+
end
|
498
|
+
|
499
|
+
you will then add the following to your initialization file for the custom type:
|
500
|
+
|
501
|
+
GraphqlModelMapper::CustomType::CarParentUnionOutput = GraphQL::UnionType.define do
|
502
|
+
name "CarParentUnionOutput"
|
503
|
+
description "UnionType for polymorphic association parent on Car"
|
504
|
+
possible_types [GraphqlModelMapper::CHEVYOUTPUT, GraphqlModelMapper::FORDOUTPUT]
|
505
|
+
resolve_type ->(obj, ctx) {
|
506
|
+
#the field resolve_type will dereference the correct type when queried using the GraphqlModelMapper::MapperType.graph_object utility method to return the correct type mapped to the model (this method could also be used in the possible_types declaration if prefferred over the use of the assigned contant)
|
507
|
+
|
508
|
+
GraphqlModelMapper::MapperType.graph_object(obj.class.name)
|
509
|
+
}
|
510
|
+
end
|
511
|
+
|
512
|
+
when resolving the parent attribute in a query you will need to write the query to late resolve the type when the data is fetched:
|
513
|
+
|
514
|
+
query {
|
515
|
+
car{
|
516
|
+
items {
|
517
|
+
nodes {
|
518
|
+
parent {
|
519
|
+
... on FordOutput{
|
520
|
+
id
|
521
|
+
model
|
522
|
+
ford_specific_attribute
|
523
|
+
}
|
524
|
+
|
525
|
+
... on ChevyOutput{
|
526
|
+
id
|
527
|
+
model
|
528
|
+
chevy_specific_attribute
|
529
|
+
}
|
530
|
+
}
|
531
|
+
}
|
532
|
+
}
|
533
|
+
}
|
534
|
+
}
|
535
|
+
|
536
|
+
**Note: when querying the model, you will still use the underlying database field value for any custom type when using it in a 'where' argument since the query is sent directly to the db and has no knowlege of the Rails enum or other GraphQL custom types.**
|
537
|
+
|
537
538
|
## Development
|
538
539
|
|
539
540
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|