graphql-rails-api 0.5.0 → 0.6.0

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
  SHA256:
3
- metadata.gz: f6402207e57c8de721a10386de5110da04b1bfdacc4b5c81c86acae75ef45e65
4
- data.tar.gz: f06272fc40c35d3c7f27df5fcec19e6ed967296fa739559df0d90ab8b21e6847
3
+ metadata.gz: 066b640b12080a61dc163b4989543cb22cff0c0bc000d5c5f2881ef74a37656a
4
+ data.tar.gz: '08e978790a84b509ad77c8a0fb58c6d496d891c095092676289c1516fcb478d9'
5
5
  SHA512:
6
- metadata.gz: 403a79ef9705821684ed7d00283542db925d41ad6cfd40741c5d954d0cb9e0b682286745d01fc70ebf893ba002502133c56b58a21aa72e773e1b4119c96b3163
7
- data.tar.gz: e388d99bd961ca380843d8d65b33beba46249373c70ef44d3793797bd5e7a63799b51eb59a62a730b535a879babd1f54033a3ea6bc33d2e75ab12c1d1844ce47
6
+ metadata.gz: 68ab8d0cc279ca2edebdc20bf3b490b73e8badf3a9f7f0284ef9c3f85c4a1c56ff8f29f8d69d1f16eb7b97e2035e3223308d2f3b093ab5692c40ad560a42cc35
7
+ data.tar.gz: 44ffdd02ea1cf52065c29db18df694c518ca7b08a9a3264ff25895d702308127265dbce8319ea6e7ce5844df2aeb50f6f3d9c5cdcc4ac0e574675064ab71e58a
@@ -7,14 +7,13 @@ class GraphqlAllConnectionsGenerator < Rails::Generators::NamedBase
7
7
  end
8
8
  end
9
9
 
10
- private
11
-
12
10
  def generate_connection(dir, resource)
13
11
  File.write(
14
12
  "#{dir}/connection.rb",
15
13
  <<~STRING
16
14
  #{resource.pluralize.camelize}::Connection = #{resource.pluralize.camelize}::Type.define_connection do
17
15
  name '#{resource.camelize}Connection'
16
+
18
17
  field :total_count, types.Int do
19
18
  resolve ->(obj, _, _) { obj.nodes.count }
20
19
  end
@@ -27,17 +27,4 @@ class GraphqlBulkUpdateMutationsGenerator < Rails::Generators::NamedBase
27
27
  resource.pluralize.camelize
28
28
  end
29
29
 
30
- def write_at(file_name, line, data)
31
- open(file_name, 'r+') do |f|
32
- while (line -= 1).positive?
33
- f.readline
34
- end
35
- pos = f.pos
36
- rest = f.read
37
- f.seek pos
38
- f.write data
39
- f.write rest
40
- end
41
- end
42
-
43
30
  end
@@ -8,10 +8,44 @@ class GraphqlMutationsGenerator < Rails::Generators::NamedBase
8
8
  generate_create_mutation(dir, resource)
9
9
  generate_update_mutation(dir, resource)
10
10
  generate_destroy_mutation(dir, resource)
11
+ generate_bulk_create_mutation(dir, resource)
12
+ generate_bulk_update_mutation(dir, resource)
11
13
  end
12
14
 
13
15
  private
14
16
 
17
+ def generate_bulk_create_mutation(dir, resource)
18
+ File.write(
19
+ "#{dir}/bulk_create.rb",
20
+ <<~STRING
21
+ #{resource_class(resource)}::Mutations::BulkCreate = GraphQL::Field.define do
22
+ description 'creates some #{resource_class(resource).pluralize}'
23
+ type types[#{resource_class(resource)}::Type]
24
+
25
+ argument :#{resource}, types[#{resource_class(resource)}::Mutations::InputType]
26
+
27
+ resolve ApplicationService.call(:#{resource}, :bulk_create)
28
+ end
29
+ STRING
30
+ )
31
+ end
32
+
33
+ def generate_bulk_update_mutation(dir, resource)
34
+ File.write(
35
+ "#{dir}/bulk_update.rb",
36
+ <<~STRING
37
+ #{resource_class(resource)}::Mutations::BulkUpdate = GraphQL::Field.define do
38
+ description 'Updates some #{resource_class(resource).pluralize}'
39
+ type types[#{resource_class(resource)}::Type]
40
+
41
+ argument :#{resource}, types[#{resource_class(resource)}::Mutations::InputType]
42
+
43
+ resolve ApplicationService.call(:#{resource}, :bulk_update)
44
+ end
45
+ STRING
46
+ )
47
+ end
48
+
15
49
  def generate_create_mutation(dir, resource)
16
50
  File.write(
17
51
  "#{dir}/create.rb",
@@ -21,7 +21,7 @@ module GraphqlRailsApi
21
21
  write_controller
22
22
  if options.action_cable_subs?
23
23
  write_websocket_connection
24
- write_channel
24
+ write_online_users_channel
25
25
  end
26
26
  write_initializer
27
27
  write_require_application_rb
@@ -51,6 +51,7 @@ module GraphqlRailsApi
51
51
  'app/models/application_record.rb',
52
52
  lines_count,
53
53
  <<-STRING
54
+
54
55
  def self.visible_for(*)
55
56
  all
56
57
  end
@@ -59,6 +60,45 @@ module GraphqlRailsApi
59
60
  all
60
61
  end
61
62
 
63
+ STRING
64
+ )
65
+ return unless options.action_cable_subs?
66
+
67
+ lines_count = File.read('app/models/application_record.rb').lines.count
68
+ write_at(
69
+ 'app/models/application_record.rb',
70
+ lines_count,
71
+ <<-STRING
72
+ after_commit :notify_online_users
73
+
74
+ def notify_online_users
75
+ Redis.current.keys('#{Rails.application.class.parent_name.underscore}_subscribed_query_*').each_with_object({}) do |key, hash|
76
+ hash[
77
+ key.gsub('#{Rails.application.class.parent_name.underscore}_subscribed_query_', '')
78
+ ] = Redis.current.hgetall(key).each_with_object([]) do |(data, vars), array|
79
+ data = data.split('/////')
80
+ array << { query: data[0], store: data[1], variables: vars.blank? ? nil : JSON.parse(vars), scope: data[2] }
81
+ end
82
+ end.each do |user_id, user_queries_array|
83
+ user_queries_array.map { |user_hash| notify_user(user_id, user_hash) }
84
+ end
85
+ end
86
+
87
+ def notify_user(user_id, user_hash)
88
+ model_name = self.class.to_s.underscore
89
+ if !user_hash[:query].include?(model_name.singularize + '(id: $id') &&
90
+ !user_hash[:query].include?(' ' + model_name.pluralize)
91
+ return
92
+ end
93
+ return if user_hash[:query].include?(model_name + '(id: $id') && user_hash[:variables]['id'] != id
94
+
95
+ u = User.find_by(id: user_id)
96
+ return unless u
97
+
98
+ result = #{Rails.application.class.parent_name}ApiSchema.execute(user_hash[:query], context: { current_user: u }, variables: user_hash[:variables])
99
+ OnlineUsersChannel.broadcast_to(u, store: user_hash[:store], scope: user_hash[:scope], result: result['data'])
100
+ end
101
+
62
102
  STRING
63
103
  )
64
104
  end
@@ -98,9 +138,6 @@ module GraphqlRailsApi
98
138
  config = Graphql::Rails::Api::Config.instance
99
139
 
100
140
  config.id_type = #{options.pg_uuid? ? ':uuid' : ':id'} # :id or :uuid
101
-
102
- # Possibilites are :create, :update or :destroy
103
- config.basic_mutations = %i[create update destroy]
104
141
  STRING
105
142
  )
106
143
  end
@@ -125,58 +162,45 @@ module GraphqlRailsApi
125
162
  )
126
163
  end
127
164
 
128
- def write_channel
165
+ def write_online_users_channel
129
166
  File.write(
130
- 'app/channels/graphql_channel.rb',
167
+ 'app/channels/online_users_channel.rb',
131
168
  <<~STRING
132
- class GraphqlChannel < ApplicationCable::Channel
169
+ class OnlineUsersChannel < ApplicationCable::Channel
133
170
 
134
171
  def subscribed
135
- @subscription_ids = []
136
- end
137
-
138
- # see graphql-ruby from details
139
- def execute(data)
140
- query, context, variables, operation_name = options_for_execute(data)
141
- result = #{@app_name.camelize}Schema.execute(query: query, context: context,
142
- variables: variables, operation_name: operation_name)
143
- payload = { result: result.subscription? ? nil : result.to_h, more: result.subscription?,
144
- errors: result ? result.to_h[:errors] : nil }
145
- @subscription_ids << result.context[:subscription_id] if result.context[:subscription_id]
146
- transmit(payload)
172
+ stream_for(current_user)
173
+ Redis.current.hset('#{Rails.application.class.parent_name.underscore}_online_users', current_user.id, '1')
174
+ User.online.each do |user|
175
+ OnlineUsersChannel.broadcast_to(user, User.online_user_ids)
176
+ end
147
177
  end
148
178
 
149
- def unsubscribed
150
- @subscription_ids.each do |sid|
151
- #{@app_name.camelize}Schema.subscriptions.delete_subscription(sid)
152
- end
179
+ def subscribe_to_query(data)
180
+ Redis.current.hset(
181
+ '#{Rails.application.class.parent_name.underscore}_subscribed_query_' + current_user.id,
182
+ data['query'] + '/////' + data['store'] + '/////' + data['scope'],
183
+ data['variables']
184
+ )
153
185
  end
154
186
 
155
- def options_for_execute(data)
156
- query = data['query']
157
- variables = ensure_hash(data['variables'])
158
- operation_name = data['operationName']
159
- context = { current_user: current_user, channel: self }.
160
- merge(ensure_hash(data['context']).symbolize_keys). # ensure context is filled
161
- merge(variables) # include variables in context too
162
- [query, context, variables, operation_name]
187
+ def unsubscribe_to_query(data)
188
+ Redis.current.hdel(
189
+ '#{Rails.application.class.parent_name.underscore}_subscribed_query_' + current_user.id,
190
+ data['query'] + '/////' + data['store']
191
+ )
163
192
  end
164
193
 
165
- # Handle form data, JSON body, or a blank value
166
- def ensure_hash(ambiguous_param)
167
- case ambiguous_param
168
- when String
169
- ambiguous_param.present? ? ensure_hash(JSON.parse(ambiguous_param)) : {}
170
- when Hash, ActionController::Parameters
171
- ambiguous_param
172
- when nil
173
- {}
174
- else
175
- raise ArgumentError, 'Unexpected parameter: ' + ambiguous_param
194
+ def unsubscribed
195
+ Redis.current.hset('#{Rails.application.class.parent_name.underscore}_online_users', current_user.id, '0')
196
+ Redis.current.hdel('#{Rails.application.class.parent_name.underscore}_subscribed_query_' + current_user.id, current_user.id)
197
+ User.online.each do |user|
198
+ OnlineUsersChannel.broadcast_to(user, User.online_user_ids)
176
199
  end
177
200
  end
178
201
 
179
202
  end
203
+
180
204
  STRING
181
205
  )
182
206
  end
@@ -347,7 +371,7 @@ module GraphqlRailsApi
347
371
  attr_accessor :params, :object, :fields, :user
348
372
 
349
373
  def initialize(params: {}, object: nil, object_id: nil, user: nil, context: nil)
350
- @params = params.to_h.symbolize_keys
374
+ @params = params.is_a?(Array) ? params.map { |p| p.to_h.symbolize_keys } : params.to_h.symbolize_keys
351
375
  @context = context
352
376
  @object = object || (object_id && model.visible_for(user: user).find_by(id: object_id))
353
377
  @object_id = object_id
@@ -365,51 +389,76 @@ module GraphqlRailsApi
365
389
  end
366
390
 
367
391
  def index
368
- Graphql::HydrateQuery.new(model.all, @context, user: user).run
392
+ HydrateQuery.new(
393
+ model.all,
394
+ @context,
395
+ order_by: params[:order_by],
396
+ filter: params[:filter],
397
+ user: user
398
+ ).run
369
399
  end
370
400
 
371
401
  def show
372
- object = Graphql::HydrateQuery.new(model.all, @context, user: user, id: params[:id]).run
373
- return not_allowed if object.blank?
374
- object
402
+ return not_allowed if access_not_allowed
403
+
404
+ showed_resource = HydrateQuery.new(model.all, @context, user: user, id: object.id).run
405
+
406
+ return not_allowed if showed_resource.blank?
407
+
408
+ showed_resource
375
409
  end
376
410
 
377
411
  def create
378
- object = model.new(params.select { |p| model.new.respond_to?(p) })
379
- if object.save
380
- object
412
+ if user&.action?("can_create_#{singular_resource}")
413
+ created_resource = model.new(params.select { |p| model.new.respond_to?(p) })
414
+ return not_allowed if not_allowed_to_create_resource(created_resource)
415
+
416
+ created_resource.save ? created_resource : graphql_error(created_resource.errors.full_messages.join(', '))
417
+ elsif user&.action?("can_create_#{singular_resource}_with_verif")
418
+ Verification.create(action: 'create', model: model.to_s, params: params, user_id: user.id)
419
+ graphql_error("Pending verification for a #{singular_resource} creation")
381
420
  else
382
- graphql_error(object.errors.full_messages.join(', '))
421
+ not_allowed
383
422
  end
384
423
  end
385
424
 
386
- def destroy
387
- object = model.find_by(id: params[:id])
425
+ def update
388
426
  return not_allowed if write_not_allowed
389
- if object.destroy
390
- object
427
+
428
+ if user.action?("can_update_#{singular_resource}")
429
+ object.update_attributes(params) ? object : graphql_error(object.errors.full_messages.join(', '))
430
+ elsif user.action?("can_update_#{singular_resource}_with_verif")
431
+ create_update_verification
391
432
  else
392
- graphql_error(object.errors.full_messages.join(', '))
433
+ not_allowed
393
434
  end
394
435
  end
395
436
 
396
- def update
397
- return not_allowed if write_not_allowed
398
- if object.update_attributes(params)
399
- object
400
- else
401
- graphql_error(object.errors.full_messages.join(', '))
402
- end
437
+ def destroy
438
+ return not_allowed if write_not_allowed || !user.action?("can_delete_#{singular_resource}")
439
+
440
+ object.destroy ? object : graphql_error(object.errors.full_messages.join(', '))
403
441
  end
404
442
 
405
443
  private
406
444
 
445
+ def create_update_verification
446
+ Verification.create(
447
+ action: 'update', model: model.to_s, params: params.merge(id: object.id), user_id: user.id
448
+ )
449
+ graphql_error("Pending verification for a #{singular_resource} update")
450
+ end
451
+
407
452
  def write_not_allowed
408
- !model.visible_for(user: user).include?(object) if object
453
+ return true unless object
454
+
455
+ !model.writable_for(user: user).pluck(:id).include?(object.id)
409
456
  end
410
457
 
411
458
  def access_not_allowed
412
- !model.visible_for(user: user).include?(object) if object
459
+ return true unless object
460
+
461
+ !model.visible_for(user: user).pluck(:id).include?(object.id)
413
462
  end
414
463
 
415
464
  def not_allowed
@@ -432,8 +481,23 @@ module GraphqlRailsApi
432
481
  self.class.to_s.split(':').first.underscore
433
482
  end
434
483
 
484
+ def not_allowed_to_create_resource(created_resource)
485
+ params.select { |k, _| k.to_s.end_with?('_id') }.each do |belongs_relation, rel_id|
486
+ klass = created_resource.class.reflect_on_association(belongs_relation.to_s.gsub('_id', '')).klass
487
+ return true if rel_id.present? && !klass.visible_for(user: user).pluck(:id).include?(rel_id)
488
+ end
489
+
490
+ params.select { |k, _| k.to_s.end_with?('_ids') }.each do |many_relation, rel_ids|
491
+ klass = created_resource.class.reflect_on_association(many_relation.to_s.gsub('_ids', '').pluralize).klass
492
+ ids = klass.visible_for(user: user).pluck(:id)
493
+ rel_ids.each { |id| return true if id.present? && !ids.include?(id) }
494
+ end
495
+ false
496
+ end
497
+
435
498
  end
436
499
 
500
+
437
501
  STRING
438
502
  )
439
503
  end
@@ -1,12 +1,15 @@
1
1
  class GraphqlResourceGenerator < Rails::Generators::NamedBase
2
2
 
3
- %i[migration model mutations service graphql_input_type graphql_type propagation migrate].each do |opt|
3
+ %i[
4
+ migration model mutations service graphql_input_type
5
+ graphql_type propagation connection migrate
6
+ ].each do |opt|
4
7
  class_option(opt, type: :boolean, default: true)
5
8
  end
6
9
 
7
10
  TYPES_MAPPING = {
8
- 'id' => '!types.ID',
9
- 'uuid' => '!types.String',
11
+ 'id' => 'types.ID',
12
+ 'uuid' => 'types.String',
10
13
  'boolean' => 'types.Boolean',
11
14
  'float' => 'types.Float',
12
15
  'decimal' => 'types.Float',
@@ -16,6 +19,7 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
16
19
 
17
20
  def create_graphql_files
18
21
  return if args.blank?
22
+
19
23
  parse_args
20
24
 
21
25
  # Generate migration
@@ -27,6 +31,9 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
27
31
  # Graphql Type
28
32
  generate_graphql_type(@resource) if options.graphql_type?
29
33
 
34
+ # Graphql Connection
35
+ generate_graphql_connection(@resource) if options.connection?
36
+
30
37
  # Model
31
38
  generate_model(@resource) if options.model?
32
39
 
@@ -44,16 +51,16 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
44
51
  private
45
52
 
46
53
  def types_mapping(type)
47
- TYPES_MAPPING[type] || "types.String"
54
+ TYPES_MAPPING[type] || 'types.String'
48
55
  end
49
56
 
50
57
  def parse_args
51
58
  if Graphql::Rails::Api::Config.instance.id_type == :uuid
52
59
  @id_db_type = 'uuid'
53
- @id_type = '!types.String'
60
+ @id_type = 'types.String'
54
61
  else
55
62
  @id_db_type = 'integer'
56
- @id_type = '!types.ID'
63
+ @id_type = 'types.ID'
57
64
  end
58
65
 
59
66
  @resource = file_name.singularize
@@ -64,6 +71,7 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
64
71
 
65
72
  @args = args.each_with_object({}) do |f, hash|
66
73
  next if f.split(':').count != 2
74
+
67
75
  case f.split(':').first
68
76
  when 'belongs_to' then
69
77
  hash["#{f.split(':').last.singularize}_id"] = @id_db_type
@@ -110,6 +118,21 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
110
118
  generate_graphql_input_type(resource) if options.graphql_input_type?
111
119
  end
112
120
 
121
+ def generate_graphql_connection(resource)
122
+ File.write(
123
+ "#{graphql_resource_directory(resource)}/connection.rb",
124
+ <<~STRING
125
+ #{resource_class(resource)}::Connection = #{resource.pluralize.camelize}::Type.define_connection do
126
+ name '#{resource.camelize}Connection'
127
+
128
+ field :total_count, types.Int do
129
+ resolve ->(obj, _, _) { obj.nodes.count }
130
+ end
131
+ end
132
+ STRING
133
+ )
134
+ end
135
+
113
136
  def generate_graphql_input_type(resource)
114
137
  system("mkdir -p #{@mutations_directory}")
115
138
  File.write(
@@ -132,7 +155,7 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
132
155
  <<~STRING
133
156
  #{resource_class(resource)}::Type = GraphQL::ObjectType.define do
134
157
  name '#{resource_class(resource).singularize}'
135
- field :id, #{@id_type}
158
+ field :id, !#{@id_type}
136
159
  field :created_at, types.String
137
160
  field :updated_at, types.String
138
161
  #{map_types(input_type: false)}
@@ -148,14 +171,16 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
148
171
  def add_has_many_fields_to_type(field, resource)
149
172
  file_name = "app/graphql/#{field.pluralize}/type.rb"
150
173
  if File.read(file_name).include?("field :#{resource.singularize}_ids") ||
151
- File.read(file_name).include?("field :#{resource.pluralize}")
174
+ File.read(file_name).include?("field :#{resource.pluralize}") ||
175
+ File.read(file_name).include?("connection :#{resource.pluralize}_connection")
152
176
  return
153
177
  end
154
178
  write_at(
155
179
  file_name, 4,
156
180
  <<-STRING
157
- field :#{resource.singularize}_ids, !types[#{@id_type}]
158
- field :#{resource.pluralize}, !types[!#{resource.pluralize.camelize}::Type]
181
+ field :#{resource.singularize}_ids, types[#{@id_type}]
182
+ field :#{resource.pluralize}, types[#{resource.pluralize.camelize}::Type]
183
+ connection :#{resource.pluralize}_connection, #{resource.pluralize.camelize}::Connection
159
184
  STRING
160
185
  )
161
186
 
@@ -167,7 +192,7 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
167
192
  write_at(
168
193
  input_type_file_name, 4,
169
194
  <<-STRING
170
- argument :#{resource.singularize}_ids, !types[#{@id_type}]
195
+ argument :#{resource.singularize}_ids, types[#{@id_type}]
171
196
  STRING
172
197
  )
173
198
 
@@ -179,11 +204,12 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
179
204
  File.read(file_name).include?("field :#{field.singularize}")
180
205
  return
181
206
  end
207
+
182
208
  write_at(
183
209
  file_name, 4,
184
210
  <<-STRING
185
211
  field :#{field.singularize}_id, #{@id_type}
186
- field :#{field.singularize}, !#{field.pluralize.camelize}::Type
212
+ field :#{field.singularize}, #{field.pluralize.camelize}::Type
187
213
  STRING
188
214
  )
189
215
  input_type_file_name = "app/graphql/#{resource.pluralize}/mutations/input_type.rb"
@@ -191,6 +217,7 @@ class GraphqlResourceGenerator < Rails::Generators::NamedBase
191
217
  File.read(input_type_file_name).include?("argument :#{field.singularize}")
192
218
  return
193
219
  end
220
+
194
221
  write_at(
195
222
  input_type_file_name, 4,
196
223
  <<-STRING
@@ -275,7 +302,7 @@ t.#{@id_db_type} :#{resource.underscore.singularize}_id
275
302
  res = "#{input_type ? 'argument' : 'field'} :#{field_name}, #{field_type}"
276
303
  if !input_type && field_name.ends_with?('_id') && @belongs_to_fields.key?(field_name)
277
304
  res += "\n field :#{field_name.gsub('_id', '')}, " \
278
- "!#{field_name.gsub('_id', '').pluralize.camelize}::Type"
305
+ "#{field_name.gsub('_id', '').pluralize.camelize}::Type"
279
306
  end
280
307
  res
281
308
  end&.join("\n ")
@@ -1,10 +1,13 @@
1
1
  require 'deep_pluck'
2
+ require 'rkelly'
2
3
 
3
4
  module Graphql
4
5
  class HydrateQuery
5
6
 
6
- def initialize(model, context, check_visibility: true, id: nil, user: nil)
7
- @fields = context&.irep_node&.scoped_children&.values&.first
7
+ def initialize(model, context, order_by: nil, filter: nil, check_visibility: true, id: nil, user: nil)
8
+ @context = context
9
+ @filter = filter
10
+ @order_by = order_by
8
11
  @model = model
9
12
  @models = [model_name.singularize.camelize]
10
13
  @check_visibility = check_visibility
@@ -13,12 +16,19 @@ module Graphql
13
16
  end
14
17
 
15
18
  def run
19
+ @model = @model.where(transform_filter(@filter)) if @filter
20
+ @model = @model.order(@order_by) if @order_by
16
21
  @model = @model.where(id: @id) if @id
17
- plucked = @model.deep_pluck(*hash_to_array_of_hashes(fields_to_hash(@fields), @model))
22
+ plucked = @model.deep_pluck(*hash_to_array_of_hashes(parse_fields(@context&.irep_node), @model))
18
23
  result = plucked_attr_to_structs(plucked, model_name.singularize.camelize.constantize)&.compact
19
24
  @id ? result.first : result
20
25
  end
21
26
 
27
+ def transform_filter(filter)
28
+ parsed_filter = RKelly::Parser.new.parse(filter).to_ecma
29
+ parsed_filter.gsub('||', 'OR').gsub('&&', 'AND').gsub('===', '=').gsub('==', '=').delete(';')
30
+ end
31
+
22
32
  def plucked_attr_to_structs(arr, parent_model)
23
33
  arr.map { |e| hash_to_struct(e, parent_model) }
24
34
  end
@@ -49,7 +59,7 @@ module Graphql
49
59
  end
50
60
 
51
61
  def hash_to_array_of_hashes(hash, parent_class)
52
- return if parent_class.nil?
62
+ return if parent_class.nil? || hash.nil?
53
63
 
54
64
  hash['id'] = nil if hash['id'].blank?
55
65
  fetch_ids_from_relation(hash)
@@ -87,14 +97,21 @@ module Graphql
87
97
  child_class_name = child.to_s.singularize.camelize
88
98
  parent_class_name = parent.to_s.singularize.camelize
89
99
  return child_class_name.constantize if activerecord_model?(child_class_name)
100
+
90
101
  return unless activerecord_model?(parent_class_name)
91
102
 
92
103
  parent_class_name.constantize.reflections[child.to_s.underscore]&.klass
93
104
  end
94
105
 
95
- def fields_to_hash(fields)
106
+ def parse_fields(irep_node)
107
+ fields = irep_node&.scoped_children&.values&.first
108
+ if fields.key?('edges')
109
+ fields = fields['edges'].scoped_children.values.first['node']&.scoped_children&.values&.first
110
+ end
111
+ return if fields.blank?
112
+
96
113
  fields.each_with_object({}) do |(k, v), h|
97
- h[k] = v.scoped_children == {} ? nil : fields_to_hash(v.scoped_children.values.first)
114
+ h[k] = v.scoped_children == {} ? nil : parse_fields(v)
98
115
  end
99
116
  end
100
117
 
@@ -4,7 +4,7 @@ module Graphql
4
4
  class Config
5
5
 
6
6
  include Singleton
7
- attr_accessor :id_type, :basic_mutations
7
+ attr_accessor :id_type
8
8
 
9
9
  def self.query_resources
10
10
  Dir.glob("#{File.expand_path('.')}/app/graphql/*/type.rb").map do |dir|
@@ -1,7 +1,7 @@
1
1
  module Graphql
2
2
  module Rails
3
3
  module Api
4
- VERSION = '0.5.0'.freeze
4
+ VERSION = '0.6.0'.freeze
5
5
  end
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql-rails-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - poilon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-13 00:00:00.000000000 Z
11
+ date: 2018-12-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -64,6 +64,20 @@ dependencies:
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
66
  version: 5.2.0
67
+ - !ruby/object:Gem::Dependency
68
+ name: rkelly-remix
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ type: :runtime
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
67
81
  description: This gem purpose is to make graphql easier to use in ruby. Mainly developed
68
82
  for from-scratch app
69
83
  email: