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
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
|