graphql_model_mapper 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +5 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +45 -0
- data/LICENSE.txt +21 -0
- data/README.md +416 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/graphql_model_mapper.gemspec +37 -0
- data/lib/graphql_model_mapper/mapper_type.rb +340 -0
- data/lib/graphql_model_mapper/mutation.rb +125 -0
- data/lib/graphql_model_mapper/query.rb +107 -0
- data/lib/graphql_model_mapper/railtie.rb +8 -0
- data/lib/graphql_model_mapper/resolve.rb +221 -0
- data/lib/graphql_model_mapper/schema.rb +110 -0
- data/lib/graphql_model_mapper/schema_types.rb +0 -0
- data/lib/graphql_model_mapper/utility.rb +0 -0
- data/lib/graphql_model_mapper/version.rb +3 -0
- data/lib/graphql_model_mapper.rb +228 -0
- metadata +122 -0
@@ -0,0 +1,340 @@
|
|
1
|
+
module GraphqlModelMapper
|
2
|
+
module MapperType
|
3
|
+
def self.graphql_types(
|
4
|
+
name: self.name,
|
5
|
+
query: {},
|
6
|
+
update: {},
|
7
|
+
delete: {},
|
8
|
+
create: {}
|
9
|
+
)
|
10
|
+
typesuffix = method(__method__).parameters.map { |arg| eval arg[1].to_s }.hash.abs.to_i.to_s
|
11
|
+
return GraphqlModelMapper.get_constant("#{name.upcase}#{typesuffix}_GRAPHQL_DEFAULT_TYPES") if GraphqlModelMapper.const_defined?("#{name.upcase}#{typesuffix}_GRAPHQL_DEFAULT_TYPES")
|
12
|
+
|
13
|
+
graphql_type = {}
|
14
|
+
graphql_type[:query] = query
|
15
|
+
graphql_type[:update] = update
|
16
|
+
graphql_type[:delete] = delete
|
17
|
+
graphql_type[:create] = create
|
18
|
+
merged_graphql_type = self.graphql_default_types.deep_merge(graphql_type)
|
19
|
+
GraphqlModelMapper.set_constant("#{name.upcase}#{typesuffix}_GRAPHQL_DEFAULT_TYPES", merged_graphql_type)
|
20
|
+
|
21
|
+
merged_graphql_type
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def self.get_ar_object_with_params(name, type_key: nil, type_sub_key: nil)
|
26
|
+
self.get_ar_object(name, self.get_type_params(name, type_key: type_key, type_sub_key: type_sub_key))
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.get_ar_object(name,
|
30
|
+
required_attributes: [],
|
31
|
+
excluded_attributes: [],
|
32
|
+
allowed_attributes: [],
|
33
|
+
foreign_keys: false,
|
34
|
+
primary_keys: false,
|
35
|
+
validation_keys: false,
|
36
|
+
association_macro: nil,
|
37
|
+
source_nulls: true,
|
38
|
+
type_key: nil,
|
39
|
+
type_sub_key: nil)
|
40
|
+
|
41
|
+
|
42
|
+
#typesuffix = method(__method__).parameters.map { |arg| eval arg[1].to_s }.hash.abs.to_i.to_s
|
43
|
+
typesuffix = "#{type_key.to_s.classify}#{GraphqlModelMapper.underscore(type_sub_key.to_s)}".camelize
|
44
|
+
typename = "#{GraphqlModelMapper.get_type_name(name)}#{typesuffix}"
|
45
|
+
|
46
|
+
return GraphqlModelMapper.get_constant(typename) if GraphqlModelMapper.defined_constant?(typename)
|
47
|
+
|
48
|
+
model = name.classify.constantize
|
49
|
+
|
50
|
+
required_attributes = required_attributes.map(&:to_sym) | (validation_keys ? self.model_validation_keys(name) : [])
|
51
|
+
|
52
|
+
columns = model.columns_hash
|
53
|
+
|
54
|
+
# figure out which association fields we are exposing
|
55
|
+
association_includes = (model.reflect_on_all_associations(association_macro).map(&:name)).map(&:to_sym) - excluded_attributes
|
56
|
+
|
57
|
+
# 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
|
58
|
+
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) }
|
59
|
+
# never show foreign keys for defined associations
|
60
|
+
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)
|
61
|
+
|
62
|
+
# figure out which database fields we are exposing
|
63
|
+
allowed_attributes = allowed_attributes.count > 0 ? allowed_attributes.map(&:to_sym) : associations.map(&:name) + columns.keys.map(&:to_sym)
|
64
|
+
allowed_associations = (associations.map(&:name) - excluded_attributes - db_fields_never) & allowed_attributes
|
65
|
+
db_fields = (columns.keys.map(&:to_sym) - excluded_attributes - db_fields_never) & allowed_attributes
|
66
|
+
associations = associations.select{|m| allowed_associations.include?(m.name)}
|
67
|
+
|
68
|
+
ret_type = GraphQL::InputObjectType.define do
|
69
|
+
#ensure type name is unique so it does not collide with known types
|
70
|
+
name typename
|
71
|
+
description "an input interface for the #{name} ActiveRecord model"
|
72
|
+
# create GraphQL fields for each exposed database field
|
73
|
+
db_fields.select{|s| (primary_keys && s.to_sym == :id)}.each do |f|
|
74
|
+
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))}
|
75
|
+
end
|
76
|
+
db_fields.select{|s| required_attributes.include?(s)}.each do |f|
|
77
|
+
argument f.to_sym, -> {GraphqlModelMapper.convert_type(columns[f.to_s].type, columns[f.to_s].sql_type, false)}
|
78
|
+
end
|
79
|
+
# create GraphQL fields for each association
|
80
|
+
associations.sort_by(&:name).each do |reflection|
|
81
|
+
begin
|
82
|
+
klass = reflection.klass
|
83
|
+
rescue
|
84
|
+
next # most likely an invalid association without a class name, skip if other errors are encountered
|
85
|
+
end
|
86
|
+
if reflection.macro == :has_many
|
87
|
+
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
|
88
|
+
if GraphqlModelMapper.use_authorize
|
89
|
+
authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
|
90
|
+
model_name klass.name
|
91
|
+
access_type type_key.to_s
|
92
|
+
end
|
93
|
+
end
|
94
|
+
else
|
95
|
+
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
|
96
|
+
if GraphqlModelMapper.use_authorize
|
97
|
+
authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
|
98
|
+
model_name klass.name
|
99
|
+
access_type type_key.to_s
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
db_fields.reject{|s| (primary_keys && s.to_sym == :id) || required_attributes.include?(s)}.sort.each do |f|
|
106
|
+
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))}
|
107
|
+
end
|
108
|
+
end if type_sub_key == :input_type
|
109
|
+
|
110
|
+
ret_type = GraphQL::ObjectType.define do
|
111
|
+
#ensure type name is unique so it does not collide with known types
|
112
|
+
name typename
|
113
|
+
description "an output interface for the #{name} ActiveRecord model"
|
114
|
+
# create GraphQL fields for each exposed database field
|
115
|
+
db_fields.select{|s| (primary_keys && s.to_sym == :id)}.each do |f|
|
116
|
+
#puts "source null #{f} #{source_nulls ? columns[f.to_s].null : true}"
|
117
|
+
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))}
|
118
|
+
end
|
119
|
+
db_fields.select{|s| required_attributes.include?(s)}.sort.each do |f|
|
120
|
+
field f.to_sym, -> {GraphqlModelMapper::MapperType.convert_type(columns[f.to_s].type, columns[f.to_s].sql_type, false)}
|
121
|
+
end
|
122
|
+
# create GraphQL fields for each association
|
123
|
+
associations.sort_by(&:name).each do |reflection|
|
124
|
+
begin
|
125
|
+
klass = reflection.klass
|
126
|
+
rescue
|
127
|
+
next # most likely an invalid association without a class name, skip if other errors are encountered
|
128
|
+
end
|
129
|
+
if reflection.macro == :has_many
|
130
|
+
if [:deep].include?(GraphqlModelMapper.nesting_strategy) && type_key == :query
|
131
|
+
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
|
132
|
+
if GraphqlModelMapper.use_authorize
|
133
|
+
authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
|
134
|
+
model_name klass.name
|
135
|
+
access_type :read.to_s
|
136
|
+
end
|
137
|
+
end
|
138
|
+
else
|
139
|
+
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
|
140
|
+
if GraphqlModelMapper.use_authorize
|
141
|
+
authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
|
142
|
+
model_name klass.name
|
143
|
+
access_type :read.to_s
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
else
|
148
|
+
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
|
149
|
+
if GraphqlModelMapper.use_authorize
|
150
|
+
authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
|
151
|
+
model_name klass.name
|
152
|
+
access_type :read.to_s
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
db_fields.reject{|s| (primary_keys && s.to_sym == :id) || required_attributes.include?(s)}.sort.each do |f|
|
158
|
+
#puts "source null #{f} #{source_nulls ? columns[f.to_s].null : true}"
|
159
|
+
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))}
|
160
|
+
end
|
161
|
+
end if type_sub_key == :output_type
|
162
|
+
GraphqlModelMapper.set_constant(typename, ret_type) if !GraphqlModelMapper.defined_constant?(typename)
|
163
|
+
ret_type
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.get_type_params(name, type_key: nil, type_sub_key: nil)
|
167
|
+
model = name.classify.constantize
|
168
|
+
if model.public_methods.include?(:graphql_types)
|
169
|
+
params = model.graphql_types
|
170
|
+
else
|
171
|
+
params = self.graphql_default_types
|
172
|
+
end
|
173
|
+
#puts params
|
174
|
+
if !type_key.nil?
|
175
|
+
if params.keys.include?(type_key.to_sym)
|
176
|
+
params = params[type_key.to_sym]
|
177
|
+
if !type_sub_key.nil?
|
178
|
+
if params.keys.include?(type_sub_key.to_sym)
|
179
|
+
params = params[type_sub_key.to_sym]
|
180
|
+
else
|
181
|
+
params = nil
|
182
|
+
end
|
183
|
+
end
|
184
|
+
else
|
185
|
+
params = nil
|
186
|
+
end
|
187
|
+
end
|
188
|
+
params
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.graphql_default_types(
|
192
|
+
query: {
|
193
|
+
input_type: {
|
194
|
+
required_attributes: [],
|
195
|
+
excluded_attributes: [],
|
196
|
+
allowed_attributes: [],
|
197
|
+
foreign_keys: true,
|
198
|
+
primary_keys: true,
|
199
|
+
validation_keys: false,
|
200
|
+
association_macro: nil,
|
201
|
+
source_nulls: false,
|
202
|
+
type_key: :query,
|
203
|
+
type_sub_key: :input_type
|
204
|
+
},
|
205
|
+
output_type: {
|
206
|
+
required_attributes: [],
|
207
|
+
excluded_attributes: [],
|
208
|
+
allowed_attributes: [],
|
209
|
+
foreign_keys: true,
|
210
|
+
primary_keys: true,
|
211
|
+
validation_keys: false,
|
212
|
+
association_macro: nil,
|
213
|
+
source_nulls: false,
|
214
|
+
type_key: :query,
|
215
|
+
type_sub_key: :output_type
|
216
|
+
}
|
217
|
+
},
|
218
|
+
update: {
|
219
|
+
input_type: {
|
220
|
+
required_attributes: [],
|
221
|
+
excluded_attributes: [],
|
222
|
+
allowed_attributes: [],
|
223
|
+
foreign_keys: true,
|
224
|
+
primary_keys: true,
|
225
|
+
validation_keys: false,
|
226
|
+
association_macro: nil,
|
227
|
+
source_nulls: false,
|
228
|
+
type_key: :update,
|
229
|
+
type_sub_key: :input_type
|
230
|
+
},
|
231
|
+
output_type: {
|
232
|
+
required_attributes: [],
|
233
|
+
excluded_attributes: [],
|
234
|
+
allowed_attributes: [],
|
235
|
+
foreign_keys: true,
|
236
|
+
primary_keys: true,
|
237
|
+
validation_keys: false,
|
238
|
+
association_macro: nil,
|
239
|
+
source_nulls: true,
|
240
|
+
type_key: :update,
|
241
|
+
type_sub_key: :output_type
|
242
|
+
}
|
243
|
+
},
|
244
|
+
delete: {
|
245
|
+
input_type: {
|
246
|
+
required_attributes: [:id],
|
247
|
+
excluded_attributes: [],
|
248
|
+
allowed_attributes: [:id],
|
249
|
+
foreign_keys: false,
|
250
|
+
primary_keys: true,
|
251
|
+
validation_keys: false,
|
252
|
+
association_macro: nil,
|
253
|
+
source_nulls: false,
|
254
|
+
type_key: :delete,
|
255
|
+
type_sub_key: :input_type
|
256
|
+
},
|
257
|
+
output_type: {
|
258
|
+
required_attributes: [],
|
259
|
+
excluded_attributes: [],
|
260
|
+
allowed_attributes: [],
|
261
|
+
foreign_keys: false,
|
262
|
+
primary_keys: true,
|
263
|
+
validation_keys: false,
|
264
|
+
association_macro: nil,
|
265
|
+
source_nulls: true,
|
266
|
+
type_key: :delete,
|
267
|
+
type_sub_key: :output_type
|
268
|
+
}
|
269
|
+
},
|
270
|
+
create: {
|
271
|
+
input_type: {
|
272
|
+
required_attributes: [],
|
273
|
+
excluded_attributes: [],
|
274
|
+
allowed_attributes: [],
|
275
|
+
foreign_keys: true,
|
276
|
+
primary_keys: false,
|
277
|
+
validation_keys: false,
|
278
|
+
association_macro: :has_many,
|
279
|
+
source_nulls: false,
|
280
|
+
type_key: :create,
|
281
|
+
type_sub_key: :input_type
|
282
|
+
},
|
283
|
+
output_type: {
|
284
|
+
required_attributes: [],
|
285
|
+
excluded_attributes: [],
|
286
|
+
allowed_attributes: [],
|
287
|
+
foreign_keys: true,
|
288
|
+
primary_keys: true,
|
289
|
+
validation_keys: false,
|
290
|
+
association_macro: nil,
|
291
|
+
source_nulls: true,
|
292
|
+
type_key: :create,
|
293
|
+
type_sub_key: :output_type
|
294
|
+
}
|
295
|
+
})
|
296
|
+
return GraphqlModelMapper.get_constant("GRAPHQL_DEFAULT_TYPES") if GraphqlModelMapper.const_defined?("GRAPHQL_DEFAULT_TYPES")
|
297
|
+
|
298
|
+
graphql_type = {}
|
299
|
+
graphql_type[:query] = query
|
300
|
+
graphql_type[:update] = update
|
301
|
+
graphql_type[:delete] = delete
|
302
|
+
graphql_type[:create] = create
|
303
|
+
|
304
|
+
GraphqlModelMapper.set_constant("GRAPHQL_DEFAULT_TYPES", graphql_type)
|
305
|
+
graphql_type
|
306
|
+
end
|
307
|
+
|
308
|
+
def self.model_validation_keys(name)
|
309
|
+
model = name.classify.constantize
|
310
|
+
validation_attributes = model.validators.select{|m| m.is_a?(ActiveModel::Validations::PresenceValidator) && !m.options[:if]}.map(&:attributes).flatten
|
311
|
+
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)
|
312
|
+
end
|
313
|
+
|
314
|
+
# convert a database type to a GraphQL type
|
315
|
+
# @param db_type [Symbol] the type returned by columns_hash[column_name].type
|
316
|
+
# @param db_sql_type [String] the sql_type returned by columns_hash[column_name].sql_type
|
317
|
+
# @return [GraphQL::ScalarType] a GraphQL type
|
318
|
+
def self.convert_type db_type, db_sql_type="", nullable=true
|
319
|
+
# because we are outside of a GraphQL define block we cannot use the types helper
|
320
|
+
# we must refer directly to the built-in GraphQL scalar types
|
321
|
+
case db_type
|
322
|
+
when :integer
|
323
|
+
nullable ? GraphQL::INT_TYPE : !GraphQL::INT_TYPE
|
324
|
+
when :decimal, :float
|
325
|
+
nullable ? GraphQL::FLOAT_TYPE : !GraphQL::FLOAT_TYPE
|
326
|
+
when :boolean
|
327
|
+
nullable ? GraphQL::BOOLEAN_TYPE : !GraphQL::BOOLEAN_TYPE
|
328
|
+
when :date, :datetime
|
329
|
+
nullable ? GraphqlModelMapper::DATE_TYPE : !GraphqlModelMapper::DATE_TYPE
|
330
|
+
else
|
331
|
+
case db_sql_type.to_sym #these are strings not symbols
|
332
|
+
when :geometry, :multipolygon, :polygon
|
333
|
+
nullable ? GraphqlModelMapper::GEOMETRY_TYPE : !GraphqlModelMapper::GEOMETRY_TYPE
|
334
|
+
else
|
335
|
+
nullable ? GraphQL::STRING_TYPE : !GraphQL::STRING_TYPE
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module GraphqlModelMapper
|
2
|
+
module Mutation
|
3
|
+
def self.graphql_update(name: "",description:"",
|
4
|
+
resolver: nil)
|
5
|
+
|
6
|
+
|
7
|
+
input_type = GraphqlModelMapper::MapperType.get_ar_object_with_params(name, type_key: :update, type_sub_key: :input_type)
|
8
|
+
output_type = GraphqlModelMapper::MapperType.get_ar_object_with_params(name, type_key: :update, type_sub_key: :output_type)
|
9
|
+
|
10
|
+
self.get_mutation(name, description, "Update", resolver, input_type, output_type, name.downcase, "item")
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.graphql_delete(name: "", description:"",
|
14
|
+
resolver: nil,
|
15
|
+
arguments: [],
|
16
|
+
scope_methods: [])
|
17
|
+
|
18
|
+
input_type = GraphqlModelMapper::MapperType.get_ar_object_with_params(name, type_key: :delete, type_sub_key: :input_type)
|
19
|
+
output_type = GraphqlModelMapper::MapperType.get_ar_object_with_params(name, type_key: :delete, type_sub_key: :output_type).to_list_type
|
20
|
+
self.get_delete_mutation(name, description, "Delete", resolver, arguments, scope_methods, input_type, output_type)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.graphql_create(name: "", description:"",
|
24
|
+
resolver: nil)
|
25
|
+
|
26
|
+
input_type = GraphqlModelMapper::MapperType.get_ar_object_with_params(name, type_key: :create, type_sub_key: :input_type)
|
27
|
+
output_type = GraphqlModelMapper::MapperType.get_ar_object_with_params(name, type_key: :create, type_sub_key: :output_type)
|
28
|
+
|
29
|
+
self.get_mutation(name, description, "Create", resolver, input_type, output_type, name.downcase, "item")
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.get_mutation(name, description, operation_name, resolver, input_type, output_type, input_name, output_name)
|
33
|
+
mutation_type_name = GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(name)}#{operation_name}")
|
34
|
+
return GraphqlModelMapper.get_constant(mutation_type_name) if GraphqlModelMapper.defined_constant?(mutation_type_name)
|
35
|
+
mutation_type = GraphQL::Relay::Mutation.define do
|
36
|
+
name mutation_type_name
|
37
|
+
description description
|
38
|
+
input_field input_name.to_sym, -> {input_type}
|
39
|
+
return_field output_name.to_sym, -> {output_type}
|
40
|
+
|
41
|
+
resolve resolver
|
42
|
+
end
|
43
|
+
|
44
|
+
GraphqlModelMapper.set_constant(mutation_type_name, mutation_type.field)
|
45
|
+
GraphqlModelMapper.get_constant(mutation_type_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.get_delete_mutation(name, description, operation_name, resolver, arguments, scope_methods, input_type, output_type)
|
49
|
+
query_type_name = GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(name)}#{operation_name}")
|
50
|
+
return GraphqlModelMapper.get_constant(query_type_name) if GraphqlModelMapper.defined_constant?(query_type_name)
|
51
|
+
|
52
|
+
model = name.classify.constantize
|
53
|
+
|
54
|
+
default_arguments = self.get_default_select_arguments(model, scope_methods)
|
55
|
+
select_input_type_name = GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(name)}SelectInput")
|
56
|
+
if GraphqlModelMapper.defined_constant?(select_input_type_name)
|
57
|
+
query_input_object_type = GraphqlModelMapper.get_constant(select_input_type_name)
|
58
|
+
else
|
59
|
+
query_input_object_type = GraphQL::InputObjectType.define do
|
60
|
+
name select_input_type_name
|
61
|
+
default_arguments.each do |k|
|
62
|
+
argument k[:name].to_sym, k[:type], k[:description], default_value: k[:default]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
GraphqlModelMapper.set_constant(select_input_type_name, query_input_object_type)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
ret_type = GraphQL::Relay::Mutation.define do
|
70
|
+
name query_type_name
|
71
|
+
#return_field :item, output_object_type
|
72
|
+
return_field :items, output_type
|
73
|
+
return_field :total, -> {GraphQL::INT_TYPE}
|
74
|
+
|
75
|
+
#description description
|
76
|
+
#input_field "input".to_sym, -> {input_object_type}
|
77
|
+
input_field :select, -> {!query_input_object_type}
|
78
|
+
|
79
|
+
resolve resolver
|
80
|
+
end
|
81
|
+
GraphqlModelMapper.set_constant(query_type_name, ret_type.field)
|
82
|
+
GraphqlModelMapper.get_constant(query_type_name)
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.get_default_select_arguments(model, scope_methods)
|
86
|
+
default_arguments = [
|
87
|
+
{:name=>:explain, :type=>GraphQL::BOOLEAN_TYPE, :default=>nil},
|
88
|
+
{:name=>:id, :type=>GraphQL::INT_TYPE, :default=>nil},
|
89
|
+
{:name=>:ids, :type=>GraphQL::INT_TYPE.to_list_type, :default=>nil},
|
90
|
+
{:name=>:limit, :type=>GraphQL::INT_TYPE, :default=>50},
|
91
|
+
{:name=>:offset, :type=>GraphQL::INT_TYPE, :default=>nil},
|
92
|
+
{:name=>:order, :type=>GraphQL::STRING_TYPE, :default=>nil},
|
93
|
+
{:name=>:where, :type=>GraphQL::STRING_TYPE.to_list_type, :default=>nil}
|
94
|
+
]
|
95
|
+
|
96
|
+
scope_methods = scope_methods.map(&:to_sym)
|
97
|
+
#.select{|m| model.method(m.to_sym).arity == 0}
|
98
|
+
if (model.public_methods - model.instance_methods - Object.methods - ActiveRecord::Base.methods).include?(:with_deleted)
|
99
|
+
default_arguments << {:name=>:with_deleted, :type=>GraphQL::BOOLEAN_TYPE, :default=>false}
|
100
|
+
end
|
101
|
+
allowed_scope_methods = []
|
102
|
+
if scope_methods.count > 0
|
103
|
+
scope_methods.each do |s|
|
104
|
+
#.select{|m| model.method(m.to_sym).arity == 0}
|
105
|
+
allowed_scope_methods << s if (model.public_methods - model.instance_methods - Object.methods - ActiveRecord::Base.methods).include?(s)
|
106
|
+
end
|
107
|
+
if allowed_scope_methods.count > 0
|
108
|
+
typename = GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(model.name)}Scope_Enum")
|
109
|
+
if !GraphqlModelMapper.defined_constant?(typename)
|
110
|
+
enum_type = GraphQL::EnumType.define do
|
111
|
+
name typename
|
112
|
+
description "scope enum for #{GraphqlModelMapper.get_type_name(model.name)}"
|
113
|
+
allowed_scope_methods.sort.each do |s|
|
114
|
+
value(s, "")
|
115
|
+
end
|
116
|
+
end
|
117
|
+
GraphqlModelMapper.set_constant typename, enum_type
|
118
|
+
end
|
119
|
+
default_arguments << {:name=>:scope, :type=>GraphqlModelMapper.get_constant(typename), :default=>nil}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
default_arguments
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module GraphqlModelMapper
|
2
|
+
module Query
|
3
|
+
def self.graphql_query(name: "",
|
4
|
+
description: "",
|
5
|
+
resolver: nil,
|
6
|
+
arguments: [],
|
7
|
+
scope_methods: []
|
8
|
+
)
|
9
|
+
|
10
|
+
input_type = GraphqlModelMapper::MapperType.get_ar_object_with_params(name, type_key: :query, type_sub_key: :input_type)
|
11
|
+
output_type = GraphqlModelMapper::MapperType.get_ar_object_with_params(name, type_key: :query, type_sub_key: :output_type)
|
12
|
+
self.get_query(name, description, "Query", resolver, arguments, scope_methods, input_type, output_type)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.get_default_select_arguments(model, scope_methods)
|
16
|
+
default_arguments = [
|
17
|
+
{:name=>:explain, :type=>GraphQL::BOOLEAN_TYPE, :default=>nil},
|
18
|
+
{:name=>:id, :type=>GraphQL::INT_TYPE, :default=>nil},
|
19
|
+
{:name=>:ids, :type=>GraphQL::INT_TYPE.to_list_type, :default=>nil},
|
20
|
+
{:name=>:limit, :type=>GraphQL::INT_TYPE, :default=>50},
|
21
|
+
{:name=>:offset, :type=>GraphQL::INT_TYPE, :default=>nil},
|
22
|
+
{:name=>:order, :type=>GraphQL::STRING_TYPE, :default=>nil},
|
23
|
+
{:name=>:where, :type=>GraphQL::STRING_TYPE.to_list_type, :default=>nil}
|
24
|
+
]
|
25
|
+
|
26
|
+
scope_methods = scope_methods.map(&:to_sym)
|
27
|
+
#.select{|m| model.method(m.to_sym).arity == 0}
|
28
|
+
if (model.public_methods - model.instance_methods - Object.methods - ActiveRecord::Base.methods).include?(:with_deleted)
|
29
|
+
default_arguments << {:name=>:with_deleted, :type=>GraphQL::BOOLEAN_TYPE, :default=>false}
|
30
|
+
end
|
31
|
+
allowed_scope_methods = []
|
32
|
+
if scope_methods.count > 0
|
33
|
+
scope_methods.each do |s|
|
34
|
+
#.select{|m| model.method(m.to_sym).arity == 0}
|
35
|
+
allowed_scope_methods << s if (model.public_methods - model.instance_methods - Object.methods - ActiveRecord::Base.methods).include?(s)
|
36
|
+
end
|
37
|
+
if allowed_scope_methods.count > 0
|
38
|
+
typename = GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(model.name)}Scope_Enum")
|
39
|
+
if !GraphqlModelMapper.defined_constant?(typename)
|
40
|
+
enum_type = GraphQL::EnumType.define do
|
41
|
+
name typename
|
42
|
+
description "scope enum for #{GraphqlModelMapper.get_type_name(model.name)}"
|
43
|
+
allowed_scope_methods.sort.each do |s|
|
44
|
+
value(s, "")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
GraphqlModelMapper.set_constant typename, enum_type
|
48
|
+
end
|
49
|
+
default_arguments << {:name=>:scope, :type=>GraphqlModelMapper.get_constant(typename), :default=>nil}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
default_arguments
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.get_query(name, description, operation_name, resolver, arguments, scope_methods, input_type, output_type)
|
56
|
+
|
57
|
+
query_type_name = GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(name)}#{operation_name}")
|
58
|
+
return GraphqlModelMapper.get_constant(query_type_name) if GraphqlModelMapper.defined_constant?(query_type_name)
|
59
|
+
|
60
|
+
model = name.classify.constantize
|
61
|
+
|
62
|
+
default_arguments = self.get_default_select_arguments(model, scope_methods)
|
63
|
+
select_input_type_name = "#{GraphqlModelMapper.get_type_case(GraphqlModelMapper.get_type_name(name))}QueryInput"
|
64
|
+
if GraphqlModelMapper.defined_constant?(select_input_type_name)
|
65
|
+
select_input_type = GraphqlModelMapper.get_constant(select_input_type_name)
|
66
|
+
else
|
67
|
+
select_input_type = GraphQL::InputObjectType.define do
|
68
|
+
name select_input_type_name
|
69
|
+
default_arguments.each do |k|
|
70
|
+
argument k[:name].to_sym, k[:type], k[:description], default_value: k[:default]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
GraphqlModelMapper.set_constant(select_input_type_name, select_input_type)
|
74
|
+
end
|
75
|
+
|
76
|
+
total_output_type_name = "#{GraphqlModelMapper.get_type_name(name)}QueryPayload"
|
77
|
+
if GraphqlModelMapper.defined_constant?(total_output_type_name)
|
78
|
+
total_output_type = GraphqlModelMapper.get_constant(total_output_type_name)
|
79
|
+
else
|
80
|
+
total_output_type = GraphQL::ObjectType.define do
|
81
|
+
name total_output_type_name
|
82
|
+
if [:deep, :shallow].include?(GraphqlModelMapper.nesting_strategy)
|
83
|
+
connection :items, -> {output_type.connection_type}, hash_key: :items
|
84
|
+
else
|
85
|
+
field :items, -> {output_type.to_list_type}, hash_key: :items
|
86
|
+
end
|
87
|
+
field :total, -> {GraphQL::INT_TYPE}, hash_key: :total
|
88
|
+
end
|
89
|
+
GraphqlModelMapper.set_constant(total_output_type_name, total_output_type)
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
ret_type = GraphQL::Field.define do
|
94
|
+
name query_type_name
|
95
|
+
type total_output_type
|
96
|
+
#argument :select, -> {!select_input_type}
|
97
|
+
default_arguments.each do |k|
|
98
|
+
argument k[:name].to_sym, k[:type], k[:description], default_value: k[:default]
|
99
|
+
end
|
100
|
+
|
101
|
+
resolve resolver
|
102
|
+
end
|
103
|
+
GraphqlModelMapper.set_constant(query_type_name, ret_type)
|
104
|
+
GraphqlModelMapper.get_constant(query_type_name)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|