graphql_model_mapper 0.0.6 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
data/lib/graphql_model_mapper.rb
CHANGED
@@ -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(
|
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,
|
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
|
-
|
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
|
-
|
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
|
@@ -2,29 +2,32 @@ module GraphqlModelMapper
|
|
2
2
|
module MapperType
|
3
3
|
def self.graphql_types(
|
4
4
|
name: self.name,
|
5
|
-
|
6
|
-
|
7
|
-
delete: {},
|
8
|
-
create: {}
|
5
|
+
input_type: {},
|
6
|
+
output_type: {}
|
9
7
|
)
|
10
|
-
|
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[:
|
16
|
-
graphql_type[:
|
17
|
-
graphql_type[:
|
18
|
-
graphql_type[:
|
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.
|
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.
|
27
|
-
|
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,
|
59
|
-
associations = model.reflect_on_all_associations(association_macro).select{|t| begin t.klass rescue next end}.select{|t|
|
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 ? [] : (
|
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.
|
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,
|
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
|
94
|
+
access_type :read
|
94
95
|
end
|
95
96
|
end
|
96
97
|
else
|
97
|
-
|
98
|
-
if GraphqlModelMapper.
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
-
|
108
|
-
|
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
|
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.
|
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)
|
134
|
-
connection reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_ar_object_with_params(klass.name,
|
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,
|
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
|
-
|
152
|
-
if
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
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
|
-
|
161
|
-
|
162
|
-
|
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
|
-
|
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
|
-
|
177
|
-
|
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
|
-
|
263
|
+
else
|
184
264
|
params = nil
|
185
|
-
end
|
186
265
|
end
|
187
|
-
|
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
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
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[:
|
303
|
-
graphql_type[:
|
304
|
-
|
305
|
-
graphql_type
|
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
|