graphql_model_mapper 0.0.2
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 +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
|