graphql_rails 0.1.0 → 0.2.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile.lock +2 -2
  4. data/README.md +76 -8
  5. data/graphql_rails.gemspec +1 -1
  6. data/lib/graphql_rails/attribute.rb +7 -42
  7. data/lib/graphql_rails/attribute/attribute_type_parser.rb +99 -0
  8. data/lib/graphql_rails/controller.rb +17 -5
  9. data/lib/graphql_rails/controller/action.rb +93 -0
  10. data/lib/graphql_rails/controller/action_configuration.rb +1 -2
  11. data/lib/graphql_rails/controller/controller_function.rb +7 -6
  12. data/lib/graphql_rails/controller/request.rb +3 -3
  13. data/lib/graphql_rails/model.rb +1 -1
  14. data/lib/graphql_rails/model/configuration.rb +14 -50
  15. data/lib/graphql_rails/model/graphql_type_builder.rb +37 -0
  16. data/lib/graphql_rails/router.rb +36 -19
  17. data/lib/graphql_rails/router/{mutation_action.rb → mutation_route.rb} +2 -2
  18. data/lib/graphql_rails/router/{query_action.rb → query_route.rb} +2 -2
  19. data/lib/graphql_rails/router/resource_routes_builder.rb +82 -0
  20. data/lib/graphql_rails/router/route.rb +40 -0
  21. data/lib/graphql_rails/router/schema_builder.rb +9 -5
  22. data/lib/graphql_rails/rspec_controller_helpers.rb +94 -0
  23. data/lib/graphql_rails/version.rb +1 -1
  24. metadata +14 -29
  25. data/lib/graphiti.rb +0 -10
  26. data/lib/graphiti/attribute.rb +0 -86
  27. data/lib/graphiti/controller.rb +0 -54
  28. data/lib/graphiti/controller/action_configuration.rb +0 -46
  29. data/lib/graphiti/controller/configuration.rb +0 -32
  30. data/lib/graphiti/controller/controller_function.rb +0 -41
  31. data/lib/graphiti/controller/request.rb +0 -40
  32. data/lib/graphiti/errors/execution_error.rb +0 -18
  33. data/lib/graphiti/errors/validation_error.rb +0 -26
  34. data/lib/graphiti/model.rb +0 -33
  35. data/lib/graphiti/model/configuration.rb +0 -81
  36. data/lib/graphiti/router.rb +0 -65
  37. data/lib/graphiti/router/action.rb +0 -21
  38. data/lib/graphiti/router/mutation_action.rb +0 -18
  39. data/lib/graphiti/router/query_action.rb +0 -18
  40. data/lib/graphiti/router/resource_actions_builder.rb +0 -82
  41. data/lib/graphiti/router/schema_builder.rb +0 -37
  42. data/lib/graphiti/version.rb +0 -5
  43. data/lib/graphql_rails/controller/action_path_parser.rb +0 -75
  44. data/lib/graphql_rails/router/action.rb +0 -21
  45. data/lib/graphql_rails/router/resource_actions_builder.rb +0 -82
@@ -38,8 +38,7 @@ module GraphqlRails
38
38
 
39
39
  def permit_attribute(name, type = nil)
40
40
  field_name = name.to_s.remove(/!\Z/)
41
- required = name.to_s.end_with?('!')
42
- attributes[field_name] = Attribute.new(field_name, type, required: required)
41
+ attributes[field_name] = Attribute.new(field_name, type)
43
42
  end
44
43
  end
45
44
  end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'graphql_rails/controller/action_path_parser'
3
+ require 'graphql_rails/controller/action'
4
4
  require_relative 'request'
5
+ require_relative 'action'
5
6
 
6
7
  module GraphqlRails
7
8
  class Controller
@@ -16,16 +17,16 @@ module GraphqlRails
16
17
  @type = return_type
17
18
  end
18
19
 
19
- def self.build(action_path, **options)
20
- action_parser = ActionPathParser.new(action_path, **options)
20
+ def self.from_route(route)
21
+ action = Action.new(route)
21
22
 
22
23
  action_function = Class.new(self) do
23
- action_parser.arguments.each do |action_attribute|
24
- argument(action_attribute.field_name, action_attribute.graphql_field_type)
24
+ action.arguments.each do |attribute|
25
+ argument(attribute.field_name, attribute.graphql_field_type)
25
26
  end
26
27
  end
27
28
 
28
- action_function.new(action_parser.controller, action_parser.action_name, action_parser.return_type)
29
+ action_function.new(action.controller, action.name, action.return_type)
29
30
  end
30
31
 
31
32
  def call(object, inputs, ctx)
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../errors/execution_error'
3
+ require 'graphql_rails/errors/execution_error'
4
4
 
5
5
  module GraphqlRails
6
6
  class Controller
7
7
  # Contains all info related with single request to controller
8
8
  class Request
9
9
  attr_accessor :object_to_return
10
- attr_reader :errors
10
+ attr_reader :errors, :context
11
11
 
12
12
  def initialize(graphql_object, inputs, context)
13
13
  @graphql_object = graphql_object
@@ -34,7 +34,7 @@ module GraphqlRails
34
34
 
35
35
  private
36
36
 
37
- attr_reader :graphql_object, :inputs, :context
37
+ attr_reader :graphql_object, :inputs
38
38
  end
39
39
  end
40
40
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'model/configuration'
3
+ require 'graphql_rails/model/configuration'
4
4
 
5
5
  module GraphqlRails
6
6
  # this module allows to convert any ruby class in to grapql type object
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'graphql_rails/attribute'
4
+ require 'graphql_rails/model/graphql_type_builder'
4
5
 
5
6
  module GraphqlRails
6
7
  module Model
@@ -13,68 +14,31 @@ module GraphqlRails
13
14
  @attributes = {}
14
15
  end
15
16
 
16
- def attribute(attribute_name, type: nil, hidden: false)
17
- attributes[attribute_name.to_s] = Attribute.new(attribute_name, type, hidden: hidden)
17
+ def name(type_name = nil)
18
+ @name ||= type_name
18
19
  end
19
20
 
20
- def include_model_attributes(except: [])
21
- except = Array(except).map(&:to_s)
21
+ def description(description = nil)
22
+ @description ||= description
23
+ end
22
24
 
23
- if defined?(Mongoid) && model_class < Mongoid::Document
24
- assign_default_mongoid_attributes(except: except)
25
- elsif defined?(ActiveRecord) && model_class < ActiveRecord::Base
26
- assign_default_active_record_attributes(except: except)
27
- end
25
+ def attribute(attribute_name, type: nil, hidden: false, property: attribute_name)
26
+ attributes[attribute_name.to_s] = Attribute.new(attribute_name, type, hidden: hidden, property: property)
28
27
  end
29
28
 
30
29
  def graphql_type
31
- @graphql_type ||= generate_graphql_type(graphql_type_name, visible_attributes)
30
+ @graphql_type ||= begin
31
+ type_name = name || name_by_class_name
32
+ GrapqhlTypeBuilder.new(name: type_name, description: description, attributes: attributes).call
33
+ end
32
34
  end
33
35
 
34
36
  private
35
37
 
36
38
  attr_reader :model_class
37
39
 
38
- def visible_attributes
39
- attributes.reject { |_name, attribute| attribute.hidden? }
40
- end
41
-
42
- def graphql_type_name
43
- model_class.name.split('::').last
44
- end
45
-
46
- def generate_graphql_type(type_name, attributes)
47
- GraphQL::ObjectType.define do
48
- name(type_name)
49
- description("Generated programmatically from model: #{type_name}")
50
-
51
- attributes.each_value do |attribute|
52
- field(attribute.field_name, attribute.graphql_field_type, property: attribute.name.to_sym)
53
- end
54
- end
55
- end
56
-
57
- def assign_default_mongoid_attributes(except: [])
58
- allowed_fields = model_class.fields.except('_type', '_id', *except)
59
-
60
- attribute('id', type: 'id')
61
-
62
- allowed_fields.each_value do |field|
63
- attribute(field.name, type: field.type.to_s.split('::').last)
64
- end
65
- end
66
-
67
- def assign_default_active_record_attributes(except: [])
68
- allowed_fields = model_class.columns.index_by(&:name).except('type', *except)
69
-
70
- allowed_fields.each_value do |field|
71
- field_type = field.cast_type.class.to_s.downcase.split('::').last
72
- field_type = 'string' if field_type.ends_with?('string')
73
- field_type = 'date' if field_type.include?('date')
74
- field_type = 'time' if field_type.include?('time')
75
-
76
- attribute(field.name, type: field_type.to_s.split('::').last)
77
- end
40
+ def name_by_class_name
41
+ @name_by_class_name ||= model_class.name.split('::').last
78
42
  end
79
43
  end
80
44
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlRails
4
+ module Model
5
+ # stores information about model specific config, like attributes and types
6
+ class GrapqhlTypeBuilder
7
+ def initialize(name:, description: nil, attributes:)
8
+ @name = name
9
+ @attributes = attributes
10
+ @description = description
11
+ end
12
+
13
+ def call
14
+ type_name = name
15
+ type_description = description
16
+ type_attributes = visible_attributes
17
+
18
+ GraphQL::ObjectType.define do
19
+ name(type_name)
20
+ description(type_description)
21
+
22
+ type_attributes.each_value do |attribute|
23
+ field(attribute.field_name, attribute.graphql_field_type, property: attribute.property.to_sym)
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :model_configuration, :attributes, :name, :description
31
+
32
+ def visible_attributes
33
+ attributes.reject { |_name, attribute| attribute.hidden? }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -2,64 +2,81 @@
2
2
 
3
3
  require 'active_support/core_ext/string/inflections'
4
4
 
5
- require_relative 'router/schema_builder'
6
- require_relative 'router/mutation_action'
7
- require_relative 'router/query_action'
8
- require_relative 'router/resource_actions_builder'
5
+ require 'graphql_rails/router/schema_builder'
6
+ require 'graphql_rails/router/mutation_route'
7
+ require 'graphql_rails/router/query_route'
8
+ require 'graphql_rails/router/resource_routes_builder'
9
9
 
10
10
  module GraphqlRails
11
11
  # graphql router that mimics Rails.application.routes
12
12
  class Router
13
+ RAW_ACTION_NAMES = %i[rescue_from query_analyzer instrument].freeze
14
+
13
15
  def self.draw(&block)
14
16
  router = new
15
17
  router.instance_eval(&block)
16
18
  router.graphql_schema
17
19
  end
18
20
 
19
- attr_reader :actions, :namespace_name
21
+ attr_reader :routes, :namespace_name, :raw_graphql_actions
20
22
 
21
23
  def initialize(module_name: '')
22
24
  @module_name = module_name
23
- @actions ||= Set.new
25
+ @routes ||= Set.new
26
+ @raw_graphql_actions ||= []
24
27
  end
25
28
 
26
29
  def scope(**options, &block)
27
30
  full_module_name = [module_name, options[:module]].reject(&:empty?).join('/')
28
31
  scoped_router = self.class.new(module_name: full_module_name)
29
32
  scoped_router.instance_eval(&block)
30
- actions.merge(scoped_router.actions)
33
+ routes.merge(scoped_router.routes)
31
34
  end
32
35
 
33
36
  def resources(name, **options, &block)
34
- builder_options = default_action_options.merge(options)
35
- actions_builder = ResourceActionsBuilder.new(name, **builder_options)
36
- actions_builder.instance_eval(&block) if block
37
- actions.merge(actions_builder.actions)
37
+ builder_options = default_route_options.merge(options)
38
+ routes_builder = ResourceRoutesBuilder.new(name, **builder_options)
39
+ routes_builder.instance_eval(&block) if block
40
+ routes.merge(routes_builder.routes)
38
41
  end
39
42
 
40
43
  def query(name, **options)
41
- actions << build_action(QueryAction, name, **options)
44
+ routes << build_route(QueryRoute, name, **options)
42
45
  end
43
46
 
44
47
  def mutation(name, **options)
45
- actions << build_action(MutationAction, name, **options)
48
+ routes << build_route(MutationRoute, name, **options)
49
+ end
50
+
51
+ RAW_ACTION_NAMES.each do |action_name|
52
+ define_method(action_name) do |*args, &block|
53
+ add_raw_action(action_name, *args, &block)
54
+ end
46
55
  end
47
56
 
48
57
  def graphql_schema
49
- SchemaBuilder.new(queries: actions.select(&:query?), mutations: actions.select(&:mutation?)).call
58
+ SchemaBuilder.new(
59
+ queries: routes.select(&:query?),
60
+ mutations: routes.select(&:mutation?),
61
+ raw_actions: raw_graphql_actions
62
+ ).call
50
63
  end
51
64
 
52
65
  private
53
66
 
54
67
  attr_reader :module_name
55
68
 
56
- def build_action(action_builder, name, **options)
57
- action_options = default_action_options.merge(options)
58
- action_builder.new(name, action_options)
69
+ def add_raw_action(name, *args, &block)
70
+ raw_graphql_actions << { name: name, args: args, block: block }
71
+ end
72
+
73
+ def build_route(route_builder, name, **options)
74
+ route_options = default_route_options.merge(options)
75
+ route_builder.new(name, route_options)
59
76
  end
60
77
 
61
- def default_action_options
62
- { module: module_name }
78
+ def default_route_options
79
+ { module: module_name, on: :member }
63
80
  end
64
81
  end
65
82
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'action'
3
+ require_relative 'route'
4
4
 
5
5
  module GraphqlRails
6
6
  class Router
7
7
  # stores mutation type graphql action info
8
- class MutationAction < Action
8
+ class MutationRoute < Route
9
9
  def query?
10
10
  false
11
11
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'action'
3
+ require_relative 'route'
4
4
 
5
5
  module GraphqlRails
6
6
  class Router
7
7
  # stores query type graphql action info
8
- class QueryAction < Action
8
+ class QueryRoute < Route
9
9
  def query?
10
10
  true
11
11
  end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'query_route'
4
+ require_relative 'mutation_route'
5
+
6
+ module GraphqlRails
7
+ class Router
8
+ # Generates graphql routes based on resource name and options
9
+ class ResourceRoutesBuilder
10
+ AVAILABLE_ROUTES = %i[show index create update destroy].freeze
11
+
12
+ def initialize(name, only: nil, except: [], **options)
13
+ @name = name.to_s
14
+
15
+ @options = options
16
+ @autogenerated_action_names = initial_action_names(only, except, AVAILABLE_ROUTES)
17
+ end
18
+
19
+ def routes
20
+ @routes ||= initial_routes
21
+ end
22
+
23
+ def query(*args)
24
+ routes << build_query(*args)
25
+ end
26
+
27
+ def mutation(*args)
28
+ routes << build_mutation(*args)
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :autogenerated_action_names, :name, :options
34
+
35
+ def initial_routes
36
+ routes = initial_query_routes
37
+ routes << build_mutation(:create, on: :member) if autogenerated_action_names.include?(:create)
38
+ routes << build_mutation(:update, on: :member) if autogenerated_action_names.include?(:update)
39
+ routes << build_mutation(:destroy, on: :member) if autogenerated_action_names.include?(:destroy)
40
+ routes
41
+ end
42
+
43
+ def initial_query_routes
44
+ routes = Set.new
45
+
46
+ if autogenerated_action_names.include?(:show)
47
+ routes << build_route(QueryRoute, 'show', to: "#{name}#show", prefix: '', on: :member)
48
+ end
49
+
50
+ if autogenerated_action_names.include?(:index)
51
+ routes << build_route(QueryRoute, 'index', to: "#{name}#index", prefix: '', on: :collection)
52
+ end
53
+
54
+ routes
55
+ end
56
+
57
+ def build_mutation(*args)
58
+ build_route(MutationRoute, *args)
59
+ end
60
+
61
+ def build_query(*args)
62
+ build_route(QueryRoute, *args)
63
+ end
64
+
65
+ def build_route(builder, action, prefix: action, on: :member, **custom_options)
66
+ action_options = options.merge(custom_options)
67
+ action_name = [prefix, resource_name(on)].reject(&:empty?).join('_')
68
+ builder.new(action_name, to: "#{name}##{action}", **action_options)
69
+ end
70
+
71
+ def initial_action_names(only, except, available)
72
+ alowed_routes = Array(only || available) & available
73
+ only_routes = alowed_routes.map(&:to_sym) - Array(except).map(&:to_sym)
74
+ Set.new(only_routes)
75
+ end
76
+
77
+ def resource_name(type)
78
+ type.to_sym == :member ? name.singularize : name
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../controller/controller_function'
4
+
5
+ module GraphqlRails
6
+ class Router
7
+ # Generic class for any type graphql action. Should not be used directly
8
+ class Route
9
+ attr_reader :name, :module_name, :on
10
+
11
+ def initialize(name, to:, on:, **options)
12
+ @name = name.to_s.camelize(:lower)
13
+ @module_name = options[:module].to_s
14
+ @relative_path = to
15
+ @on = on.to_sym
16
+ end
17
+
18
+ def path
19
+ return relative_path if module_name.empty?
20
+ [module_name, relative_path].join('/')
21
+ end
22
+
23
+ def collection?
24
+ on == :collection
25
+ end
26
+
27
+ def member?
28
+ on == :member
29
+ end
30
+
31
+ def options
32
+ { function: Controller::ControllerFunction.build(path) }
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :relative_path
38
+ end
39
+ end
40
+ end