Module: GraphqlModelMapper::Resolve

Defined in:
lib/graphql_model_mapper/resolve.rb

Class Method Summary collapse

Class Method Details

.create_resolver(obj, inputs, ctx, model_name) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/graphql_model_mapper/resolve.rb', line 87

def self.create_resolver(obj, inputs, ctx, model_name)
    if !GraphqlModelMapper.authorized?(ctx, model_name, :create)
        raise GraphQL::ExecutionError.new("error: unauthorized access: create '#{model_name.classify}'")
    end
    model = model_name.classify.constantize   
    item = model.new(inputs[model_name.downcase].to_h)
    begin
      if !item.valid?
        raise GraphQL::ExecutionError.new(item.errors.full_messages.join("; "))
      else
        raise GraphQL::ExecutionError.new("error: WIP, item not saved but is a valid '#{model_name.classify}'")
        #item.save!
      end
    end
    item
end

.delete_resolver(obj, inputs, ctx, model_name) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/graphql_model_mapper/resolve.rb', line 68

def self.delete_resolver(obj, inputs, ctx, model_name)
    model = model_name.classify.constantize
    items = self.query_resolver(obj, inputs, ctx, model_name)
    ids = items.collect(&:id)
    if !GraphqlModelMapper.authorized?(ctx, model_name, :update)
        raise GraphQL::ExecutionError.new("error: unauthorized access: delete '#{model_name.classify}', transaction cancelled")
    end    
    begin
        deleted_items = model.delete(ids)
    rescue => e
        raise e #GraphQL::ExecutionError.new("error: delete")
    end
    if model.methods.include?(:with_deleted)
        items.with_deleted
    else
        items
    end
end

.get_implied_includes(class_name, ast_node, dependencies = {}) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/graphql_model_mapper/resolve.rb', line 130

def self.get_implied_includes(class_name, ast_node, dependencies={})
  ast_node.selections.each do |selection|
    name = selection.name
    
    if using_relay_pagination?(selection)
      map_relay_pagination_depencies(class_name, selection, dependencies)
      next
    end
    
    if using_nodes_pagination?(selection)
      get_implied_includes(class_name, selection, dependencies)
      next
    end

    if using_is_items_collection?(selection)
      get_implied_includes(class_name, selection, dependencies)
      next
    end
    
    if has_reflection_with_name?(class_name, name)
      begin
        current_class_name = selection.name.singularize.classify.constantize
        dependencies[name] = get_implied_includes(current_class_name, selection)
      rescue NameError
        selection_name = class_name.reflections.with_indifferent_access[selection.name].options[:class_name]
        current_class_name = selection_name.singularize.classify.constantize
        dependencies[selection.name.to_sym] = get_implied_includes(current_class_name, selection)
        next
      end
    end
  end
  dependencies
end

.has_reflection_with_name?(class_name, selection_name) ⇒ Boolean

Returns:

  • (Boolean)


116
117
118
# File 'lib/graphql_model_mapper/resolve.rb', line 116

def self.has_reflection_with_name?(class_name, selection_name)
  class_name.reflect_on_all_associations.select{|m|m.name == selection_name.to_sym}.present?
end

.map_relay_pagination_depencies(class_name, selection, dependencies) ⇒ Object



120
121
122
123
124
125
126
127
128
# File 'lib/graphql_model_mapper/resolve.rb', line 120

def self.map_relay_pagination_depencies(class_name, selection, dependencies)
  node_selection = selection.selections.find { |sel| sel.name == 'node' }
    
  if node_selection.present?
    get_implied_includes(class_name, node_selection, dependencies)
  else
    dependencies
  end
end

.nested_update(ctx, model_name, inputs, child_name = nil, child_id = nil, parent_name = nil, parent_id = nil, klass_name = nil) ⇒ Object



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/graphql_model_mapper/resolve.rb', line 165

def self.nested_update(ctx, model_name, inputs, child_name=nil, child_id=nil, parent_name=nil, parent_id=nil, klass_name=nil)
  model = model_name.classify.constantize
  
  if !child_name.nil? && !child_id.nil? # has_many && has_one
    inputs_root = inputs
    #puts "inputs_root[:id] #{inputs_root[:id]} #{inputs_root}"
    if model.public_methods.include?(:with_deleted)
      item = model.with_deleted.where("id = ? and #{child_name.downcase}_id = ?", inputs_root[:id], child_id).first
    else
      item = model.where("id = ? and #{child_name.downcase}_id = ?", inputs_root[:id], child_id).first
    end
    raise GraphQL::ExecutionError.new("error: #{model.name} record not found for #{model.name}.id = #{inputs_root[:id]} and #{model.name}.#{child_name.downcase}_id = #{child_id}") if item.nil?
  elsif !parent_name.nil? && !parent_id.nil? # belongs_to
    inputs_root = inputs
    #puts "parent_id #{parent_id} parent_name #{parent_name} #{model_name} model.with_deleted.find(#{parent_id}).send(#{parent_name}.to_sym).id} inputs_root[:id] #{inputs_root[:id]} #{inputs_root}"
    if model.public_methods.include?(:with_deleted)
      item = model.with_deleted.find(parent_id).public_send(parent_name.to_sym) if model.with_deleted.find(parent_id).public_send(parent_name.to_sym) && model.with_deleted.find(parent_id).public_send(parent_name.to_sym).id == inputs_root[:id]
    else
      item = model.find(parent_id).public_send(parent_name.to_sym) if model.find(parent_id).public_send(parent_name.to_sym) && model.with_deleted.find(parent_id).public_send(parent_name.to_sym).id == inputs_root[:id]
    end
    raise GraphQL::ExecutionError.new("error: #{model.name}.#{parent_name} record not found for  #{model.name}.with_deleted.find(#{parent_id}).#{parent_name}_id = #{inputs_root[:id]}") if item.nil?
    model_name = klass_name
    model = klass_name.classify.constantize
  else #root query always single record, need to offeset property for object_input_type
    inputs_root = inputs[model_name.downcase]
    #puts "inputs_root[:id] #{inputs_root[:id]} #{inputs_root}"
    if model.public_methods.include?(:with_deleted)
      item = model.with_deleted.find(inputs_root[:id])
    else
      item = model.find(inputs_root[:id])
    end
    raise GraphQL::ExecutionError.new("error: #{model.name} record not found for #{model.name}.id=#{inputs[model_name.downcase][:id]}") if item.nil?
  end
  if !GraphqlModelMapper.authorized?(ctx, model.name, :update)
    raise GraphQL::ExecutionError.new("error: unauthorized access: #{:update} '#{model}', transaction cancelled")
  end
        
  item_associations = model.reflect_on_all_associations.select{|t| begin t.klass rescue next end}.select{|t| !t.options[:polymorphic]}
  item_association_names = item_associations.map{|m| m.name.to_s}
  input_association_names = item_association_names & inputs_root.to_h.keys
  
  item.transaction do
    #puts "***********item.update_attributes(#{inputs_root.to_h.except('id').except!(*item_association_names)})"
    #puts "***********ctx[current_user.to_sym].is_admin?(#{ctx[:current_user].is_admin?})"
    item.update_attributes(inputs_root.to_h.except('id').except!(*item_association_names))
    input_association_names.each do |ia|
      lclinput = inputs_root[ia]
      ass = item_associations.select{|a| a.name.to_s == ia}.first
      klass = ass.klass
      is_collection = ass.collection?
      belongs_to = ass.belongs_to?
      #puts "#{ass.name} #{ass.collection?} #{ass.belongs_to?}"
      #puts "#{ass.association_foreign_key} #{ass.association_primary_key} #{ass.active_record_primary_key}"
      
      if is_collection
        #puts "is_collection"
        lclinput.each do |i|
          #puts "#{klass.name}  #{i.to_h}  #{model_name.downcase} #{inputs_root[:id]}"
          GraphqlModelMapper::Resolve.nested_update(ctx, klass.name, i, model_name.downcase, inputs_root[:id])
        end
      elsif !is_collection && belongs_to
        #puts "belongs_to"
        #puts "self.nested_update(#{ctx}, #{model.name}, #{lclinput.to_h}, nil, nil, #{ass.name}, #{inputs_root[:id]}, #{klass.name})"
        GraphqlModelMapper::Resolve.nested_update(ctx, model.name, lclinput, nil, nil, ass.name, inputs_root[:id], klass.name)
      elsif !is_collection && !belongs_to #has_one
        #puts "has_one"
        #puts "self.nested_update(#{ctx}, #{klass.name}, #{lclinput.to_h}, #{model_name.downcase}, #{inputs_root[:id]})"
        GraphqlModelMapper::Resolve.nested_update(ctx, model.name, lclinput, nil, nil, ass.name, inputs_root[:id], klass.name)
      end
    end
  end
  item
end

.query_resolver(obj, args, ctx, name) ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/graphql_model_mapper/resolve.rb', line 3

def self.query_resolver(obj, args, ctx, name)
    obj_context = name.classify.constantize
    select_args = args[:select] || args

    if !GraphqlModelMapper.authorized?(ctx, obj_context.name, :query)
      raise GraphQL::ExecutionError.new("error: unauthorized access: #{:query} '#{obj_context.class_name.classify}'")
    end
    classmethods = []
    scope_allowed = false
    with_deleted_allowed = false
    if select_args[:scope]
      classmethods = obj_context.methods - Object.methods
      scope_allowed = classmethods.include?(select_args[:scope].to_sym)
      raise GraphQL::ExecutionError.new("error: invalid scope '#{select_args[:scope]}' specified, '#{select_args[:scope]}' method does not exist on '#{ctx.field.name.classify}'") unless scope_allowed
    end
    if select_args[:with_deleted]
      classmethods = obj_context.methods - Object.methods
      with_deleted_allowed = classmethods.include?(:with_deleted)
      raise GraphQL::ExecutionError.new("error: invalid usage of 'with_deleted', 'with_deleted' method does not exist on '#{ctx.field.name.classify}'") unless with_deleted_allowed
    end

    implied_includes = self.get_implied_includes(name.classify.constantize, ctx.ast_node)

    if !implied_includes.empty? 
      obj_context = obj_context.includes(implied_includes)
      if Rails.version.split(".").first.to_i > 3
        obj_context = obj_context.references(implied_includes)
      end
    end
    if select_args[:ids]
        obj_context = obj_context.where(["#{obj_context.model_name.plural}.id in (?)", select_args[:ids]])
    end
    if select_args[:id]
      obj_context = obj_context.where(["#{obj_context.model_name.plural}.id = ?", select_args[:id].to_i])
    end
    if select_args[:where]
      obj_context = obj_context.where(select_args[:where])
    end
    if with_deleted_allowed
      obj_context = obj_context.with_deleted
    end
    if scope_allowed
      obj_context = obj_context.send(select_args[:scope].to_sym)
    end
    if !select_args[:limit].nil? && select_args[:limit].to_f > 0
      obj_context = obj_context.limit(select_args[:limit])
    end
    if select_args[:offset]
      obj_context = obj_context.offset(select_args[:offset])
    end
    if select_args[:order]
      obj_context = obj_context.order(select_args[:order])
    end
    if select_args[:explain]
      obj_context = obj_context.eager_load(implied_includes)
      raise GraphQL::ExecutionError.new(obj_context.explain.split("\n").first.sub("EXPLAIN for: ", ""))
    end
    obj_context
end

.update_resolver(obj, inputs, ctx, name) ⇒ Object



63
64
65
66
# File 'lib/graphql_model_mapper/resolve.rb', line 63

def self.update_resolver(obj, inputs, ctx, name)
    item = GraphqlModelMapper::Resolve.nested_update(ctx, name, inputs)
    item
end

.using_is_items_collection?(selection) ⇒ Boolean

Returns:

  • (Boolean)


108
109
110
# File 'lib/graphql_model_mapper/resolve.rb', line 108

def self.using_is_items_collection?(selection)
  selection.name == 'items'
end

.using_nodes_pagination?(selection) ⇒ Boolean

Returns:

  • (Boolean)


112
113
114
# File 'lib/graphql_model_mapper/resolve.rb', line 112

def self.using_nodes_pagination?(selection)
  selection.name == 'nodes'
end

.using_relay_pagination?(selection) ⇒ Boolean

Returns:

  • (Boolean)


104
105
106
# File 'lib/graphql_model_mapper/resolve.rb', line 104

def self.using_relay_pagination?(selection)
  selection.name == 'edges'
end