graphql_rails 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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