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
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'graphql_rails/controller/action_path_parser'
4
- require_relative 'request'
5
-
6
- module GraphqlRails
7
- class Controller
8
- # graphql resolver which redirects actions to appropriate controller and controller action
9
- class ControllerFunction < GraphQL::Function
10
- # accepts path of given format "controller_name#action"
11
- attr_reader :type
12
-
13
- def initialize(controller, action_name, return_type)
14
- @controller = controller
15
- @action_name = action_name
16
- @type = return_type
17
- end
18
-
19
- def self.build(action_path, **options)
20
- action_parser = ActionPathParser.new(action_path, **options)
21
-
22
- 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)
25
- end
26
- end
27
-
28
- action_function.new(action_parser.controller, action_parser.action_name, action_parser.return_type)
29
- end
30
-
31
- def call(object, inputs, ctx)
32
- request = Request.new(object, inputs, ctx)
33
- controller.new(request).call(action_name)
34
- end
35
-
36
- private
37
-
38
- attr_reader :controller, :action_name
39
- end
40
- end
41
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../errors/execution_error'
4
-
5
- module GraphqlRails
6
- class Controller
7
- # Contains all info related with single request to controller
8
- class Request
9
- attr_accessor :object_to_return
10
- attr_reader :errors
11
-
12
- def initialize(graphql_object, inputs, context)
13
- @graphql_object = graphql_object
14
- @inputs = inputs
15
- @context = context
16
- end
17
-
18
- def errors=(new_errors)
19
- @errors = new_errors
20
-
21
- new_errors.each do |error|
22
- error_message = error.is_a?(String) ? error : error.message
23
- context.add_error(ExecutionError.new(error_message))
24
- end
25
- end
26
-
27
- def no_object_to_return?
28
- !defined?(@object_to_return)
29
- end
30
-
31
- def params
32
- inputs.to_h
33
- end
34
-
35
- private
36
-
37
- attr_reader :graphql_object, :inputs, :context
38
- end
39
- end
40
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module GraphqlRails
4
- # base class which is returned in case something bad happens. Contains all error rendering tructure
5
- class ExecutionError < GraphQL::ExecutionError
6
- def to_h
7
- super.except('locations').merge('type' => type, 'http_status_code' => http_status_code)
8
- end
9
-
10
- def type
11
- 'system_error'
12
- end
13
-
14
- def http_status_code
15
- 500
16
- end
17
- end
18
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module GraphqlRails
4
- # GrapqhQL error that is raised when invalid data is given
5
- class ValidationError < ExecutionError
6
- attr_reader :short_message, :field
7
-
8
- def initialize(short_message, field)
9
- super([field.presence, short_message].compact.join(' '))
10
- @short_message = short_message
11
- @field = field
12
- end
13
-
14
- def type
15
- 'validation_error'
16
- end
17
-
18
- def http_status_code
19
- 422
20
- end
21
-
22
- def to_h
23
- super.merge('field' => field, 'short_message' => short_message)
24
- end
25
- end
26
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'model/configuration'
4
-
5
- module GraphqlRails
6
- # this module allows to convert any ruby class in to grapql type object
7
- #
8
- # usage:
9
- # class YourModel
10
- # include GraphqlRails::Model
11
- #
12
- # graphql do
13
- # attribute :id
14
- # attribute :title
15
- # end
16
- # end
17
- #
18
- # YourModel.new.grapql_type # => type with [:id, :title] attributes
19
- module Model
20
- def self.included(base)
21
- base.extend(ClassMethods)
22
- end
23
-
24
- # static methods for GraphqlRails::Model
25
- module ClassMethods
26
- def graphql
27
- @graphql ||= Model::Configuration.new(self)
28
- yield(@graphql) if block_given?
29
- @graphql
30
- end
31
- end
32
- end
33
- end
@@ -1,81 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'graphql_rails/attribute'
4
-
5
- module GraphqlRails
6
- module Model
7
- # stores information about model specific config, like attributes and types
8
- class Configuration
9
- attr_reader :attributes
10
-
11
- def initialize(model_class)
12
- @model_class = model_class
13
- @attributes = {}
14
- end
15
-
16
- def attribute(attribute_name, type: nil, hidden: false)
17
- attributes[attribute_name.to_s] = Attribute.new(attribute_name, type, hidden: hidden)
18
- end
19
-
20
- def include_model_attributes(except: [])
21
- except = Array(except).map(&:to_s)
22
-
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
28
- end
29
-
30
- def graphql_type
31
- @graphql_type ||= generate_graphql_type(graphql_type_name, visible_attributes)
32
- end
33
-
34
- private
35
-
36
- attr_reader :model_class
37
-
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
78
- end
79
- end
80
- end
81
- end
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/core_ext/string/inflections'
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'
9
-
10
- module GraphqlRails
11
- # graphql router that mimics Rails.application.routes
12
- class Router
13
- def self.draw(&block)
14
- router = new
15
- router.instance_eval(&block)
16
- router.graphql_schema
17
- end
18
-
19
- attr_reader :actions, :namespace_name
20
-
21
- def initialize(module_name: '')
22
- @module_name = module_name
23
- @actions ||= Set.new
24
- end
25
-
26
- def scope(**options, &block)
27
- full_module_name = [module_name, options[:module]].reject(&:empty?).join('/')
28
- scoped_router = self.class.new(module_name: full_module_name)
29
- scoped_router.instance_eval(&block)
30
- actions.merge(scoped_router.actions)
31
- end
32
-
33
- 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)
38
- end
39
-
40
- def query(name, **options)
41
- actions << build_action(QueryAction, name, **options)
42
- end
43
-
44
- def mutation(name, **options)
45
- actions << build_action(MutationAction, name, **options)
46
- end
47
-
48
- def graphql_schema
49
- SchemaBuilder.new(queries: actions.select(&:query?), mutations: actions.select(&:mutation?)).call
50
- end
51
-
52
- private
53
-
54
- attr_reader :module_name
55
-
56
- def build_action(action_builder, name, **options)
57
- action_options = default_action_options.merge(options)
58
- action_builder.new(name, action_options)
59
- end
60
-
61
- def default_action_options
62
- { module: module_name }
63
- end
64
- end
65
- end
@@ -1,21 +0,0 @@
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 Action
9
- attr_reader :name, :controller_action_path
10
-
11
- def initialize(name, to:, **options)
12
- @name = name.to_s.camelize(:lower)
13
- @controller_action_path = [options[:module].to_s, to].reject(&:empty?).join('/')
14
- end
15
-
16
- def options
17
- { function: Controller::ControllerFunction.build(controller_action_path) }
18
- end
19
- end
20
- end
21
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'action'
4
-
5
- module GraphqlRails
6
- class Router
7
- # stores mutation type graphql action info
8
- class MutationAction < Action
9
- def query?
10
- false
11
- end
12
-
13
- def mutation?
14
- true
15
- end
16
- end
17
- end
18
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'action'
4
-
5
- module GraphqlRails
6
- class Router
7
- # stores query type graphql action info
8
- class QueryAction < Action
9
- def query?
10
- true
11
- end
12
-
13
- def mutation?
14
- false
15
- end
16
- end
17
- end
18
- end
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'query_action'
4
- require_relative 'mutation_action'
5
-
6
- module GraphqlRails
7
- class Router
8
- # Generates graphql actions based on resource name and options
9
- class ResourceActionsBuilder
10
- AVAILABLE_ACTIONS = %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_ACTIONS)
17
- end
18
-
19
- def actions
20
- @actions ||= initial_actions
21
- end
22
-
23
- def query(*args)
24
- actions << build_query(*args)
25
- end
26
-
27
- def mutation(*args)
28
- actions << build_mutation(*args)
29
- end
30
-
31
- private
32
-
33
- attr_reader :autogenerated_action_names, :name, :options
34
-
35
- def initial_actions
36
- actions = initial_query_actions
37
- actions << build_mutation(:create, on: :member) if autogenerated_action_names.include?(:create)
38
- actions << build_mutation(:update, on: :member) if autogenerated_action_names.include?(:update)
39
- actions << build_mutation(:destroy, on: :member) if autogenerated_action_names.include?(:destroy)
40
- actions
41
- end
42
-
43
- def initial_query_actions
44
- actions = Set.new
45
-
46
- if autogenerated_action_names.include?(:show)
47
- actions << build_action(QueryAction, 'show', to: "#{name}#show", prefix: '', on: :member)
48
- end
49
-
50
- if autogenerated_action_names.include?(:index)
51
- actions << build_action(QueryAction, 'index', to: "#{name}#index", prefix: '', on: :collection)
52
- end
53
-
54
- actions
55
- end
56
-
57
- def build_mutation(*args)
58
- build_action(MutationAction, *args)
59
- end
60
-
61
- def build_query(*args)
62
- build_action(QueryAction, *args)
63
- end
64
-
65
- def build_action(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_actions = Array(only || available) & available
73
- only_actions = alowed_actions.map(&:to_sym) - Array(except).map(&:to_sym)
74
- Set.new(only_actions)
75
- end
76
-
77
- def resource_name(type)
78
- type.to_sym == :member ? name.singularize : name
79
- end
80
- end
81
- end
82
- end