graphql_model_mapper 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,221 @@
1
+ module GraphqlModelMapper
2
+ module Resolve
3
+ def self.query_resolver(obj, args, ctx, name)
4
+ obj_context = name.classify.constantize
5
+ select_args = args[:select] || args
6
+
7
+ if !GraphqlModelMapper.authorized?(ctx, obj_context.name, :query)
8
+ raise GraphQL::ExecutionError.new("error: unauthorized access: #{:query} '#{obj_context.class_name.classify}'")
9
+ end
10
+ classmethods = []
11
+ scope_allowed = false
12
+ with_deleted_allowed = false
13
+ if select_args[:scope]
14
+ classmethods = obj_context.methods - Object.methods
15
+ scope_allowed = classmethods.include?(select_args[:scope].to_sym)
16
+ 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
17
+ end
18
+ if select_args[:with_deleted]
19
+ classmethods = obj_context.methods - Object.methods
20
+ with_deleted_allowed = classmethods.include?(:with_deleted)
21
+ 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
22
+ end
23
+ implied_includes = GraphqlModelMapper::Resolve.get_implied_includes(obj_context, GraphqlModelMapper::Resolve.get_include_fields(ctx))
24
+ if !implied_includes.empty?
25
+ obj_context = obj_context.includes(implied_includes)
26
+ if Rails.version.split(".").first.to_i > 3
27
+ obj_context = obj_context.references(implied_includes)
28
+ end
29
+ end
30
+ if select_args[:ids]
31
+ obj_context = obj_context.where(["#{obj_context.model_name.plural}.id in (?)", select_args[:ids]])
32
+ end
33
+ if select_args[:id]
34
+ obj_context = obj_context.where(["#{obj_context.model_name.plural}.id = ?", select_args[:id].to_i])
35
+ end
36
+ if select_args[:where]
37
+ obj_context = obj_context.where(select_args[:where])
38
+ end
39
+ if with_deleted_allowed
40
+ obj_context = obj_context.with_deleted
41
+ end
42
+ if scope_allowed
43
+ obj_context = obj_context.send(select_args[:scope].to_sym)
44
+ end
45
+ if !select_args[:limit].nil? && select_args[:limit].to_f > 0
46
+ obj_context = obj_context.limit(select_args[:limit])
47
+ end
48
+ if select_args[:offset]
49
+ obj_context = obj_context.offset(select_args[:offset])
50
+ end
51
+ if select_args[:order]
52
+ obj_context = obj_context.order(select_args[:order])
53
+ end
54
+ if select_args[:explain]
55
+ obj_context = obj_context.eager_load(implied_includes)
56
+ raise GraphQL::ExecutionError.new(obj_context.explain.split("\n").first.sub("EXPLAIN for: ", ""))
57
+ end
58
+ obj_context
59
+ end
60
+
61
+ def self.update_resolver(obj, inputs, ctx, name)
62
+ item = GraphqlModelMapper::Resolve.nested_update(ctx, name, inputs)
63
+ item
64
+ end
65
+
66
+ def self.delete_resolver(obj, inputs, ctx, model_name)
67
+ model = model_name.classify.constantize
68
+ items = self.query_resolver(obj, inputs, ctx, model_name)
69
+ ids = items.collect(&:id)
70
+ if !GraphqlModelMapper.authorized?(ctx, model_name, :update)
71
+ raise GraphQL::ExecutionError.new("error: unauthorized access: delete '#{model_name.classify}', transaction cancelled")
72
+ end
73
+ begin
74
+ deleted_items = model.delete(ids)
75
+ rescue => e
76
+ raise e #GraphQL::ExecutionError.new("error: delete")
77
+ end
78
+ if model.methods.include?(:with_deleted)
79
+ items.with_deleted
80
+ else
81
+ items
82
+ end
83
+ end
84
+
85
+ def self.create_resolver(obj, inputs, ctx, model_name)
86
+ if !GraphqlModelMapper.authorized?(ctx, model_name, :create)
87
+ raise GraphQL::ExecutionError.new("error: unauthorized access: create '#{model_name.classify}'")
88
+ end
89
+ model = model_name.classify.constantize
90
+ item = model.new(inputs[model_name.downcase].to_h)
91
+ begin
92
+ if !item.valid?
93
+ raise GraphQL::ExecutionError.new(item.errors.full_messages.join("; "))
94
+ else
95
+ raise GraphQL::ExecutionError.new("error: WIP, item not saved but is a valid '#{model_name.classify}'")
96
+ #item.save!
97
+ end
98
+ end
99
+ item
100
+ end
101
+
102
+
103
+ # build includes list for associated tables in use in the query, skips [:nodes, :edges] and result entries while walking references
104
+ def self.get_implied_includes(model, field_names=nil, first=true, org_field_names=nil, resolve_fields=false)
105
+ if first
106
+ org_field_names = field_names
107
+ # associations fields that are on the model
108
+ a = field_names.select{|m| model.reflect_on_all_associations.map(&:name).include?(m[:name].to_sym)}.select{|m| field_names.map{|m| m[:parent_line]}.include?(m[:line])}
109
+ # base field names that have no parent, get the lowest number parent_line on the associated field names
110
+ a = a.select{|o| o[:parent_line] == a.map{|v| v[:parent_line]}.sort.first}
111
+ else
112
+ a = field_names
113
+ end
114
+ final_out = []
115
+ a.each do |b|
116
+ out = []
117
+ child_relations = org_field_names.select{|g| g[:parent_line] == b[:line]}
118
+ if !child_relations.empty?
119
+ children = GraphqlModelMapper::Resolve.get_implied_includes(nil, child_relations, false, org_field_names, resolve_fields)
120
+ if children.empty?
121
+ out << b[:name].to_sym if ![:edges, :node].include?(b[:name].to_sym)
122
+ else
123
+ if ![:edges, :node].include?(b[:name].to_sym)
124
+ out << { b[:name].to_sym => children.flatten }
125
+ else
126
+ out = children.flatten
127
+ end
128
+ end
129
+ end
130
+ if resolve_fields && out.empty?
131
+ out << b[:name].to_sym
132
+ end
133
+ final_out << out if !out.empty?
134
+ end
135
+ final_out
136
+ end
137
+
138
+ def self.get_include_fields(ctx)
139
+ fieldnames = []
140
+ visitor = GraphQL::Language::Visitor.new(ctx.query.document)
141
+ visitor[GraphQL::Language::Nodes::Field] << ->(node, parent) { fieldnames << {:line=>node.line, :parent_line=>parent.line, :parent=>parent.name, :name=>node.name} }
142
+ visitor.visit
143
+ fieldnames
144
+ end
145
+
146
+
147
+ def self.nested_update(ctx, model_name, inputs, child_name=nil, child_id=nil, parent_name=nil, parent_id=nil, klass_name=nil)
148
+ model = model_name.classify.constantize
149
+
150
+ if !child_name.nil? && !child_id.nil? # has_many && has_one
151
+ inputs_root = inputs
152
+ #puts "inputs_root[:id] #{inputs_root[:id]} #{inputs_root}"
153
+ if model.public_methods.include?(:with_deleted)
154
+ item = model.with_deleted.where("id = ? and #{child_name.downcase}_id = ?", inputs_root[:id], child_id).first
155
+ else
156
+ item = model.where("id = ? and #{child_name.downcase}_id = ?", inputs_root[:id], child_id).first
157
+ end
158
+ 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?
159
+ elsif !parent_name.nil? && !parent_id.nil? # belongs_to
160
+ inputs_root = inputs
161
+ #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}"
162
+ if model.public_methods.include?(:with_deleted)
163
+ 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]
164
+ else
165
+ 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]
166
+ end
167
+ 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?
168
+ model_name = klass_name
169
+ model = klass_name.classify.constantize
170
+ else #root query always single record, need to offeset property for object_input_type
171
+ inputs_root = inputs[model_name.downcase]
172
+ #puts "inputs_root[:id] #{inputs_root[:id]} #{inputs_root}"
173
+ if model.public_methods.include?(:with_deleted)
174
+ item = model.with_deleted.find(inputs_root[:id])
175
+ else
176
+ item = model.find(inputs_root[:id])
177
+ end
178
+ raise GraphQL::ExecutionError.new("error: #{model.name} record not found for #{model.name}.id=#{inputs[model_name.downcase][:id]}") if item.nil?
179
+ end
180
+ if !GraphqlModelMapper.authorized?(ctx, model.name, :update)
181
+ raise GraphQL::ExecutionError.new("error: unauthorized access: #{:update} '#{model}', transaction cancelled")
182
+ end
183
+
184
+ item_associations = model.reflect_on_all_associations.select{|t| begin t.klass rescue next end}.select{|t| !t.options[:polymorphic]}
185
+ item_association_names = item_associations.map{|m| m.name.to_s}
186
+ input_association_names = item_association_names & inputs_root.to_h.keys
187
+
188
+ item.transaction do
189
+ #puts "***********item.update_attributes(#{inputs_root.to_h.except('id').except!(*item_association_names)})"
190
+ #puts "***********ctx[current_user.to_sym].is_admin?(#{ctx[:current_user].is_admin?})"
191
+ item.update_attributes(inputs_root.to_h.except('id').except!(*item_association_names))
192
+ input_association_names.each do |ia|
193
+ lclinput = inputs_root[ia]
194
+ ass = item_associations.select{|a| a.name.to_s == ia}.first
195
+ klass = ass.klass
196
+ is_collection = ass.collection?
197
+ belongs_to = ass.belongs_to?
198
+ #puts "#{ass.name} #{ass.collection?} #{ass.belongs_to?}"
199
+ #puts "#{ass.association_foreign_key} #{ass.association_primary_key} #{ass.active_record_primary_key}"
200
+
201
+ if is_collection
202
+ #puts "is_collection"
203
+ lclinput.each do |i|
204
+ #puts "#{klass.name} #{i.to_h} #{model_name.downcase} #{inputs_root[:id]}"
205
+ GraphqlModelMapper::Resolve.nested_update(ctx, klass.name, i, model_name.downcase, inputs_root[:id])
206
+ end
207
+ elsif !is_collection && belongs_to
208
+ #puts "belongs_to"
209
+ #puts "self.nested_update(#{ctx}, #{model.name}, #{lclinput.to_h}, nil, nil, #{ass.name}, #{inputs_root[:id]}, #{klass.name})"
210
+ GraphqlModelMapper::Resolve.nested_update(ctx, model.name, lclinput, nil, nil, ass.name, inputs_root[:id], klass.name)
211
+ elsif !is_collection && !belongs_to #has_one
212
+ #puts "has_one"
213
+ #puts "self.nested_update(#{ctx}, #{klass.name}, #{lclinput.to_h}, #{model_name.downcase}, #{inputs_root[:id]})"
214
+ GraphqlModelMapper::Resolve.nested_update(ctx, model.name, lclinput, nil, nil, ass.name, inputs_root[:id], klass.name)
215
+ end
216
+ end
217
+ end
218
+ item
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,110 @@
1
+ module GraphqlModelMapper
2
+ def self.Schema(log_query_depth: false, log_query_complexity: false, use_backtrace: false, use_authorize: false, nesting_strategy: :shallow, type_case: :camelize)
3
+
4
+ return GraphqlModelMapper.get_constant("GraphqlModelMapperSchema".upcase) if GraphqlModelMapper.defined_constant?("GraphqlModelMapperSchema".upcase)
5
+ GraphqlModelMapper.use_authorize = use_authorize
6
+ GraphqlModelMapper.nesting_strategy = nesting_strategy
7
+ GraphqlModelMapper.type_case = type_case
8
+
9
+ if GraphqlModelMapper.use_authorize
10
+ metadata_definitions = {
11
+ authorized: ->(field, authorized_proc) { field.metadata[:authorized_proc] = authorized_proc },
12
+ model_name: GraphQL::Define.assign_metadata_key(:model_name),
13
+ access_type: GraphQL::Define.assign_metadata_key(:access_type)
14
+ }
15
+ GraphQL::Field.accepts_definitions(metadata_definitions)
16
+ GraphQL::Argument.accepts_definitions(metadata_definitions)
17
+ end
18
+
19
+ schema = GraphQL::Schema.define do
20
+ use GraphQL::Backtrace if use_backtrace
21
+ default_max_page_size 100
22
+ mutation GraphqlModelMapper.MutationType
23
+ query GraphqlModelMapper.QueryType
24
+ end
25
+
26
+
27
+ schema.query_analyzers << GraphQL::Analysis::QueryDepth.new { |query, depth| Rails.logger.info("[******GraphqlModelMapper Query Depth] #{depth}") } if log_query_depth
28
+ schema.query_analyzers << GraphQL::Analysis::QueryComplexity.new { |query, complexity| Rails.logger.info("[******GraphqlModelMapper Query Complexity] #{complexity}")} if log_query_complexity
29
+
30
+ GraphqlModelMapper.set_constant("GraphqlModelMapperSchema".upcase, schema)
31
+
32
+ end
33
+
34
+
35
+ def self.QueryType
36
+ return GraphQL::ObjectType.define do
37
+ name 'Query'
38
+ # create queries for each AR model object
39
+ field :welcomeQuery, types.String, hash_key: :welcomeMutation do
40
+ resolve -> (obj, args, ctx){
41
+ {
42
+ welcomeQuery: "this is a placeholder mutation in case you do not have access to other queries"
43
+ }
44
+ }
45
+ end
46
+ GraphqlModelMapper.schema_queries.each do |f|
47
+ field f[:name], f[:field] do
48
+ if GraphqlModelMapper.use_authorize
49
+ authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
50
+ model_name f[:model_name]
51
+ access_type f[:access_type].to_s
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ def self.MutationType
59
+ return GraphQL::ObjectType.define do
60
+ name 'Mutation'
61
+
62
+ field :welcomeMutation, types.String, hash_key: :welcomeMutation do
63
+ resolve -> (obj, args, ctx){
64
+ {
65
+ welcomeMutation: "this is a placeholder mutation in case you do not have access to other mutations"
66
+ }
67
+ }
68
+ end
69
+
70
+ GraphqlModelMapper.schema_mutations.each do |f|
71
+ field f[:name], f[:field] do
72
+ if GraphqlModelMapper.use_authorize
73
+ authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
74
+ model_name f[:model_name]
75
+ access_type f[:access_type].to_s
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ GraphqlModelMapper::GEOMETRY_TYPE = GraphQL::ScalarType.define do
84
+ name "Geometry"
85
+ description "The Geometry scalar type enables the serialization of Geometry data"
86
+
87
+ coerce_input ->(value, ctx) do
88
+ begin
89
+ value.nil? ? nil : GeoRuby::SimpleFeatures::Geometry.from_geojson(value)
90
+ rescue ArgumentError
91
+ raise GraphQL::CoercionError, "cannot coerce `#{value.inspect}` to json"
92
+ end
93
+ end
94
+ coerce_result ->(value, ctx) { value.nil? ? "" : value.to_json }
95
+ end
96
+
97
+ GraphqlModelMapper::DATE_TYPE = GraphQL::ScalarType.define do
98
+ name "Date"
99
+ description "The Date scalar type enables the serialization of date data to/from iso8601"
100
+
101
+ coerce_input ->(value, ctx) do
102
+ begin
103
+ value.nil? ? nil : Date.iso8601(value)
104
+ rescue ArgumentError
105
+ raise GraphQL::CoercionError, "cannot coerce `#{value.inspect}` to date"
106
+ end
107
+ end
108
+ coerce_result ->(value, ctx) { value.nil? ? nil : value.iso8601 }
109
+ end
110
+
File without changes
File without changes
@@ -0,0 +1,3 @@
1
+ module GraphqlModelMapper
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,228 @@
1
+ require "graphql"
2
+ require "graphql_model_mapper/mapper_type"
3
+ require "graphql_model_mapper/mutation"
4
+ require "graphql_model_mapper/query"
5
+ require "graphql_model_mapper/resolve"
6
+ require "graphql_model_mapper/schema"
7
+ require "graphql_model_mapper/version"
8
+ require 'graphql_model_mapper/railtie' if defined?(Rails)
9
+
10
+ module GraphqlModelMapper
11
+ mattr_accessor :type_case
12
+ mattr_accessor :nesting_strategy
13
+ mattr_accessor :use_authorize
14
+
15
+ class << self
16
+ attr_writer :logger
17
+
18
+ def logger
19
+ @logger ||= Logger.new($stdout).tap do |log|
20
+ log.progname = self.name
21
+ end
22
+ end
23
+ end
24
+
25
+ @@type_case = :camelize
26
+ @@nesting_strategy = :shallow
27
+ @@use_authorize = false
28
+
29
+ def self.included(klazz)
30
+ klazz.extend GraphqlModelMapper_Macros
31
+ end
32
+
33
+ module GraphqlModelMapper_Macros
34
+ protected
35
+
36
+ def graphql_types(name: self.name, query: {}, update: {}, delete: {}, create: {})
37
+ define_singleton_method(:graphql_types) do
38
+ GraphqlModelMapper::MapperType.graphql_types(name: name, query: query, update: update, delete: delete, create: create)
39
+ end
40
+ end
41
+
42
+ def graphql_update(name: self.name, description:"", resolver: -> (obj, inputs, ctx){
43
+ GraphqlModelMapper.logger.info "********************update"
44
+ item = GraphqlModelMapper::Resolve.update_resolver(obj, inputs, ctx, name)
45
+ {
46
+ item: item
47
+ }
48
+ })
49
+ define_singleton_method(:graphql_update) do
50
+ GraphqlModelMapper::Mutation.graphql_update(name: name, description: description, resolver: resolver)
51
+ end
52
+ end
53
+
54
+ def graphql_delete(name: self.name, description:"", resolver: -> (obj, inputs, ctx){
55
+ GraphqlModelMapper.logger.info "********************delete"
56
+ items = GraphqlModelMapper::Resolve.delete_resolver(obj, inputs, ctx, name)
57
+ {
58
+ total: items.length,
59
+ items: items
60
+ }
61
+ }, arguments: [], scope_methods: [])
62
+ define_singleton_method(:graphql_delete) do
63
+ GraphqlModelMapper::Mutation.graphql_delete(name: name, description: description, resolver: resolver, scope_methods: scope_methods)
64
+ end
65
+ end
66
+
67
+ def graphql_create(name: self.name, description:"", resolver: -> (obj, args, ctx){
68
+ GraphqlModelMapper.logger.info "********************create"
69
+ item = GraphqlModelMapper::Resolve.create_resolver(obj, args, ctx, name)
70
+ {
71
+ item: item
72
+ }
73
+ })
74
+ define_singleton_method(:graphql_create) do
75
+ GraphqlModelMapper::Mutation.graphql_create(name: name, description: description, resolver: resolver)
76
+ end
77
+ end
78
+
79
+ def graphql_query(name: self.name, description: "", resolver: -> (obj, args, ctx) {
80
+ items = GraphqlModelMapper::Resolve.query_resolver(obj, args, ctx, name)
81
+ {
82
+ items: items,
83
+ total: items.length
84
+ }
85
+ }, arguments: [], scope_methods: [])
86
+ define_singleton_method(:graphql_query) do
87
+ GraphqlModelMapper::Query.graphql_query(name: name, description: description, resolver: resolver, scope_methods: scope_methods)
88
+ end
89
+ end
90
+ end
91
+
92
+ def self.implementations
93
+ Rails.application.eager_load!
94
+ ActiveRecord::Base.descendants.each.select do |clz|
95
+ begin
96
+ clz.included_modules.include?(GraphqlModelMapper) && (clz.public_methods.include?(:graphql_query) || clz.public_methods.include?(:graphql_update) || clz.public_methods.include?(:graphql_delete) || clz.public_methods.include?(:graphql_create) || clz.public_methods.include?(:graphql_types))
97
+ rescue
98
+ # it is okay that this is empty - just covering the possibility
99
+ end
100
+ end
101
+ end
102
+
103
+ def self.schema_queries
104
+ fields = []
105
+ GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_query)}.each { |t|
106
+ fields << { :name =>GraphqlModelMapper.get_type_case(t.name, false).to_sym, :field => t.graphql_query, :model_name=>t.name, :access_type=>:query }
107
+ }
108
+ fields
109
+ end
110
+
111
+ def self.schema_mutations
112
+ fields = []
113
+ GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_create)}.each { |t|
114
+ fields << {:name => GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(t.name)}Create", false).to_sym, :field=> t.graphql_create, :model_name=>t.name, :access_type=>:create }
115
+ }
116
+ GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_update)}.each { |t|
117
+ fields << {:name =>GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(t.name)}Update", false).to_sym, :field=>t.graphql_update, :model_name=>t.name, :access_type=>:update }
118
+ }
119
+ GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_delete)}.each { |t|
120
+ fields << {:name =>GraphqlModelMapper.get_type_case("#{GraphqlModelMapper.get_type_name(t.name)}Delete", false).to_sym, :field=>t.graphql_delete, :model_name=>t.name, :access_type=>:delete }
121
+ }
122
+ fields
123
+ end
124
+
125
+ def self.select_list(model_name, classes=[])
126
+ model = model_name.classify.constantize
127
+ output = []
128
+ columns = model.columns_hash.keys.map{|m| "#{model.name.underscore.pluralize}.#{m}"}
129
+ relation_includes = model.reflect_on_all_associations.select{|t| begin t.klass rescue next end}.select{|t| !t.options[:polymorphic]}.map{|m| "#{model.name.underscore.pluralize}.#{m.name}"}
130
+ relations = model.reflect_on_all_associations.select{|t| begin t.klass rescue next end}.select{|t| !t.options[:polymorphic]}
131
+ relations.each do |a|
132
+ if !classes.include?(a.klass.name)
133
+ classes << a.klass.name
134
+ output = output + GraphqlModelMapper.select_list(a.klass.name, classes)
135
+ end
136
+ end
137
+ output << relation_includes + columns
138
+ output.sort
139
+ end
140
+
141
+ def self.authorized?(ctx, model_name, access, roles=nil)
142
+ model = model_name.classify.constantize
143
+ access = access.to_sym
144
+ #here it is checking to see if public methods are exposed on items based on the operation being performed
145
+ if (access && access == :read) || (access && access == :query)
146
+ access = :read
147
+ if !model.public_methods.include?(:graphql_query)
148
+ return false
149
+ end
150
+ elsif access && access == :create
151
+ if !model.public_methods.include?(:graphql_create)
152
+ return false
153
+ end
154
+ elsif access && access == :update
155
+ if !model.public_methods.include?(:graphql_update)
156
+ return false
157
+ end
158
+ elsif access && access == :delete
159
+ if !model.public_methods.include?(:graphql_delete)
160
+ return false
161
+ end
162
+ end
163
+ if roles && roles.length > 0
164
+ roles.each do |r|
165
+ if !ctx[:current_user].hash_role?(role)
166
+ return false
167
+ end
168
+ end
169
+ end
170
+ #implementation specific, here it is using an ability method on the user class plugged into cancan
171
+ if ctx[:current_user].public_methods.include?(:ability)
172
+ if !ctx[:current_user].ability.can? access, model
173
+ return false
174
+ end
175
+ end
176
+ true
177
+ end
178
+
179
+ def self.get_type_name(classname, lowercase_first_letter=false)
180
+ str = "#{classname.classify.demodulize}"
181
+ if lowercase_first_letter && str.length > 0
182
+ str = str[0].downcase + str[1..-1]
183
+ end
184
+ str
185
+ end
186
+
187
+ def self.get_type_case(str, uppercase=true)
188
+ if @@type_case == :camelize
189
+ if uppercase
190
+ str.to_s.camelize(:upper)
191
+ else
192
+ str.to_s.camelize(:lower)
193
+ end
194
+ elsif @@type_case == :underscore
195
+ if uppercase
196
+ self.underscore(str)
197
+ else
198
+ str.underscore
199
+ end
200
+ elsif @@type_case == :classify
201
+ str
202
+ else
203
+ str
204
+ end
205
+ end
206
+
207
+ def self.underscore(str, upcase=true)
208
+ if upcase
209
+ str.split('_').map {|w| w.capitalize}.join('_')
210
+ else
211
+ str.underscore
212
+ end
213
+ end
214
+
215
+ def self.get_constant(type_name)
216
+ GraphqlModelMapper.const_get(type_name.upcase)
217
+ end
218
+
219
+ def self.set_constant(type_name, type)
220
+ GraphqlModelMapper.const_set(type_name.upcase, type)
221
+ end
222
+
223
+ def self.defined_constant?(type_name)
224
+ GraphqlModelMapper.const_defined?(type_name.upcase)
225
+ end
226
+ end
227
+
228
+ ActiveRecord::Base.send(:include, GraphqlModelMapper) if defined?(ActiveRecord)
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: graphql_model_mapper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Gene Black
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-11-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: graphql
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.7.5
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.7.5
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.16'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.16'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.0'
69
+ description: This gem extends ActiveRecord::Base to add automatic generation of GraphQL
70
+ objects based on your models.
71
+ email:
72
+ - geblack@hotmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - Gemfile.lock
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - bin/console
85
+ - bin/setup
86
+ - graphql_model_mapper.gemspec
87
+ - lib/graphql_model_mapper.rb
88
+ - lib/graphql_model_mapper/mapper_type.rb
89
+ - lib/graphql_model_mapper/mutation.rb
90
+ - lib/graphql_model_mapper/query.rb
91
+ - lib/graphql_model_mapper/railtie.rb
92
+ - lib/graphql_model_mapper/resolve.rb
93
+ - lib/graphql_model_mapper/schema.rb
94
+ - lib/graphql_model_mapper/schema_types.rb
95
+ - lib/graphql_model_mapper/utility.rb
96
+ - lib/graphql_model_mapper/version.rb
97
+ homepage: https://github.com/geneblack/graphql_model_mapper
98
+ licenses:
99
+ - MIT
100
+ metadata:
101
+ allowed_push_host: https://rubygems.org
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 2.6.14
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Adds GraphQL object generation based on your ActiveRecord models.
122
+ test_files: []