Module: GraphqlModelMapper

Defined in:
lib/graphql_model_mapper.rb,
lib/graphql_model_mapper/query.rb,
lib/graphql_model_mapper/schema.rb,
lib/graphql_model_mapper/railtie.rb,
lib/graphql_model_mapper/resolve.rb,
lib/graphql_model_mapper/utility.rb,
lib/graphql_model_mapper/version.rb,
lib/graphql_model_mapper/mutation.rb,
lib/graphql_model_mapper/mapper_type.rb

Defined Under Namespace

Modules: GraphqlModelMapper_Macros, MapperType, Mutation, Query, Resolve Classes: Railtie

Constant Summary collapse

GEOMETRY_OBJECT_TYPE =
GraphQL::ScalarType.define do
  name "GeometryObject"
  description "The Geometry scalar type enables the serialization of Geometry data"
  require 'geo_ruby/geojson' if !defined?(GeoRuby).nil?

  coerce_input ->(value, ctx) do
      begin
          if value.nil? 
            nil 
          elsif !defined?(GeoRuby::GeojsonParser).nil?
            GeoRuby::SimpleFeatures::Geometry.from_geojson(value) 
          elsif !defined?(RGeo::GeoJSON).nil?
            RGeo::GeoJSON.decode(value, json_parser: :json)
          else 
            raise ArgumentError
          end
      rescue ArgumentError
          raise GraphQL::CoercionError, "cannot coerce `#{value.inspect}` to json"
      end
  end
  coerce_result ->(value, ctx) { (value.nil? ? "" : (defined?(GeoRuby) == "constant" && value.kind_of?(GeoRuby::SimpleFeatures::Geometry) ? value.to_json : (defined?(RGeo) == "constant" && defined?(RGeo::GeoJSON) == "constant" && RGeo::Geos.is_capi_geos?(value) ? RGeo::GeoJSON.encode(value).to_json : value))) }
end
GEOMETRY_STRING_TYPE =
GraphQL::ScalarType.define do
  name "GeometryString"
  description "The Geometry scalar type enables the serialization of Geometry data"
  require 'geo_ruby/geojson' if !defined?(GeoRuby).nil?

  coerce_input ->(value, ctx) do
      begin
          if value.nil? 
            nil 
          elsif !defined?(GeoRuby::GeojsonParser).nil?
            GeoRuby::SimpleFeatures::Geometry.from_geojson(value).as_wkt 
          elsif !defined?(RGeo::GeoJSON).nil?
            RGeo::GeoJSON.decode(value, json_parser: :json).as_text
          else 
            raise ArgumentError
          end
      rescue ArgumentError
          raise GraphQL::CoercionError, "cannot coerce `#{value.inspect}` to json"
      end
  end
  coerce_result ->(value, ctx) { (value.nil? ? "" : (defined?(GeoRuby) == "constant" && value.kind_of?(GeoRuby::SimpleFeatures::Geometry) ? value.to_json : (defined?(RGeo) == "constant" && defined?(RGeo::GeoJSON) == "constant" && RGeo::Geos.is_capi_geos?(value) ? RGeo::GeoJSON.encode(value).to_json : value))) }
end
DATE_TYPE =
GraphQL::ScalarType.define do
  name "Date"
  description "The Date scalar type enables the serialization of date data to/from iso8601"

  coerce_input ->(value, ctx) do
      begin
          value.nil? ? nil :  Date.iso8601(value)
      rescue ArgumentError
          raise GraphQL::CoercionError, "cannot coerce `#{value.inspect}` to date"
      end
  end
  coerce_result ->(value, ctx) { value.nil? ? nil : value.iso8601 }
end
VERSION =
"0.0.6"
@@type_case =
:camelize
@@nesting_strategy =
:shallow
@@use_authorize =
false

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.loggerObject



23
24
25
26
27
# File 'lib/graphql_model_mapper.rb', line 23

def logger
  @logger ||= Logger.new($stdout).tap do |log|
    log.progname = self.name
  end
end

Class Method Details

.authorized?(ctx, model_name, access, roles = nil) ⇒ Boolean

Returns:

  • (Boolean)


52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/graphql_model_mapper/utility.rb', line 52

def self.authorized?(ctx, model_name, access, roles=nil)
  model = model_name.classify.constantize
  access = access.to_sym
  #here it is checking to see if public methods are exposed on items based on the operation being performed
  if (access && access == :read) || (access && access == :query)
    access = :read 
    if !model.public_methods.include?(:graphql_query)
      return false
    end
  elsif access && access == :create
    if !model.public_methods.include?(:graphql_create)
      return false
    end
  elsif access && access == :update
    if !model.public_methods.include?(:graphql_update)
      return false
    end
  elsif access && access == :delete
    if !model.public_methods.include?(:graphql_delete)
      return false
    end
  end
  if roles && roles.length > 0
    roles.each do |r|
      if !ctx[:current_user].hash_role?(role)
        return false
      end
    end
  end
  #implementation specific, here it is using an ability method on the user class plugged into cancan
  if ctx[:current_user].public_methods.include?(:ability)
    if !ctx[:current_user].ability.can? access, model
      return false
    end
  end
  true
end

.defined_constant?(type_name) ⇒ Boolean

Returns:

  • (Boolean)


134
135
136
# File 'lib/graphql_model_mapper/utility.rb', line 134

def self.defined_constant?(type_name)
  GraphqlModelMapper.const_defined?(type_name.upcase)
end

.get_constant(type_name) ⇒ Object



126
127
128
# File 'lib/graphql_model_mapper/utility.rb', line 126

def self.get_constant(type_name)
  GraphqlModelMapper.const_get(type_name.upcase)
end

.get_type_case(str, uppercase = true) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/graphql_model_mapper/utility.rb', line 98

def self.get_type_case(str, uppercase=true)
  if @@type_case == :camelize
    if uppercase
      str.to_s.camelize(:upper)
    else
      str.to_s.camelize(:lower)
    end
  elsif @@type_case == :underscore
    if uppercase
      self.underscore(str)
    else
      str.underscore
    end
  elsif @@type_case == :classify
    str
  else
    str
  end
end

.get_type_name(classname, lowercase_first_letter = false) ⇒ Object



90
91
92
93
94
95
96
# File 'lib/graphql_model_mapper/utility.rb', line 90

def self.get_type_name(classname, lowercase_first_letter=false)
  str = "#{classname.classify.demodulize}"
  if lowercase_first_letter && str.length > 0
    str = str[0].downcase + str[1..-1]
  end
  str
end

.implementationsObject



3
4
5
6
7
8
9
10
11
12
# File 'lib/graphql_model_mapper/utility.rb', line 3

def self.implementations
  Rails.application.eager_load!
  ActiveRecord::Base.descendants.each.select do |clz|
    begin
      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))
    rescue
      # it is okay that this is empty - just covering the possibility
    end
  end
end

.included(klazz) ⇒ Object



31
32
33
# File 'lib/graphql_model_mapper.rb', line 31

def self.included(klazz)
  klazz.extend GraphqlModelMapper_Macros
end

.MutationTypeObject



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/graphql_model_mapper/schema.rb', line 58

def self.MutationType
  return GraphQL::ObjectType.define do
    name 'Mutation'
  
    field :welcomeMutation, types.String, hash_key: :welcomeMutation do
      resolve -> (obj, args, ctx){
        {
          welcomeMutation: "this is a placeholder mutation in case you do not have access to other mutations"
        }
      }
    end

    GraphqlModelMapper.schema_mutations.each do |f|
      field f[:name], f[:field]  do
        if GraphqlModelMapper.use_authorize
          authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
          model_name  f[:model_name]
          access_type f[:access_type].to_s
        end
      end
    end   
  end
end

.QueryTypeObject



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/graphql_model_mapper/schema.rb', line 35

def self.QueryType
  return GraphQL::ObjectType.define do
    name 'Query'
    # create queries for each AR model object
    field :welcomeQuery, types.String, hash_key: :welcomeMutation do
      resolve -> (obj, args, ctx){
        {
          welcomeQuery: "this is a placeholder mutation in case you do not have access to other queries"
        }
      }
    end
    GraphqlModelMapper.schema_queries.each do |f|
      field f[:name], f[:field]  do
        if GraphqlModelMapper.use_authorize
          authorized ->(ctx, model_name, access_type) { GraphqlModelMapper.authorized?(ctx, model_name, access_type.to_sym) }
          model_name f[:model_name]
          access_type f[:access_type].to_s
        end
      end   
    end
  end
end

.Schema(log_query_depth: false, log_query_complexity: false, use_backtrace: false, use_authorize: false, nesting_strategy: :shallow, type_case: :camelize) ⇒ Object



2
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
# File 'lib/graphql_model_mapper/schema.rb', line 2

def self.Schema(log_query_depth: false, log_query_complexity: false, use_backtrace: false, use_authorize: false, nesting_strategy: :shallow, type_case: :camelize)

  return GraphqlModelMapper.get_constant("GraphqlModelMapperSchema".upcase) if GraphqlModelMapper.defined_constant?("GraphqlModelMapperSchema".upcase)
  GraphqlModelMapper.use_authorize = use_authorize
  GraphqlModelMapper.nesting_strategy = nesting_strategy
  GraphqlModelMapper.type_case = type_case

  if GraphqlModelMapper.use_authorize
     = {
      authorized: ->(field, authorized_proc) { field.[:authorized_proc] = authorized_proc },
      model_name: GraphQL::Define.(:model_name),
      access_type: GraphQL::Define.(:access_type)
    }
    GraphQL::Field.accepts_definitions()
    GraphQL::Argument.accepts_definitions()
  end

  schema = GraphQL::Schema.define do
    use GraphQL::Backtrace if use_backtrace
    default_max_page_size 100
    mutation GraphqlModelMapper.MutationType
    query GraphqlModelMapper.QueryType
  end

 
  schema.query_analyzers << GraphQL::Analysis::QueryDepth.new { |query, depth| Rails.logger.info("[******GraphqlModelMapper Query Depth] #{depth}") } if log_query_depth
  schema.query_analyzers << GraphQL::Analysis::QueryComplexity.new { |query, complexity| Rails.logger.info("[******GraphqlModelMapper Query Complexity] #{complexity}")} if log_query_complexity

  GraphqlModelMapper.set_constant("GraphqlModelMapperSchema".upcase, schema)

end

.schema_mutationsObject



22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/graphql_model_mapper/utility.rb', line 22

def self.schema_mutations
  fields = []
  GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_create)}.each { |t|
    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 }
  }
  GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_update)}.each { |t|
    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 } 
  }
  GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_delete)}.each { |t|
    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 }
  }
  fields
end

.schema_queriesObject



14
15
16
17
18
19
20
# File 'lib/graphql_model_mapper/utility.rb', line 14

def self.schema_queries
  fields = []
  GraphqlModelMapper.implementations.select{|t| t.public_methods.include?(:graphql_query)}.each { |t|      
    fields << { :name =>GraphqlModelMapper.get_type_case(t.name, false).to_sym, :field => t.graphql_query, :model_name=>t.name, :access_type=>:query }
  }
  fields
end

.select_list(model_name, classes = []) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/graphql_model_mapper/utility.rb', line 36

def self.select_list(model_name, classes=[])
  model = model_name.classify.constantize
  output = []
  columns = model.columns_hash.keys.map{|m| "#{model.name.underscore.pluralize}.#{m}"}
  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}"}
  relations = model.reflect_on_all_associations.select{|t| begin t.klass rescue next end}.select{|t| !t.options[:polymorphic]}
  relations.each do |a|
    if !classes.include?(a.klass.name)
      classes << a.klass.name
      output = output + GraphqlModelMapper.select_list(a.klass.name, classes)
    end
  end
  output << relation_includes + columns
  output.sort
end

.set_constant(type_name, type) ⇒ Object



130
131
132
# File 'lib/graphql_model_mapper/utility.rb', line 130

def self.set_constant(type_name, type)
  GraphqlModelMapper.const_set(type_name.upcase, type)
end

.underscore(str, upcase = true) ⇒ Object



118
119
120
121
122
123
124
# File 'lib/graphql_model_mapper/utility.rb', line 118

def self.underscore(str, upcase=true)
  if upcase
    str.split('_').map {|w| w.capitalize}.join('_')
  else
    str.underscore
  end
end