graphql-rails-api 0.5.0 → 0.6.0

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