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.
@@ -1,5 +1,6 @@
1
1
  require "graphql"
2
2
  require "graphql_model_mapper/mapper_type"
3
+ require "graphql_model_mapper/custom_type"
3
4
  require "graphql_model_mapper/mutation"
4
5
  require "graphql_model_mapper/query"
5
6
  require "graphql_model_mapper/resolve"
@@ -9,14 +10,30 @@ require "graphql_model_mapper/version"
9
10
  require 'graphql_model_mapper/railtie' if defined?(Rails)
10
11
 
11
12
  module GraphqlModelMapper
13
+ mattr_accessor :query_resolve_wrapper
14
+ mattr_accessor :mutation_resolve_wrapper
15
+ mattr_accessor :resolve_wrapper
12
16
  mattr_accessor :type_case
13
17
  mattr_accessor :nesting_strategy
14
18
  mattr_accessor :use_authorize
19
+ mattr_accessor :max_page_size
20
+ mattr_accessor :scan_for_polymorphic_associations
21
+ mattr_accessor :default_nodes_field
22
+ mattr_accessor :bidirectional_pagination
23
+
15
24
 
16
25
  @@type_case = :camelize
17
26
  @@nesting_strategy = :shallow
18
27
  @@use_authorize = false
28
+ @@max_page_size = 100
29
+ @@scan_for_polymorphic_associations = false
30
+ @@query_resolve_wrapper = nil
31
+ @@mutation_resolve_wrapper = nil
32
+ @@default_nodes_field = false
33
+ @@bidirectional_pagination = false
19
34
 
35
+
36
+
20
37
  class << self
21
38
  attr_writer :logger
22
39
 
@@ -36,15 +53,15 @@ module GraphqlModelMapper
36
53
 
37
54
  protected
38
55
 
39
- def graphql_types(query: {}, update: {}, delete: {}, create: {})
56
+ def graphql_types(input_type:{}, output_type:{})
40
57
  name = self.name
41
58
  define_singleton_method(:graphql_types) do
42
- GraphqlModelMapper::MapperType.graphql_types(name: name, query: query, update: update, delete: delete, create: create)
59
+ GraphqlModelMapper::MapperType.graphql_types(name: name, input_type: input_type, output_type: output_type)
43
60
  end
44
61
  end
45
62
 
46
63
  def graphql_update(description:"", resolver: -> (obj, inputs, ctx){
47
- item = GraphqlModelMapper::Resolve.update_resolver(obj, inputs, ctx, name)
64
+ item = GraphqlModelMapper::Resolve.update_resolver(obj, inputs, ctx, name)
48
65
  {
49
66
  item: item
50
67
  }
@@ -58,7 +75,6 @@ module GraphqlModelMapper
58
75
  def graphql_delete(description:"", resolver: -> (obj, inputs, ctx){
59
76
  items = GraphqlModelMapper::Resolve.delete_resolver(obj, inputs, ctx, name)
60
77
  {
61
- total: items.length,
62
78
  items: items
63
79
  }
64
80
  }, arguments: [], scope_methods: [])
@@ -69,7 +85,7 @@ module GraphqlModelMapper
69
85
  end
70
86
 
71
87
  def graphql_create(description:"", resolver: -> (obj, args, ctx){
72
- item = GraphqlModelMapper::Resolve.create_resolver(obj, args, ctx, name)
88
+ item = GraphqlModelMapper::Resolve.create_resolver(obj, args, ctx, name)
73
89
  {
74
90
  item: item
75
91
  }
@@ -82,14 +98,10 @@ module GraphqlModelMapper
82
98
 
83
99
  def graphql_query(description: "", resolver: -> (obj, args, ctx) {
84
100
  items = GraphqlModelMapper::Resolve.query_resolver(obj, args, ctx, name)
85
- {
86
- items: items,
87
- total: items.length
88
- }
89
101
  }, arguments: [], scope_methods: [])
90
102
  name = self.name
91
103
  define_singleton_method(:graphql_query) do
92
- GraphqlModelMapper::Query.graphql_query(name: name, description: description, resolver: resolver, scope_methods: scope_methods)
104
+ GraphqlModelMapper::Query.graphql_query(name: name, description: description, resolver: resolver, scope_methods: scope_methods, arguments: arguments)
93
105
  end
94
106
  end
95
107
  end
@@ -0,0 +1,4 @@
1
+ module GraphqlModelMapper
2
+ module CustomType
3
+ end
4
+ end
@@ -2,29 +2,32 @@ module GraphqlModelMapper
2
2
  module MapperType
3
3
  def self.graphql_types(
4
4
  name: self.name,
5
- query: {},
6
- update: {},
7
- delete: {},
8
- create: {}
5
+ input_type: {},
6
+ output_type: {}
9
7
  )
10
- #typesuffix = method(__method__).parameters.map { |arg| eval arg[1].to_s }.hash.abs.to_i.to_s
11
- GraphqlModelMapper.logger.info("#{name.upcase}") if GraphqlModelMapper.const_defined?("#{name.upcase}_GRAPHQL_DEFAULT_TYPES")
12
- return GraphqlModelMapper.get_constant("#{name.upcase}_GRAPHQL_DEFAULT_TYPES") if GraphqlModelMapper.const_defined?("#{name.upcase}_GRAPHQL_DEFAULT_TYPES")
13
- #GraphqlModelMapper.logger.info "#{name.upcase}_GRAPHQL_DEFAULT_TYPES"
8
+ return GraphqlModelMapper::CustomType.const_get("#{name.upcase}_GRAPHQL_DEFAULT_TYPES") if GraphqlModelMapper::CustomType.const_defined?("#{name.upcase}_GRAPHQL_DEFAULT_TYPES")
14
9
  graphql_type = {}
15
- graphql_type[:query] = query
16
- graphql_type[:update] = update
17
- graphql_type[:delete] = delete
18
- graphql_type[:create] = create
10
+ graphql_type[:input_type] = input_type
11
+ graphql_type[:input_type][:type_sub_key] = :input_type
12
+ graphql_type[:output_type] = output_type
13
+ graphql_type[:output_type][:type_sub_key] = :output_type
19
14
  merged_graphql_type = self.graphql_default_types.deep_merge(graphql_type)
20
- GraphqlModelMapper.set_constant("#{name.upcase}_GRAPHQL_DEFAULT_TYPES", merged_graphql_type)
15
+ GraphqlModelMapper::CustomType.const_set("#{name.upcase}_GRAPHQL_DEFAULT_TYPES", merged_graphql_type)
21
16
 
22
17
  merged_graphql_type
23
18
  end
24
19
 
25
20
 
26
- def self.get_ar_object_with_params(name, type_key: nil, type_sub_key: nil)
27
- self.get_ar_object(name, self.get_type_params(name, type_key: type_key, type_sub_key: type_sub_key))
21
+ def self.graph_object(name)
22
+ if GraphqlModelMapper.implementations.map(&:to_s).include?(name.classify)
23
+ self.get_ar_object_with_params(name)
24
+ else
25
+ name.classify.constantize
26
+ end
27
+ end
28
+
29
+ def self.get_ar_object_with_params(name, type_sub_key: :output_type)
30
+ self.get_ar_object(name, self.get_type_params(name, type_sub_key: type_sub_key))
28
31
  end
29
32
 
30
33
  def self.get_ar_object(name,
@@ -36,12 +39,13 @@ module GraphqlModelMapper
36
39
  validation_keys: false,
37
40
  association_macro: nil,
38
41
  source_nulls: true,
39
- type_key: nil,
40
42
  type_sub_key: nil)
41
43
 
42
44
 
43
45
  #typesuffix = method(__method__).parameters.map { |arg| eval arg[1].to_s }.hash.abs.to_i.to_s
44
- typesuffix = "#{type_key.to_s.classify}#{GraphqlModelMapper.underscore(type_sub_key.to_s)}".camelize
46
+ #typesuffix = "#{type_key.to_s.classify}#{GraphqlModelMapper.underscore(type_sub_key.to_s)}".camelize
47
+ typesuffix = "#{GraphqlModelMapper.underscore(type_sub_key.to_s).sub("_Type", "")}".camelize
48
+ #typesuffix = "#{type_sub_key == :input_type ? '_i' : '_o'}"
45
49
  typename = "#{GraphqlModelMapper.get_type_name(name)}#{typesuffix}"
46
50
 
47
51
  return GraphqlModelMapper.get_constant(typename) if GraphqlModelMapper.defined_constant?(typename)
@@ -55,83 +59,102 @@ module GraphqlModelMapper
55
59
  # figure out which association fields we are exposing
56
60
  association_includes = (model.reflect_on_all_associations(association_macro).map(&:name)).map(&:to_sym) - excluded_attributes
57
61
 
58
- # find all relations for this model, skip ones where the association klass is invalid, as well as polymorphic associations, be cognizant of include/exclude arrays similar to dbfields
59
- associations = model.reflect_on_all_associations(association_macro).select{|t| begin t.klass rescue next end}.select{|t| !t.options[:polymorphic] && association_includes.include?(t.name.to_sym) }
62
+ # find all relations for this model, skip ones where the association klass is invalid, be cognizant of include/exclude arrays similar to dbfields
63
+ associations = model.reflect_on_all_associations(association_macro).select{|t| begin t.klass rescue next end}.select{|t| association_includes.include?(t.name.to_sym) }
64
+ # now include polymorphic relations whose association klass is invalid, but is correct
65
+ associations = associations + model.reflect_on_all_associations(association_macro).select{|t| t.options[:polymorphic]}.select{|t| association_includes.include?(t.name.to_sym) }
60
66
  # never show foreign keys for defined associations
61
- db_fields_never = foreign_keys ? [] : ( associations.map(&:association_foreign_key) + associations.map(&:options).select{|v| v.key?(:foreign_key) }.map {|x| x[:foreign_key]} ).uniq.map(&:to_sym)
67
+ db_fields_never = foreign_keys ? [] : ( model.reflect_on_all_associations.map(&:association_foreign_key) + model.reflect_on_all_associations.map(&:options).select{|v| v.key?(:foreign_key) }.map {|x| x[:foreign_key]} ).uniq.map(&:to_sym)
62
68
 
63
69
  # figure out which database fields we are exposing
64
70
  allowed_attributes = allowed_attributes.count > 0 ? allowed_attributes.map(&:to_sym) : associations.map(&:name) + columns.keys.map(&:to_sym)
65
71
  allowed_associations = (associations.map(&:name) - excluded_attributes - db_fields_never) & allowed_attributes
66
72
  db_fields = (columns.keys.map(&:to_sym) - excluded_attributes - db_fields_never) & allowed_attributes
67
73
  associations = associations.select{|m| allowed_associations.include?(m.name)}
68
-
74
+ enums = (Rails.version.split(".").first.to_i >= 4 && Rails.version.split(".").second.to_i >= 1) || (Rails.version.split(".").first.to_i >= 5) ? model.defined_enums.keys : []
75
+ enum_values = (Rails.version.split(".").first.to_i >= 4 && Rails.version.split(".").second.to_i >= 1) || (Rails.version.split(".").first.to_i >= 5) ? model.defined_enums : []
76
+
69
77
  ret_type = GraphQL::InputObjectType.define do
70
78
  #ensure type name is unique so it does not collide with known types
71
79
  name typename
72
80
  description "an input interface for the #{name} ActiveRecord model"
73
- # create GraphQL fields for each exposed database field
74
- db_fields.select{|s| (primary_keys && s.to_sym == :id)}.each do |f|
75
- argument f.to_sym, -> {GraphqlModelMapper::MapperType.convert_type(columns[f.to_s].type, columns[f.to_s].sql_type, (source_nulls ? columns[f.to_s].null : true))}
76
- end
77
- db_fields.select{|s| required_attributes.include?(s)}.each do |f|
78
- argument f.to_sym, -> {GraphqlModelMapper.convert_type(columns[f.to_s].type, columns[f.to_s].sql_type, false)}
79
- end
80
81
  # create GraphQL fields for each association
81
82
  associations.sort_by(&:name).each do |reflection|
82
83
  begin
83
- klass = reflection.klass
84
+ klass = reflection.klass if !reflection.options[:polymorphic]
84
85
  rescue
85
- GraphqlModelMapper.logger.warning("invalid relation #{reflection.name} specified on the #{name} model, the relation class does not exist")
86
+ GraphqlModelMapper.logger.info("invalid relation #{reflection.name} specified on the #{name} model, the relation class does not exist")
86
87
  next # most likely an invalid association without a class name, skip if other errors are encountered
87
88
  end
88
89
  if reflection.macro == :has_many
89
- argument reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_ar_object_with_params(klass.name, type_key: type_key, type_sub_key: type_sub_key).to_list_type} do
90
+ argument reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_ar_object_with_params(klass.name, type_sub_key: type_sub_key).to_list_type} do
90
91
  if GraphqlModelMapper.use_authorize
91
92
  authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
92
93
  model_name klass.name
93
- access_type type_key.to_s
94
+ access_type :read
94
95
  end
95
96
  end
96
97
  else
97
- argument reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_ar_object_with_params(klass.name, type_key: type_key, type_sub_key: type_sub_key)} do
98
- if GraphqlModelMapper.use_authorize
99
- authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
100
- model_name klass.name
101
- access_type type_key.to_s
102
- end
103
- end
98
+ if reflection.options[:polymorphic] #not currently supported as an input type
99
+ #if GraphqlModelMapper.scan_for_polymorphic_associations
100
+ # argument reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_polymorphic_type(reflection, name, type_sub_key: type_sub_key)}
101
+ #end
102
+ else
103
+ argument reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_ar_object_with_params(klass.name, type_sub_key: type_sub_key)} do
104
+ if GraphqlModelMapper.use_authorize
105
+ authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
106
+ model_name klass.name
107
+ access_type :read
108
+ end
109
+ end
110
+ end
104
111
  end
105
112
  end
106
113
 
107
- db_fields.reject{|s| (primary_keys && s.to_sym == :id) || required_attributes.include?(s)}.sort.each do |f|
108
- argument f.to_sym, -> {GraphqlModelMapper::MapperType.convert_type(columns[f.to_s].type, columns[f.to_s].sql_type, (source_nulls ? columns[f.to_s].null : true))}
114
+ # create GraphQL fields for each exposed database field
115
+ #if primary keys were requested
116
+ db_fields.select{|s| (primary_keys && s.to_sym == :id)}.each do |f|
117
+ argument "item_id".to_sym, -> {GraphqlModelMapper::MapperType.convert_type(columns[f.to_s].type, columns[f.to_s].sql_type, (source_nulls ? columns[f.to_s].null : true))}
118
+ end
119
+ argument :id, GraphQL::ID_TYPE
120
+ # force required_attributes to be non-null
121
+ db_fields.select{|s| required_attributes.include?(s)}.each do |f|
122
+ argument f.to_sym, -> {GraphqlModelMapper.convert_type(columns[f.to_s].type, columns[f.to_s].sql_type, false)}
123
+ end
124
+ #get the rest of the fields that are not primary keys or required fields
125
+ db_fields.reject{|s| (s.to_sym == :id) || required_attributes.include?(s)}.sort.each do |f|
126
+ custom_type_name = "#{name.classify}#{f.to_s.classify}AttributeInput"
127
+ if GraphqlModelMapper::CustomType.const_defined?(custom_type_name)
128
+ argument f.to_sym, GraphqlModelMapper::CustomType.const_get(custom_type_name)
129
+ else
130
+ if enums.include?(f.to_s)
131
+ argument f.to_sym, -> {GraphqlModelMapper::MapperType.get_enum_object(name, enum_values[f.to_s], f.to_s)}
132
+ else
133
+ argument f.to_sym, -> {GraphqlModelMapper::MapperType.convert_type(columns[f.to_s].type, columns[f.to_s].sql_type, (source_nulls ? columns[f.to_s].null : true))}
134
+ end
135
+ end
109
136
  end
110
137
  end if type_sub_key == :input_type
111
138
 
112
139
  ret_type = GraphQL::ObjectType.define do
113
140
  #ensure type name is unique so it does not collide with known types
114
141
  name typename
142
+ model_name name
143
+ authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
144
+ access_type :read.to_s
145
+
115
146
  description "an output interface for the #{name} ActiveRecord model"
116
- # create GraphQL fields for each exposed database field
117
- db_fields.select{|s| (primary_keys && s.to_sym == :id)}.each do |f|
118
- #puts "source null #{f} #{source_nulls ? columns[f.to_s].null : true}"
119
- field f.to_sym, -> {GraphqlModelMapper::MapperType.convert_type(columns[f.to_s].type, columns[f.to_s].sql_type, (source_nulls ? columns[f.to_s].null : true))}
120
- end
121
- db_fields.select{|s| required_attributes.include?(s)}.sort.each do |f|
122
- field f.to_sym, -> {GraphqlModelMapper::MapperType.convert_type(columns[f.to_s].type, columns[f.to_s].sql_type, false)}
123
- end
124
- # create GraphQL fields for each association
147
+ # create GraphQL fields for each association
125
148
  associations.sort_by(&:name).each do |reflection|
126
149
  begin
127
- klass = reflection.klass
150
+ klass = reflection.klass if !reflection.options[:polymorphic]
128
151
  rescue
129
- GraphqlModelMapper.logger.warning("invalid relation #{reflection.name} specified on the #{name} model, the relation class does not exist")
152
+ GraphqlModelMapper.logger.info("invalid relation #{reflection.name} specified on the #{name} model, the relation class does not exist")
130
153
  next # most likely an invalid association without a class name, skip if other errors are encountered
131
154
  end
132
155
  if reflection.macro == :has_many
133
- if [:deep].include?(GraphqlModelMapper.nesting_strategy) && type_key == :query
134
- connection reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_ar_object_with_params(klass.name, type_key: type_key, type_sub_key: type_sub_key).connection_type} do
156
+ if [:deep].include?(GraphqlModelMapper.nesting_strategy)
157
+ connection reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_ar_object_with_params(klass.name, type_sub_key: type_sub_key).connection_type}, property: reflection.name.to_sym, max_page_size: GraphqlModelMapper.max_page_size do
135
158
  if GraphqlModelMapper.use_authorize
136
159
  authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
137
160
  model_name klass.name
@@ -139,7 +162,7 @@ module GraphqlModelMapper
139
162
  end
140
163
  end
141
164
  else
142
- field reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_ar_object_with_params(klass.name, type_key: type_key, type_sub_key: type_sub_key).to_list_type} do
165
+ field reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_ar_object_with_params(klass.name, type_sub_key: type_sub_key).to_list_type}, property: reflection.name.to_sym do
143
166
  if GraphqlModelMapper.use_authorize
144
167
  authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
145
168
  model_name klass.name
@@ -148,163 +171,134 @@ module GraphqlModelMapper
148
171
  end
149
172
  end
150
173
  else
151
- field reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_ar_object_with_params(klass.name, type_key: type_key, type_sub_key: type_sub_key)} do
152
- if GraphqlModelMapper.use_authorize
153
- authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
154
- model_name klass.name
155
- access_type :read.to_s
156
- end
157
- end
174
+ if reflection.options[:polymorphic]
175
+ # if a union type is defined in custom types use it, otherwise generate a union type from the association definition (requires a table scan)
176
+ custom_type_name = "#{name.classify}#{reflection.name.to_s.classify}Union"
177
+ if GraphqlModelMapper::CustomType.const_defined?(custom_type_name)
178
+ field reflection.name.to_sym, -> {GraphqlModelMapper::CustomType.const_get(custom_type_name)}, property: reflection.name.to_sym
179
+ elsif GraphqlModelMapper.scan_for_polymorphic_associations
180
+ field reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_polymorphic_type(reflection, name)}, property: reflection.name.to_sym
181
+ end
182
+ else
183
+ field reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_ar_object_with_params(klass.name, type_sub_key: type_sub_key)}, property: reflection.name.to_sym do
184
+ if GraphqlModelMapper.use_authorize
185
+ authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
186
+ model_name klass.name
187
+ access_type :read.to_s
188
+ end
189
+ end
190
+ end
158
191
  end
159
192
  end
160
- db_fields.reject{|s| (primary_keys && s.to_sym == :id) || required_attributes.include?(s)}.sort.each do |f|
161
- #puts "source null #{f} #{source_nulls ? columns[f.to_s].null : true}"
162
- field f.to_sym, -> {GraphqlModelMapper::MapperType.convert_type(columns[f.to_s].type, columns[f.to_s].sql_type, (source_nulls ? columns[f.to_s].null : true))}
193
+ # create GraphQL fields for each exposed database field
194
+ # get primary keys if requested
195
+ implements GraphQL::Relay::Node.interface
196
+ global_id_field :id
197
+ db_fields.select{|s| (primary_keys && s.to_sym == :id)}.each do |f|
198
+ field "item_id".to_sym, -> {GraphqlModelMapper::MapperType.convert_type(columns[f.to_s].type, columns[f.to_s].sql_type, (source_nulls ? columns[f.to_s].null : true))}, property: :id do
199
+ resolve -> (obj, args, ctx) {
200
+ obj[:id]
201
+ }
202
+ end
203
+ end
204
+ # force required attributes to be non-null
205
+ db_fields.select{|s| required_attributes.include?(s)}.sort.each do |f|
206
+ field f.to_sym, -> {GraphqlModelMapper::MapperType.convert_type(columns[f.to_s].type, columns[f.to_s].sql_type, false)}, property: f.to_sym
207
+ end
208
+ # get the remaining fields and apply custom type if defined
209
+ db_fields.reject{|s| (s.to_sym == :id) || required_attributes.include?(s)}.sort.each do |f|
210
+ custom_type_name = "#{name.classify}#{f.to_s.classify}AttributeOutput"
211
+ if GraphqlModelMapper::CustomType.const_defined?(custom_type_name)
212
+ field f.to_sym, GraphqlModelMapper::CustomType.const_get(custom_type_name), property: f.to_sym
213
+ else
214
+ if enums.include?(f.to_s)
215
+ field f.to_sym, -> {GraphqlModelMapper::MapperType.get_enum_object(name, enum_values[f.to_s], f.to_s)}, property: f.to_sym
216
+ else
217
+ field f.to_sym, -> {GraphqlModelMapper::MapperType.convert_type(columns[f.to_s].type, columns[f.to_s].sql_type, (source_nulls ? columns[f.to_s].null : true))}, property: f.to_sym
218
+ end
219
+ end
163
220
  end
164
221
  end if type_sub_key == :output_type
165
222
  GraphqlModelMapper.set_constant(typename, ret_type) if !GraphqlModelMapper.defined_constant?(typename)
166
223
  ret_type
167
224
  end
168
225
 
169
- def self.get_type_params(name, type_key: nil, type_sub_key: nil)
226
+
227
+ def self.get_polymorphic_type(reflection, model_name)
228
+ type_name = "#{model_name}#{reflection.name.to_s.classify}UnionOutput"
229
+
230
+ return GraphqlModelMapper.get_constant(type_name) if GraphqlModelMapper.defined_constant?(type_name)
231
+
232
+ model = model_name.classify.constantize
233
+ has_with_deleted = model.public_methods.include?(:with_deleted)
234
+ parent_name = "#{reflection.name}_type"
235
+ parent_classes = has_with_deleted ? model.with_deleted.select("distinct #{parent_name}").map{|m| m.send("#{parent_name}".to_sym)} : model.select("distinct #{parent_name}").map{|m| m.send("#{parent_name}".to_sym)}
236
+ types = []
237
+ parent_classes.each do |p|
238
+ types << self.get_ar_object_with_params(p, type_sub_key: type_sub_key)
239
+ end
240
+
241
+ ret_type = GraphQL::UnionType.define do
242
+ name type_name
243
+ description "UnionType for polymorphic association #{reflection.name} on #{model_name}"
244
+ possible_types types
245
+ resolve_type ->(obj, ctx) {
246
+ GraphqlModelMapper::MapperType.get_ar_object_with_params(obj.class.name)
247
+ }
248
+ end
249
+ GraphqlModelMapper.set_constant(type_name, ret_type)
250
+ GraphqlModelMapper.get_constant(type_name)
251
+ end
252
+
253
+ def self.get_type_params(name, type_sub_key: :output_type)
170
254
  model = name.classify.constantize
171
255
  if model.public_methods.include?(:graphql_types)
172
256
  params = model.graphql_types
173
257
  else
174
258
  params = self.graphql_default_types
175
259
  end
176
- #puts params
177
- if !type_key.nil?
178
- if params.keys.include?(type_key.to_sym)
179
- params = params[type_key.to_sym]
180
- if !type_sub_key.nil?
181
- if params.keys.include?(type_sub_key.to_sym)
260
+ if !type_sub_key.nil?
261
+ if params.keys.include?(type_sub_key.to_sym)
182
262
  params = params[type_sub_key.to_sym]
183
- else
263
+ else
184
264
  params = nil
185
- end
186
265
  end
187
- else
266
+ else
188
267
  params = nil
189
- end
190
268
  end
269
+ params[:type_sub_key] = type_sub_key
191
270
  params
192
271
  end
193
272
 
194
273
  def self.graphql_default_types(
195
- query: {
196
- input_type: {
197
- required_attributes: [],
198
- excluded_attributes: [],
199
- allowed_attributes: [],
200
- foreign_keys: true,
201
- primary_keys: true,
202
- validation_keys: false,
203
- association_macro: nil,
204
- source_nulls: false,
205
- type_key: :query,
206
- type_sub_key: :input_type
207
- },
208
- output_type: {
209
- required_attributes: [],
210
- excluded_attributes: [],
211
- allowed_attributes: [],
212
- foreign_keys: true,
213
- primary_keys: true,
214
- validation_keys: false,
215
- association_macro: nil,
216
- source_nulls: false,
217
- type_key: :query,
218
- type_sub_key: :output_type
219
- }
220
- },
221
- update: {
222
- input_type: {
223
- required_attributes: [],
224
- excluded_attributes: [],
225
- allowed_attributes: [],
226
- foreign_keys: true,
227
- primary_keys: true,
228
- validation_keys: false,
229
- association_macro: nil,
230
- source_nulls: false,
231
- type_key: :update,
232
- type_sub_key: :input_type
233
- },
234
- output_type: {
235
- required_attributes: [],
236
- excluded_attributes: [],
237
- allowed_attributes: [],
238
- foreign_keys: true,
239
- primary_keys: true,
240
- validation_keys: false,
241
- association_macro: nil,
242
- source_nulls: true,
243
- type_key: :update,
244
- type_sub_key: :output_type
245
- }
246
- },
247
- delete: {
248
- input_type: {
249
- required_attributes: [:id],
250
- excluded_attributes: [],
251
- allowed_attributes: [:id],
252
- foreign_keys: false,
253
- primary_keys: true,
254
- validation_keys: false,
255
- association_macro: nil,
256
- source_nulls: false,
257
- type_key: :delete,
258
- type_sub_key: :input_type
259
- },
260
- output_type: {
261
- required_attributes: [],
262
- excluded_attributes: [],
263
- allowed_attributes: [],
264
- foreign_keys: false,
265
- primary_keys: true,
266
- validation_keys: false,
267
- association_macro: nil,
268
- source_nulls: true,
269
- type_key: :delete,
270
- type_sub_key: :output_type
271
- }
272
- },
273
- create: {
274
- input_type: {
275
- required_attributes: [],
276
- excluded_attributes: [],
277
- allowed_attributes: [],
278
- foreign_keys: true,
279
- primary_keys: false,
280
- validation_keys: false,
281
- association_macro: :has_many,
282
- source_nulls: false,
283
- type_key: :create,
284
- type_sub_key: :input_type
285
- },
286
- output_type: {
287
- required_attributes: [],
288
- excluded_attributes: [],
289
- allowed_attributes: [],
290
- foreign_keys: true,
291
- primary_keys: true,
292
- validation_keys: false,
293
- association_macro: nil,
294
- source_nulls: true,
295
- type_key: :create,
296
- type_sub_key: :output_type
297
- }
298
- })
299
- return GraphqlModelMapper.get_constant("GRAPHQL_DEFAULT_TYPES") if GraphqlModelMapper.const_defined?("GRAPHQL_DEFAULT_TYPES")
274
+ input_type: {
275
+ required_attributes: [],
276
+ excluded_attributes: [],
277
+ allowed_attributes: [],
278
+ foreign_keys: true,
279
+ primary_keys: true,
280
+ validation_keys: false,
281
+ association_macro: nil,
282
+ source_nulls: false
283
+ },
284
+ output_type: {
285
+ required_attributes: [],
286
+ excluded_attributes: [],
287
+ allowed_attributes: [],
288
+ foreign_keys: true,
289
+ primary_keys: true,
290
+ validation_keys: false,
291
+ association_macro: nil,
292
+ source_nulls: false
293
+ }
294
+ )
295
+ return GraphqlModelMapper::CustomType.const_get("GRAPHQL_DEFAULT_TYPES") if GraphqlModelMapper::CustomType.const_defined?("GRAPHQL_DEFAULT_TYPES")
300
296
 
301
297
  graphql_type = {}
302
- graphql_type[:query] = query
303
- graphql_type[:update] = update
304
- graphql_type[:delete] = delete
305
- graphql_type[:create] = create
306
-
307
- GraphqlModelMapper.set_constant("GRAPHQL_DEFAULT_TYPES", graphql_type)
298
+ graphql_type[:input_type] = input_type
299
+ graphql_type[:output_type] = output_type
300
+
301
+ GraphqlModelMapper::CustomType.const_set("GRAPHQL_DEFAULT_TYPES", graphql_type)
308
302
  graphql_type
309
303
  end
310
304
 
@@ -314,6 +308,20 @@ module GraphqlModelMapper
314
308
  model.reflect_on_all_associations.select{|p| validation_attributes.include?(p.name) }.map(&:foreign_key).map(&:to_sym) | validation_attributes & model.columns_hash.keys.map(&:to_sym)
315
309
  end
316
310
 
311
+ def self.get_enum_object(model_name, enum, enum_name)
312
+ enum_values = enum.keys
313
+ type_name = "#{model_name.classify}#{enum_name.classify}Enum"
314
+ return GraphqlModelMapper.get_constant(type_name) if GraphqlModelMapper.defined_constant?(type_name)
315
+ ret_type = GraphQL::EnumType.define do
316
+ name type_name
317
+ description "generated GraphQL enum for ActiveRecord enum #{enum_name} on model #{model_name}"
318
+ enum_values.each do |v|
319
+ value(v.classify, "", value: v)
320
+ end
321
+ end
322
+ GraphqlModelMapper.set_constant(type_name, ret_type)
323
+ GraphqlModelMapper.get_constant(type_name)
324
+ end
317
325
  # convert a database type to a GraphQL type
318
326
  # @param db_type [Symbol] the type returned by columns_hash[column_name].type
319
327
  # @param db_sql_type [String] the sql_type returned by columns_hash[column_name].sql_type