graphql_model_mapper 0.0.2

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.
@@ -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: []