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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a3de0884a30479b29813d7f124151fb1438942dd
4
- data.tar.gz: cc7c0689ccf60fdbe2f85a7f006eef6e3d52891a
3
+ metadata.gz: edf76e62fad8e31f3b2e1572544f170a5c151051
4
+ data.tar.gz: dee948ab9a1125e55e19b588d5ef4837dd576ea4
5
5
  SHA512:
6
- metadata.gz: 0f8a77c07c0ac1799dc4997e794413a5c08d4450c6f49aa3ba401adeb3251548167ee1195703c1f58600d2c5e7d4fb3109ed64a49ff14441b8d826158de307b4
7
- data.tar.gz: c23e038b5a35f578b82d7aa8d8cfd6499e350f3a504001b925d413d1e930a426891808f04367e583a5d59f70dced5aab6cbe43f4304642cf33f4b9c696058131
6
+ metadata.gz: 5c930baf5c2c607361e43c43d377321a9f06278c927b3b8a486a2e3c8eb99c9c94dff808c00df050573979c2ac9aef7c07ac7549ab0496b32262ac4b3ada1052
7
+ data.tar.gz: 8cd16e8b313baab6b7608a6e39b6f8dbb2edd04b3f5bf9c9ec90f6bb2890756935b66e2d8f5ef3e5c338262d5de38bf072ee47324a79993650778527134ff109
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- graphql_model_mapper (0.0.6)
4
+ graphql_model_mapper (0.1.0)
5
5
  activemodel (>= 3.2.22.5)
6
6
  activerecord (>= 3.2.22.5)
7
7
  activesupport (>= 3.2.22.5)
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
- query: {
38
- input_type: {
39
- required_attributes: [],
40
- excluded_attributes: [],
41
- allowed_attributes: [],
42
- foreign_keys: true,
43
- primary_keys: true,
44
- validation_keys: false,
45
- association_macro: nil,
46
- source_nulls: false,
47
- type_key: :query,
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
- create: {
116
- input_type: {
117
- required_attributes: [],
118
- excluded_attributes: [],
119
- allowed_attributes: [],
120
- foreign_keys: true,
121
- primary_keys: false,
122
- validation_keys: false,
123
- association_macro: :has_many,
124
- source_nulls: false,
125
- type_key: :create,
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
- graphql_type
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 query: {
151
- output_type: {
152
- excluded_attributes: [:crypted_password]
153
- }
154
- },
155
- update: {
156
- input_type: {
157
- excluded_attributes: [:crypted_password]
158
- },
159
- output_type: {
160
- excluded_attributes: [:crypted_password]
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 specify the **graphql_types** attribute on the model and initialize your own 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
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
- query: {
185
- input_type: {
186
- required_attributes: [],
187
- excluded_attributes: [],
188
- allowed_attributes: [],
189
- foreign_keys: true,
190
- primary_keys: true,
191
- validation_keys: false,
192
- association_macro: nil,
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
- delete: {
237
- input_type: {
238
- required_attributes: [:id],
239
- excluded_attributes: [],
240
- allowed_attributes: [:id],
241
- foreign_keys: false,
242
- primary_keys: true,
243
- validation_keys: false,
244
- association_macro: nil,
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
- ## Other Options
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 most likely will not be adequate for your implementation, currently it simply validates the input and does not attempt to add the record).
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){ raise GraphQL::ExecutionError.new(inputs.to_h.to_a) }
135
+ graphql_query resolver: -> (obj, inputs, ctx){ GraphqlModelMapper.log_resolve(ctx, args, generate_error: true) ) }
314
136
 
315
- 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 it is simply calling a GraphQL::ExecutionError to output the contents of the input parameters. These methods could be anywhere in your application, they are not limited to the model on which they are defined.
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
- When returning items to populate the appropriate output type, return them as a hash value shaped to fit the output types definition. GraphQL will take care of the final mapping and shapping of the models item(s)
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
- resolver: -> (obj, inputs, ctx){
320
- items = YourClass.method_that_returns_items(obj, inputs, ctx, name)
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
- resolver: -> (obj, inputs, ctx){
329
- item = YourClass.method_that_returns_an_item(obj, inputs, ctx, name)
330
- {
331
- item: item
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
- Some other attributes that you can set on the graphql_query in addition to the resolver are
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 do not collide with other tables which may be included in the associations
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 arguments exposed on the query are:
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, :type=>GraphQL::BOOLEAN_TYPE, :default=>nil}, # handled by the default resolver, outputs the top level sql for the operation
346
- {:name=>:id, :type=>GraphQL::INT_TYPE, :default=>nil}, # allows input of an id for top level record selection for the model
347
- {:name=>:ids, :type=>GraphQL::INT_TYPE.to_list_type, :default=>nil}, # allows input of an array of ids for top level records selection for the model
348
- {:name=>:limit, :type=>GraphQL::INT_TYPE, :default=>50}, # limits the number of records retuurned (defaults to 50 records)
349
- {:name=>:offset, :type=>GraphQL::INT_TYPE, :default=>nil}, # specifies an offset for the start of the records returned
350
- {:name=>:order, :type=>GraphQL::STRING_TYPE, :default=>nil}, # a string value that is passed to ActiveRecord query specifying the output order
351
- {:name=>:where, :type=>GraphQL::STRING_TYPE.to_list_type, :default=>nil}] # 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"]
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 optional ability method on your current_user in order to check the context current_users authorization to access a GraphQL objects model implementation.
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
- # nesting_strategy can be :flat, :shallow or :deep
445
- # type_case can be :camelize, :underscore or :classify
446
- # the default values are shown below
447
- options = {:log_query_depth=>false, :log_query_complexity=>false, :use_backtrace=>false, :use_authorize=>false, :nesting_strategy=>:shallow, :type_case=>:camelize}
448
- GraphqlModelMapperSchema = GraphqlModelMapper.Schema(use_authorize: true)
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.