graphql_model_mapper 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: edf76e62fad8e31f3b2e1572544f170a5c151051
4
- data.tar.gz: dee948ab9a1125e55e19b588d5ef4837dd576ea4
3
+ metadata.gz: 6cca41868fb3cd1793a2d0fc46d26a09ff391a26
4
+ data.tar.gz: 133c44e736acb0fb3ae1e0a336c7f76f47016d2b
5
5
  SHA512:
6
- metadata.gz: 5c930baf5c2c607361e43c43d377321a9f06278c927b3b8a486a2e3c8eb99c9c94dff808c00df050573979c2ac9aef7c07ac7549ab0496b32262ac4b3ada1052
7
- data.tar.gz: 8cd16e8b313baab6b7608a6e39b6f8dbb2edd04b3f5bf9c9ec90f6bb2890756935b66e2d8f5ef3e5c338262d5de38bf072ee47324a79993650778527134ff109
6
+ metadata.gz: f695954eb400f758c0f31a48c76270b853357cc1e31cca9887a59e4cd06c23769167a9a16732b2f42f3c6fd4ca6bdfb198bda757cb2acf2e01a6bdb8b50bd5b4
7
+ data.tar.gz: 3caf36685531ae7a340438c2ef08840860cc200e0a200843725f8b638cb56d864ef24a271d4e4d39fb997dc4b1ce39b2d48b2786efe9a2d712a86b893a83d962
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ gem 'activerecord', '~>3.2.22.5', :require => 'active_record'
6
6
  gem 'activemodel', '~>3.2.22.5', :require => 'active_model'
7
7
  gem 'activesupport', '~>3.2.22.5', :require => 'active_support'
8
8
  gem 'graphql', '~>1.7.5'
9
+ gem 'graphql-errors', '~>0.1.0'
9
10
  git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
10
11
 
11
12
  # Specify your gem's dependencies in graphql_model_mapper.gemspec
@@ -1,11 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- graphql_model_mapper (0.1.0)
4
+ graphql_model_mapper (0.1.1)
5
5
  activemodel (>= 3.2.22.5)
6
6
  activerecord (>= 3.2.22.5)
7
7
  activesupport (>= 3.2.22.5)
8
8
  graphql (>= 1.7.5)
9
+ graphql-errors (>= 0.1.0)
9
10
  rails (>= 3.2.22.5)
10
11
 
11
12
  GEM
@@ -43,6 +44,8 @@ GEM
43
44
  concurrent-ruby (1.0.5)
44
45
  erubis (2.7.0)
45
46
  graphql (1.7.5)
47
+ graphql-errors (0.1.0)
48
+ graphql (>= 1.6.0, < 2)
46
49
  hike (1.2.3)
47
50
  i18n (0.9.1)
48
51
  concurrent-ruby (~> 1.0)
@@ -101,6 +104,7 @@ DEPENDENCIES
101
104
  activesupport (~> 3.2.22.5)
102
105
  bundler (~> 1.16)
103
106
  graphql (~> 1.7.5)
107
+ graphql-errors (~> 0.1.0)
104
108
  graphql_model_mapper!
105
109
  minitest (~> 5.0)
106
110
  rails (~> 3.2.22.5)
data/README.md CHANGED
@@ -44,9 +44,7 @@ The default input/output types generated for the model are based on the default
44
44
  primary_keys: true,
45
45
  validation_keys: false,
46
46
  association_macro: nil,
47
- source_nulls: false,
48
- type_key: :query,
49
- type_sub_key: :input_type
47
+ source_nulls: false
50
48
  },
51
49
  output_type: {
52
50
  required_attributes: [],
@@ -56,9 +54,7 @@ The default input/output types generated for the model are based on the default
56
54
  primary_keys: true,
57
55
  validation_keys: false,
58
56
  association_macro: nil,
59
- source_nulls: false,
60
- type_key: :query,
61
- type_sub_key: :output_type
57
+ source_nulls: false
62
58
  }
63
59
  }
64
60
 
@@ -188,13 +184,14 @@ Some other attributes that you can set on the macro functions in addition to the
188
184
  ## graphql_query
189
185
 
190
186
  description: # a short description of the query
191
- scope_methods: # scope methods available to be used in the query, these should not be parameterized and must be written so that they valid in the presence of other tables which may be included in the associations
192
- arguments: # a list of argument definitions to override the default arguments, if using your own arguments you will need to override the query resolver to act on those arguments, the default
187
+ scope_methods: # scope methods available to be used in the query, these can be parameterized (must not be named parameters, must be accepted as string arguments and coerced in the method if needed) and must be written so that they valid in the presence of other tables which may be included in the associations
188
+ arguments: # a list of argument definitions to override the default GraphQL arguments, if using your own arguments you will need to override the query resolver to act on those arguments
193
189
 
194
- Arguments should be a list of objects with the following attirbutes (*required)
190
+ Arguments should be a list of objects with the following attributes (*required)
191
+
195
192
  *name - displayed name of the attribute
196
193
  *type - GraphQL type of the attribute
197
- default - default value
194
+ default - default argument value
198
195
  authorization - authorization level for the attribute (if GraphqlModelMapper.use_authorize = true this authorization will be compared to the authorized ability for the user on the model to which this attribute applies)
199
196
 
200
197
  The default arguments handled by the default resolver and exposed on the query and delete mutations are:
@@ -209,6 +206,7 @@ The default arguments handled by the default resolver and exposed on the query a
209
206
  {:name=>:offset, :type=>GraphQL::INT_TYPE, :default=>nil}, # specifies an offset for the start of the records returned
210
207
  {:name=>:order, :type=>GraphQL::STRING_TYPE, :default=>nil, :authorization=>:manage}, # a string value that is passed to ActiveRecord query specifying the output order
211
208
  {:name=>:where, :type=>GraphQL::STRING_TYPE.to_list_type, :default=>nil, :authorization=>:manage}] # a string array for use in ActiveRecord query, can be a string or a query/value array to be used by the query ["model.id =? and model.date is not nul]", "1"]
209
+ {:name=>:scopes, :type=>ModelScopeList, :default=>nil, :authorization=>:manage}] # a list of ModelScopeEnums exposed on the graphql_query/graphql_delete macro, :allowed_scopes and their optional arguments string array
212
210
 
213
211
  ## graphql_delete
214
212
  description:
@@ -32,11 +32,13 @@ Gem::Specification.new do |spec|
32
32
 
33
33
  spec.required_ruby_version = '>= 2.1'
34
34
  spec.add_runtime_dependency "graphql", ['>= 1.7.5']
35
+ spec.add_runtime_dependency "graphql-errors", ['>= 0.1.0']
35
36
  spec.add_runtime_dependency "activesupport", ['>= 3.2.22.5']
36
37
  spec.add_runtime_dependency "activemodel", ['>= 3.2.22.5']
37
38
  spec.add_runtime_dependency "activerecord", ['>= 3.2.22.5']
38
39
  spec.add_runtime_dependency "rails", ['>= 3.2.22.5']
39
40
  spec.add_development_dependency "graphql", [">= 1.7.5"]
41
+ spec.add_development_dependency "graphql-errors", ['>= 0.1.0']
40
42
  spec.add_development_dependency "bundler", "~> 1.16"
41
43
  spec.add_development_dependency "rake", "~> 10.0"
42
44
  spec.add_development_dependency "minitest", "~> 5.0"
@@ -6,6 +6,7 @@ require "graphql_model_mapper/query"
6
6
  require "graphql_model_mapper/resolve"
7
7
  require "graphql_model_mapper/schema"
8
8
  require "graphql_model_mapper/utility"
9
+ require "graphql_model_mapper/encryption"
9
10
  require "graphql_model_mapper/version"
10
11
  require 'graphql_model_mapper/railtie' if defined?(Rails)
11
12
 
@@ -20,6 +21,9 @@ module GraphqlModelMapper
20
21
  mattr_accessor :scan_for_polymorphic_associations
21
22
  mattr_accessor :default_nodes_field
22
23
  mattr_accessor :bidirectional_pagination
24
+ mattr_accessor :handle_errors
25
+ mattr_accessor :secret_token
26
+
23
27
 
24
28
 
25
29
  @@type_case = :camelize
@@ -31,7 +35,8 @@ module GraphqlModelMapper
31
35
  @@mutation_resolve_wrapper = nil
32
36
  @@default_nodes_field = false
33
37
  @@bidirectional_pagination = false
34
-
38
+ @@handle_errors = true
39
+ @@secret_token = '3eb6db5a9026c547c72708438d496d942e976b252138db7e4e0ee5edd7539457d3ed0fa02ee5e7179420ce5290462018591adaf5f42adcf855da04877827def2'
35
40
 
36
41
 
37
42
  class << self
@@ -0,0 +1,20 @@
1
+ module GraphqlModelMapper
2
+ module Encryption
3
+ def self.key
4
+ Digest::SHA256.digest(ENV['GRAPHQL_SECRET_TOKEN'] || ENV['SECRET_TOKEN'] || GraphqlModelMapper.secret_token)
5
+ end
6
+
7
+ def self.aes(m,t)
8
+ (aes = OpenSSL::Cipher::Cipher.new('aes-256-cbc').send(m)).key = Digest::SHA256.digest(self.key)
9
+ aes.update(t) << aes.final
10
+ end
11
+
12
+ def self.encode(text)
13
+ Base64.encode64(aes(:encrypt, text)).strip
14
+ end
15
+
16
+ def self.decode(text)
17
+ aes(:decrypt, Base64.decode64(text))
18
+ end
19
+ end
20
+ end
@@ -154,7 +154,7 @@ module GraphqlModelMapper
154
154
  end
155
155
  if reflection.macro == :has_many
156
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
157
+ connection reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_connection_type(klass.name, GraphqlModelMapper::MapperType.get_ar_object_with_params(klass.name, type_sub_key: type_sub_key))}, property: reflection.name.to_sym, max_page_size: GraphqlModelMapper.max_page_size do
158
158
  if GraphqlModelMapper.use_authorize
159
159
  authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
160
160
  model_name klass.name
@@ -162,7 +162,7 @@ module GraphqlModelMapper
162
162
  end
163
163
  end
164
164
  else
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
165
+ field reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_list_type(klass.name, GraphqlModelMapper::MapperType.get_ar_object_with_params(klass.name, type_sub_key: type_sub_key))}, property: reflection.name.to_sym do
166
166
  if GraphqlModelMapper.use_authorize
167
167
  authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
168
168
  model_name klass.name
@@ -175,7 +175,7 @@ module GraphqlModelMapper
175
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
176
  custom_type_name = "#{name.classify}#{reflection.name.to_s.classify}Union"
177
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
178
+ field reflection.name.to_sym, -> {GraphqlModelMapper::CustomType.const_get(custom_type_name)}
179
179
  elsif GraphqlModelMapper.scan_for_polymorphic_associations
180
180
  field reflection.name.to_sym, -> {GraphqlModelMapper::MapperType.get_polymorphic_type(reflection, name)}, property: reflection.name.to_sym
181
181
  end
@@ -209,7 +209,7 @@ module GraphqlModelMapper
209
209
  db_fields.reject{|s| (s.to_sym == :id) || required_attributes.include?(s)}.sort.each do |f|
210
210
  custom_type_name = "#{name.classify}#{f.to_s.classify}AttributeOutput"
211
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
212
+ field f.to_sym, GraphqlModelMapper::CustomType.const_get(custom_type_name)
213
213
  else
214
214
  if enums.include?(f.to_s)
215
215
  field f.to_sym, -> {GraphqlModelMapper::MapperType.get_enum_object(name, enum_values[f.to_s], f.to_s)}, property: f.to_sym
@@ -235,7 +235,7 @@ module GraphqlModelMapper
235
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
236
  types = []
237
237
  parent_classes.each do |p|
238
- types << self.get_ar_object_with_params(p, type_sub_key: type_sub_key)
238
+ types << self.get_ar_object_with_params(p, type_sub_key: :output_type)
239
239
  end
240
240
 
241
241
  ret_type = GraphQL::UnionType.define do
@@ -270,6 +270,74 @@ module GraphqlModelMapper
270
270
  params
271
271
  end
272
272
 
273
+
274
+ def self.get_connection_type(model_name, output_type)
275
+ connection_type_name = "#{GraphqlModelMapper.get_type_case(GraphqlModelMapper.get_type_name(model_name))}Connection"
276
+ if GraphqlModelMapper.defined_constant?(connection_type_name)
277
+ connection_type = GraphqlModelMapper.get_constant(connection_type_name)
278
+ else
279
+ connection_type = output_type.define_connection do
280
+ name connection_type_name
281
+ field :total, hash_key: :total do
282
+ type types.Int
283
+ resolve ->(obj, args, ctx) {
284
+ obj.nodes.limit(nil).count
285
+ #obj.nodes.length
286
+ }
287
+ end
288
+ end
289
+ GraphqlModelMapper.set_constant(connection_type_name, connection_type)
290
+ end
291
+ return GraphqlModelMapper.get_constant(connection_type_name)
292
+ end
293
+
294
+ def self.get_list_type(model_name, output_type)
295
+ list_type_name = "#{GraphqlModelMapper.get_type_case(GraphqlModelMapper.get_type_name(model_name))}List"
296
+ if GraphqlModelMapper.defined_constant?(list_type_name)
297
+ list_type = GraphqlModelMapper.get_constant(list_type_name)
298
+ else
299
+ list_type = GraphQL::ObjectType.define do
300
+ name(list_type_name)
301
+
302
+ field :items, -> {output_type.to_list_type}, hash_key: :items do
303
+ argument :per_page, GraphQL::INT_TYPE
304
+ argument :page, GraphQL::INT_TYPE
305
+ resolve -> (obj,args,ctx){ GraphqlModelMapper::MapperType.resolve_list(obj,args,ctx) }
306
+ end
307
+ field :total, -> {GraphQL::INT_TYPE}, hash_key: :total do
308
+ resolve->(obj,args, ctx){
309
+ obj.count
310
+ }
311
+ end
312
+ end
313
+ GraphqlModelMapper.set_constant(list_type_name, list_type)
314
+ end
315
+ GraphqlModelMapper.get_constant(list_type_name)
316
+ end
317
+
318
+
319
+ def self.resolve_list(obj, args, ctx)
320
+ first_rec = nil
321
+ last_rec = nil
322
+ limit = GraphqlModelMapper.max_page_size.to_i
323
+
324
+ if args[:per_page]
325
+ per_page = args[:per_page].to_i
326
+ raise GraphQL::ExecutionError.new("per_page must be greater than 0") if per_page < 1
327
+ raise GraphQL::ExecutionError.new("you requested more items than the maximum page size #{limit}, please reduce your requested per_page entry") if per_page > limit
328
+ limit = [per_page,limit].min
329
+ end
330
+ if args[:page]
331
+ page = args[:page].to_i
332
+ raise GraphQL::ExecutionError.new("page must be greater than 0") if page < 1
333
+ max_page = (obj.count/limit).ceil
334
+ raise GraphQL::ExecutionError.new("you requested page #{page} which is greater than the maximum number of pages #{max_page}") if page > max_page
335
+ obj = obj.offset((page-1)*limit)
336
+ end
337
+ obj = obj.limit(limit)
338
+ obj
339
+ end
340
+
273
341
  def self.graphql_default_types(
274
342
  input_type: {
275
343
  required_attributes: [],
@@ -129,19 +129,31 @@ module GraphqlModelMapper
129
129
  allowed_scope_methods << s if (model.public_methods - model.instance_methods - Object.methods - ActiveRecord::Base.methods).include?(s)
130
130
  end
131
131
  if allowed_scope_methods.count > 0
132
- typename = GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(model.name)}Scope_Enum")
133
- if !GraphqlModelMapper.defined_constant?(typename)
132
+ scope_enum_type_name = GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(model.name)}Scope_Enum")
133
+ if !GraphqlModelMapper.defined_constant?(scope_enum_type_name)
134
134
  enum_type = GraphQL::EnumType.define do
135
- name typename
135
+ name scope_enum_type_name
136
136
  description "scope enum for #{GraphqlModelMapper.get_type_name(model.name)}"
137
137
  allowed_scope_methods.sort.each do |s|
138
138
  value(s, "")
139
139
  end
140
140
  end
141
- GraphqlModelMapper.set_constant typename, enum_type
141
+ GraphqlModelMapper.set_constant scope_enum_type_name, enum_type
142
142
  end
143
- default_arguments << {:name=>:scope, :type=>GraphqlModelMapper.get_constant(typename), :default=>nil, :authorization=>:manage}
144
- end
143
+ #default_arguments << {:name=>:scope, :type=>GraphqlModelMapper.get_constant(typename), :default=>nil, :authorization=>:manage}
144
+
145
+ scope_list_type_name = GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(model.name)}Scope_List")
146
+ if !GraphqlModelMapper.defined_constant?(scope_list_type_name)
147
+ scope_list_type = GraphQL::InputObjectType.define do
148
+ name scope_list_type_name
149
+ description "scope list for #{GraphqlModelMapper.get_type_name(model.name)}"
150
+ argument :scope, !GraphqlModelMapper.get_constant(scope_enum_type_name)
151
+ argument :arguments, GraphQL::STRING_TYPE.to_list_type
152
+ end
153
+ GraphqlModelMapper.set_constant scope_list_type_name, scope_list_type
154
+ end
155
+ default_arguments << {:name=>:scopes, :type=>GraphqlModelMapper.get_constant(scope_list_type_name).to_list_type , :default=>nil, :authorization=>:manage}
156
+ end
145
157
  end
146
158
  default_arguments
147
159
  end
@@ -48,19 +48,31 @@ module GraphqlModelMapper
48
48
  allowed_scope_methods << s if (model.public_methods - model.instance_methods - Object.methods - ActiveRecord::Base.methods).include?(s)
49
49
  end
50
50
  if allowed_scope_methods.count > 0
51
- typename = GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(model.name)}Scope_Enum")
52
- if !GraphqlModelMapper.defined_constant?(typename)
51
+ scope_enum_type_name = GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(model.name)}Scope_Enum")
52
+ if !GraphqlModelMapper.defined_constant?(scope_enum_type_name)
53
53
  enum_type = GraphQL::EnumType.define do
54
- name typename
54
+ name scope_enum_type_name
55
55
  description "scope enum for #{GraphqlModelMapper.get_type_name(model.name)}"
56
56
  allowed_scope_methods.sort.each do |s|
57
57
  value(s, "")
58
58
  end
59
59
  end
60
- GraphqlModelMapper.set_constant typename, enum_type
60
+ GraphqlModelMapper.set_constant scope_enum_type_name, enum_type
61
61
  end
62
- default_arguments << {:name=>:scope, :type=>GraphqlModelMapper.get_constant(typename), :default=>nil, :authorization=>:manage}
63
- end
62
+ #default_arguments << {:name=>:scope, :type=>GraphqlModelMapper.get_constant(typename), :default=>nil, :authorization=>:manage}
63
+
64
+ scope_list_type_name = GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(model.name)}Scope_List")
65
+ if !GraphqlModelMapper.defined_constant?(scope_list_type_name)
66
+ scope_list_type = GraphQL::InputObjectType.define do
67
+ name scope_list_type_name
68
+ description "scope list for #{GraphqlModelMapper.get_type_name(model.name)}"
69
+ argument :scope, !GraphqlModelMapper.get_constant(scope_enum_type_name)
70
+ argument :arguments, GraphQL::STRING_TYPE.to_list_type
71
+ end
72
+ GraphqlModelMapper.set_constant scope_list_type_name, scope_list_type
73
+ end
74
+ default_arguments << {:name=>:scopes, :type=>GraphqlModelMapper.get_constant(scope_list_type_name).to_list_type , :default=>nil, :authorization=>:manage}
75
+ end
64
76
  end
65
77
  default_arguments
66
78
  end
@@ -92,7 +104,6 @@ module GraphqlModelMapper
92
104
  =end
93
105
  =begin
94
106
  page_info_type_name = "FlatPageInfo"
95
-
96
107
  if GraphqlModelMapper.defined_constant?(page_info_type_name)
97
108
  page_info_type = GraphqlModelMapper.get_constant(page_info_type_name)
98
109
  else
@@ -108,74 +119,30 @@ module GraphqlModelMapper
108
119
  end
109
120
  =end
110
121
 
111
- if [:deep, :shallow].include?(GraphqlModelMapper.nesting_strategy)
112
- connection_type_name = "#{GraphqlModelMapper.get_type_case(GraphqlModelMapper.get_type_name(name))}ConnectionType"
113
- if GraphqlModelMapper.defined_constant?(connection_type_name)
114
- connection_type = GraphqlModelMapper.get_constant(connection_type_name)
115
- else
116
- connection_type = output_type.define_connection do
117
- name connection_type_name
118
- field :total, hash_key: :total do
119
- type types.Int
120
- resolve ->(obj, args, ctx) {
121
- obj.nodes.count
122
- }
123
- end
124
- end
125
- GraphqlModelMapper.set_constant(connection_type_name, connection_type)
126
- end
127
- end
128
122
 
129
- total_output_type_name = "#{GraphqlModelMapper.get_type_name(name)}QueryPayload"
130
- if GraphqlModelMapper.defined_constant?(total_output_type_name)
131
- total_output_type = GraphqlModelMapper.get_constant(total_output_type_name)
132
- else
123
+
124
+ total_output_type_name = "#{GraphqlModelMapper.get_type_name(name)}QueryPayload"
125
+ if GraphqlModelMapper.defined_constant?(total_output_type_name)
126
+ total_output_type = GraphqlModelMapper.get_constant(total_output_type_name)
127
+ else
128
+ if [:deep, :shallow].include?(GraphqlModelMapper.nesting_strategy)
129
+ connection_type = GraphqlModelMapper::MapperType.get_connection_type(name, output_type)
133
130
  total_output_type = GraphQL::ObjectType.define do
134
131
  name total_output_type_name
135
- if [:deep, :shallow].include?(GraphqlModelMapper.nesting_strategy)
136
- connection :items, -> {connection_type}, max_page_size: GraphqlModelMapper.max_page_size do
137
- resolve -> (obj, args, ctx) {
138
- limit = GraphqlModelMapper.max_page_size
139
- raise GraphQL::ExecutionError.new("you have exceeded the maximum requested page size #{limit}") if args[:first].to_i > limit || args[:last].to_i > limit
140
-
141
- obj
142
- }
143
- end
144
- else
145
- field :items, -> {output_type.to_list_type}, hash_key: :items do
146
- argument :per_page, GraphQL::INT_TYPE
147
- argument :page, GraphQL::INT_TYPE
148
- resolve->(obj, args, ctx){
149
- first_rec = nil
150
- last_rec = nil
151
- limit = GraphqlModelMapper.max_page_size.to_i
152
-
153
- if args[:per_page]
154
- per_page = args[:per_page].to_i
155
- raise GraphQL::ExecutionError.new("per_page must be greater than 0") if per_page < 1
156
- raise GraphQL::ExecutionError.new("you have exceeded the maximum requested page size #{limit}") if per_page > limit
157
- limit = [per_page,limit].min
158
- end
159
- if args[:page]
160
- page = args[:page].to_i
161
- raise GraphQL::ExecutionError.new("page must be greater than 0") if page < 1
162
- max_page = (obj.count/per_page).floor + 1
163
- raise GraphQL::ExecutionError.new("you requested page #{page} which is greater than the max number of pages #{max_page}") if page > max_page
164
- obj = obj.offset((page-1)*limit)
165
- end
166
- obj = obj.limit(limit)
167
- obj
168
- }
169
- end
170
- field :total, -> {GraphQL::INT_TYPE}, hash_key: :total do
171
- resolve->(obj,args, ctx){
172
- obj.limit(nil).count
173
- }
132
+ connection :items, -> {connection_type}, max_page_size: GraphqlModelMapper.max_page_size do
133
+ resolve -> (obj, args, ctx) {
134
+ limit = GraphqlModelMapper.max_page_size
135
+ raise GraphQL::ExecutionError.new("you have exceeded the maximum requested page size #{limit}") if args[:first].to_i > limit || args[:last].to_i > limit
136
+ obj
137
+ }
174
138
  end
175
- end
176
139
  end
177
- GraphqlModelMapper.set_constant(total_output_type_name, total_output_type)
140
+ else
141
+ total_output_type = GraphqlModelMapper::MapperType.get_list_type(name, output_type)
178
142
  end
143
+ GraphqlModelMapper.set_constant(total_output_type_name, total_output_type)
144
+ end
145
+
179
146
 
180
147
 
181
148
  ret_type = GraphQL::Field.define do
@@ -3,6 +3,7 @@ module GraphqlModelMapper
3
3
  def self.query_resolver(obj, args, ctx, name)
4
4
  #binding.pry
5
5
  obj_context = obj || name.classify.constantize
6
+ model = name.classify.constantize
6
7
  select_args = args[:select] || args
7
8
 
8
9
  if !GraphqlModelMapper.authorized?(ctx, obj_context.name, :query)
@@ -11,16 +12,40 @@ module GraphqlModelMapper
11
12
  classmethods = []
12
13
  scope_allowed = false
13
14
  with_deleted_allowed = false
15
+
16
+ if select_args[:scopes]
17
+ input_scopes = select_args[:scopes]
18
+ allowed_scopes = []
19
+ input_scopes.each do |s|
20
+ if model.public_methods.include?(s[:scope].to_sym)
21
+ allowed_scopes << {method: s[:scope], args: s[:arguments] }
22
+ else
23
+ next
24
+ end
25
+ end
26
+ errors = []
27
+ allowed_scopes.each do |a|
28
+ begin
29
+ obj_context = obj_context.send(a[:method.to_sym], *a[:args])
30
+ rescue => e
31
+ errors << "scope method: #{a[:method]} arguments: #{a[:args] || []} error: #{e.message}"
32
+ end
33
+ end
34
+ if errors.length > 0
35
+ raise GraphQL::ExecutionError.new(errors.join("; "))
36
+ end
37
+ end
14
38
  if select_args[:scope]
15
- classmethods = obj_context.methods - Object.methods
16
- scope_allowed = classmethods.include?(select_args[:scope].to_sym)
39
+ scope_allowed = model.public_methods.include?(select_args[:scope].to_sym)
17
40
  raise GraphQL::ExecutionError.new("error: invalid scope '#{select_args[:scope]}' specified, '#{select_args[:scope]}' method does not exist on '#{obj_context.class_name.classify}'") unless scope_allowed
18
41
  end
19
42
  if select_args[:with_deleted]
20
- classmethods = obj_context.methods - Object.methods
21
- with_deleted_allowed = classmethods.include?(:with_deleted)
43
+ with_deleted_allowed = model.public_methods.include?(:with_deleted)
22
44
  raise GraphQL::ExecutionError.new("error: invalid usage of 'with_deleted', 'with_deleted' method does not exist on '#{obj_context.class_name.classify}'") unless with_deleted_allowed
23
45
  end
46
+ if with_deleted_allowed && select_args[:with_deleted]
47
+ obj_context = obj_context.send(:with_deleted)
48
+ end
24
49
 
25
50
  implied_includes = self.get_implied_includes(obj_context.name.classify.constantize, ctx.ast_node)
26
51
  if !implied_includes.empty?
@@ -30,8 +55,12 @@ module GraphqlModelMapper
30
55
  end
31
56
  end
32
57
  if select_args[:id]
33
-
34
- type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(select_args[:id])
58
+ type_name, item_id = nil
59
+ begin
60
+ type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(GraphqlModelMapper::Encryption.decode(select_args[:id]))
61
+ rescue => e
62
+ raise GraphQL::ExecutionError.new("incorrect global id: unable to resolve id: #{e.message}")
63
+ end
35
64
  raise GraphQL::ExecutionError.new("incorrect global id: unable to resolve type for id:'#{select_args[:id]}'") if type_name.nil?
36
65
  model_name = GraphqlModelMapper.get_constant(type_name.upcase).metadata[:model_name].to_s.classify
37
66
  raise GraphQL::ExecutionError.new("incorrect global id '#{select_args[:id]}': expected global id for '#{name}', received global id for '#{model_name}'") if model_name != name
@@ -41,7 +70,7 @@ module GraphqlModelMapper
41
70
  finder_array = []
42
71
  errors = []
43
72
  select_args[:ids].each do |id|
44
- type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
73
+ type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(GraphqlModelMapper::Encryption.decode(id))
45
74
  if type_name.nil?
46
75
  errors << "incorrect global id: unable to resolve type for id:'#{id}'"
47
76
  next
@@ -66,9 +95,8 @@ module GraphqlModelMapper
66
95
  end
67
96
  if select_args[:where]
68
97
  obj_context = obj_context.where(select_args[:where])
69
- end
70
- if with_deleted_allowed
71
- obj_context = obj_context.with_deleted
98
+ else
99
+ obj_context = obj_context.where("1=1")
72
100
  end
73
101
  if scope_allowed
74
102
  obj_context = obj_context.send(select_args[:scope].to_sym)
@@ -76,15 +104,21 @@ module GraphqlModelMapper
76
104
  if select_args[:order]
77
105
  obj_context = obj_context.order(select_args[:order])
78
106
  end
107
+ #check for sql errors
108
+ begin
109
+ obj_context.eager_load(implied_includes).limit(0).to_a
110
+ rescue ActiveRecord::StatementInvalid => e
111
+ raise GraphQL::ExecutionError.new(e.message.sub(" AND (1=1)", "").sub(" LIMIT 0", ""))
112
+ end
79
113
  if select_args[:explain]
80
114
  obj_context = obj_context.limit(1)
81
115
  obj_context = obj_context.eager_load(implied_includes)
82
- raise GraphQL::ExecutionError.new(obj_context.explain.split("\n").first.sub("EXPLAIN for: ", "").sub(" LIMIT 1", !select_args[:limit].nil? && select_args[:limit].to_f > 0 ? "LIMIT #{select_args[:limit]}" : ""))
116
+ raise GraphQL::ExecutionError.new(obj_context.explain.split("\n").first.sub("EXPLAIN for: ", "").sub(" LIMIT 1", !select_args[:limit].nil? && select_args[:limit].to_f > 0 ? "LIMIT #{select_args[:limit]}" : "").sub(" AND (1=1)","").sub(" WHERE (1=1)",""))
83
117
  end
84
118
  #if select_args[:limit].nil?
85
- # obj_context = obj_context.limit(100)
119
+ # obj_context = obj_context.limit(GraphqlModelMapper.max_page_size+1)
86
120
  #end
87
- obj_context = obj_context.where("1=1")
121
+
88
122
  obj_context
89
123
  end
90
124
 
@@ -105,7 +139,7 @@ module GraphqlModelMapper
105
139
  rescue => e
106
140
  raise e #GraphQL::ExecutionError.new("error: delete")
107
141
  end
108
- if model.methods.include?(:with_deleted)
142
+ if model.public_methods.include?(:with_deleted)
109
143
  items.with_deleted
110
144
  else
111
145
  items
@@ -141,6 +175,10 @@ module GraphqlModelMapper
141
175
  selection.name == 'nodes'
142
176
  end
143
177
 
178
+ def self.using_items_pagination?(selection)
179
+ selection.name == 'items'
180
+ end
181
+
144
182
  def self.has_reflection_with_name?(class_name, selection_name)
145
183
  class_name.reflect_on_all_associations.select{|m|m.name == selection_name.to_sym}.present?
146
184
  end
@@ -169,6 +207,11 @@ module GraphqlModelMapper
169
207
  next
170
208
  end
171
209
 
210
+ if using_items_pagination?(selection)
211
+ get_implied_includes(class_name, selection, dependencies)
212
+ next
213
+ end
214
+
172
215
  if using_is_items_collection?(selection)
173
216
  get_implied_includes(class_name, selection, dependencies)
174
217
  next
@@ -275,10 +318,7 @@ module GraphqlModelMapper
275
318
  end
276
319
 
277
320
  def call(obj, args, ctx)
278
- begin
279
321
  @resolve_func.call(obj, args, ctx)
280
-
281
- end
282
322
  end
283
323
  end
284
324
  end
@@ -1,5 +1,5 @@
1
1
  module GraphqlModelMapper
2
- def self.Schema(log_query_depth: false, log_query_complexity: false, use_backtrace: false, use_authorize: false, nesting_strategy: :deep, type_case: :camelize, max_page_size: 100, scan_for_polymorphic_associations: false, mutation_resolve_wrapper: nil, query_resolve_wrapper: nil, bidirectional_pagination: false, default_nodes_field: false)
2
+ def self.Schema(log_query_depth: false, log_query_complexity: false, use_backtrace: false, use_authorize: false, nesting_strategy: :deep, type_case: :camelize, max_page_size: 100, scan_for_polymorphic_associations: false, mutation_resolve_wrapper: nil, query_resolve_wrapper: nil, bidirectional_pagination: false, default_nodes_field: false, handle_errors: false, secret_token: nil)
3
3
 
4
4
  return GraphqlModelMapper.get_constant("GraphqlModelMapperSchema".upcase) if GraphqlModelMapper.defined_constant?("GraphqlModelMapperSchema".upcase)
5
5
  GraphqlModelMapper.use_authorize = use_authorize
@@ -9,7 +9,8 @@ module GraphqlModelMapper
9
9
  GraphqlModelMapper.scan_for_polymorphic_associations = scan_for_polymorphic_associations
10
10
  GraphqlModelMapper.default_nodes_field = default_nodes_field
11
11
  GraphqlModelMapper.bidirectional_pagination = bidirectional_pagination
12
-
12
+ GraphqlModelMapper.handle_errors = handle_errors
13
+
13
14
  if query_resolve_wrapper && query_resolve_wrapper < GraphqlModelMapper::Resolve::ResolveWrapper
14
15
  GraphqlModelMapper.query_resolve_wrapper = query_resolve_wrapper
15
16
  else
@@ -22,6 +23,10 @@ module GraphqlModelMapper
22
23
  GraphqlModelMapper.mutation_resolve_wrapper = GraphqlModelMapper::Resolve::ResolveWrapper
23
24
  end
24
25
 
26
+ if secret_token
27
+ GraphqlModelMapper.secret_token = secret_token
28
+ end
29
+
25
30
 
26
31
  GraphQL::Relay::ConnectionType.bidirectional_pagination = GraphqlModelMapper.bidirectional_pagination
27
32
  GraphQL::Relay::ConnectionType.default_nodes_field = GraphqlModelMapper.default_nodes_field
@@ -50,11 +55,16 @@ module GraphqlModelMapper
50
55
 
51
56
  # Create UUIDs by joining the type name & ID, then base64-encoding it
52
57
  id_from_object ->(object, type_definition, context) {
53
- GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
58
+ GraphqlModelMapper::Encryption.encode(GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id))
54
59
  }
55
60
 
56
61
  object_from_id ->(id, context) {
57
- type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
62
+ type_name, item_id = nil
63
+ begin
64
+ type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(GraphqlModelMapper::Encryption.decode(id))
65
+ rescue => e
66
+ raise GraphQL::ExecutionError.new("incorrect global id: unable to resolve id: #{e.message}")
67
+ end
58
68
 
59
69
  type = GraphqlModelMapper.get_constant(type_name.upcase)
60
70
  raise GraphQL::ExecutionError.new("unknown type for id: #{id}") if type.nil?
@@ -65,17 +75,6 @@ module GraphqlModelMapper
65
75
 
66
76
  raise GraphQL::ExecutionError.new("unauthorized access for id: #{id}") if !authorized_proc.call(context, model_name, access_type)
67
77
  model = model_name.to_s.classify.constantize
68
- =begin
69
- args = {
70
- item_id: item_id
71
- }
72
- resolver = -> (obj, args, ctx) {
73
- items = GraphqlModelMapper::Resolve.query_resolver(obj, args, ctx, nil)
74
- }
75
- resolve = GraphqlModelMapper::Query.get_resolver(resolver)
76
- resolve.call(model, args, context)
77
- =end
78
-
79
78
  model.unscoped.find(item_id)
80
79
  }
81
80
  end
@@ -83,6 +82,27 @@ module GraphqlModelMapper
83
82
 
84
83
  schema.query_analyzers << GraphQL::Analysis::QueryDepth.new { |query, depth| Rails.logger.info("[******GraphqlModelMapper Query Depth] #{depth}") } if log_query_depth
85
84
  schema.query_analyzers << GraphQL::Analysis::QueryComplexity.new { |query, complexity| Rails.logger.info("[******GraphqlModelMapper Query Complexity] #{complexity}")} if log_query_complexity
85
+ GraphQL::Errors.configure(schema) do
86
+ rescue_from ActiveRecord::RecordNotFound do |exception|
87
+ nil
88
+ end
89
+
90
+ rescue_from ActiveRecord::StatementInvalid do |exception|
91
+ GraphQL::ExecutionError.new(exception.message)
92
+ end
93
+
94
+ rescue_from ActiveRecord::RecordInvalid do |exception|
95
+ GraphQL::ExecutionError.new(exception.record.errors.full_messages.join("\n"))
96
+ end
97
+
98
+ rescue_from StandardError do |exception|
99
+ GraphQL::ExecutionError.new(exception.message)
100
+ end
101
+
102
+ rescue_from do |exception|
103
+ GraphQL::ExecutionError.new(exception.message)
104
+ end
105
+ end if GraphqlModelMapper.handle_errors && GraphQL.const_defined?("Errors")
86
106
 
87
107
  GraphqlModelMapper.set_constant("GraphqlModelMapperSchema".upcase, schema)
88
108
  GraphqlModelMapper.get_constant("GraphqlModelMapperSchema".upcase)
@@ -19,6 +19,10 @@ module GraphqlModelMapper
19
19
  fields
20
20
  end
21
21
 
22
+ def self.generate_secret_token
23
+ "secret_token: '#{SecureRandom.hex(64)}'"
24
+ end
25
+
22
26
  def self.schema_mutations
23
27
  fields = []
24
28
  GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_create)}.each { |t|
@@ -1,3 +1,3 @@
1
1
  module GraphqlModelMapper
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql_model_mapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gene Black
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-11-24 00:00:00.000000000 Z
11
+ date: 2017-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 1.7.5
27
+ - !ruby/object:Gem::Dependency
28
+ name: graphql-errors
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: activesupport
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +108,20 @@ dependencies:
94
108
  - - ">="
95
109
  - !ruby/object:Gem::Version
96
110
  version: 1.7.5
111
+ - !ruby/object:Gem::Dependency
112
+ name: graphql-errors
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: 0.1.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: 0.1.0
97
125
  - !ruby/object:Gem::Dependency
98
126
  name: bundler
99
127
  requirement: !ruby/object:Gem::Requirement
@@ -157,6 +185,7 @@ files:
157
185
  - graphql_model_mapper.gemspec
158
186
  - lib/graphql_model_mapper.rb
159
187
  - lib/graphql_model_mapper/custom_type.rb
188
+ - lib/graphql_model_mapper/encryption.rb
160
189
  - lib/graphql_model_mapper/mapper_type.rb
161
190
  - lib/graphql_model_mapper/mutation.rb
162
191
  - lib/graphql_model_mapper/query.rb