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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +2 -2
- data/README.md +76 -8
- data/graphql_rails.gemspec +1 -1
- data/lib/graphql_rails/attribute.rb +7 -42
- data/lib/graphql_rails/attribute/attribute_type_parser.rb +99 -0
- data/lib/graphql_rails/controller.rb +17 -5
- data/lib/graphql_rails/controller/action.rb +93 -0
- data/lib/graphql_rails/controller/action_configuration.rb +1 -2
- data/lib/graphql_rails/controller/controller_function.rb +7 -6
- data/lib/graphql_rails/controller/request.rb +3 -3
- data/lib/graphql_rails/model.rb +1 -1
- data/lib/graphql_rails/model/configuration.rb +14 -50
- data/lib/graphql_rails/model/graphql_type_builder.rb +37 -0
- data/lib/graphql_rails/router.rb +36 -19
- data/lib/graphql_rails/router/{mutation_action.rb → mutation_route.rb} +2 -2
- data/lib/graphql_rails/router/{query_action.rb → query_route.rb} +2 -2
- data/lib/graphql_rails/router/resource_routes_builder.rb +82 -0
- data/lib/graphql_rails/router/route.rb +40 -0
- data/lib/graphql_rails/router/schema_builder.rb +9 -5
- data/lib/graphql_rails/rspec_controller_helpers.rb +94 -0
- data/lib/graphql_rails/version.rb +1 -1
- metadata +14 -29
- data/lib/graphiti.rb +0 -10
- data/lib/graphiti/attribute.rb +0 -86
- data/lib/graphiti/controller.rb +0 -54
- data/lib/graphiti/controller/action_configuration.rb +0 -46
- data/lib/graphiti/controller/configuration.rb +0 -32
- data/lib/graphiti/controller/controller_function.rb +0 -41
- data/lib/graphiti/controller/request.rb +0 -40
- data/lib/graphiti/errors/execution_error.rb +0 -18
- data/lib/graphiti/errors/validation_error.rb +0 -26
- data/lib/graphiti/model.rb +0 -33
- data/lib/graphiti/model/configuration.rb +0 -81
- data/lib/graphiti/router.rb +0 -65
- data/lib/graphiti/router/action.rb +0 -21
- data/lib/graphiti/router/mutation_action.rb +0 -18
- data/lib/graphiti/router/query_action.rb +0 -18
- data/lib/graphiti/router/resource_actions_builder.rb +0 -82
- data/lib/graphiti/router/schema_builder.rb +0 -37
- data/lib/graphiti/version.rb +0 -5
- data/lib/graphql_rails/controller/action_path_parser.rb +0 -75
- data/lib/graphql_rails/router/action.rb +0 -21
- 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
|
-
|
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/
|
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.
|
20
|
-
|
20
|
+
def self.from_route(route)
|
21
|
+
action = Action.new(route)
|
21
22
|
|
22
23
|
action_function = Class.new(self) do
|
23
|
-
|
24
|
-
argument(
|
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(
|
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
|
-
|
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
|
37
|
+
attr_reader :graphql_object, :inputs
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
data/lib/graphql_rails/model.rb
CHANGED
@@ -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
|
17
|
-
|
17
|
+
def name(type_name = nil)
|
18
|
+
@name ||= type_name
|
18
19
|
end
|
19
20
|
|
20
|
-
def
|
21
|
-
|
21
|
+
def description(description = nil)
|
22
|
+
@description ||= description
|
23
|
+
end
|
22
24
|
|
23
|
-
|
24
|
-
|
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 ||=
|
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
|
39
|
-
|
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
|
data/lib/graphql_rails/router.rb
CHANGED
@@ -2,64 +2,81 @@
|
|
2
2
|
|
3
3
|
require 'active_support/core_ext/string/inflections'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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 :
|
21
|
+
attr_reader :routes, :namespace_name, :raw_graphql_actions
|
20
22
|
|
21
23
|
def initialize(module_name: '')
|
22
24
|
@module_name = module_name
|
23
|
-
@
|
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
|
-
|
33
|
+
routes.merge(scoped_router.routes)
|
31
34
|
end
|
32
35
|
|
33
36
|
def resources(name, **options, &block)
|
34
|
-
builder_options =
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
44
|
+
routes << build_route(QueryRoute, name, **options)
|
42
45
|
end
|
43
46
|
|
44
47
|
def mutation(name, **options)
|
45
|
-
|
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(
|
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
|
57
|
-
|
58
|
-
|
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
|
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 '
|
3
|
+
require_relative 'route'
|
4
4
|
|
5
5
|
module GraphqlRails
|
6
6
|
class Router
|
7
7
|
# stores mutation type graphql action info
|
8
|
-
class
|
8
|
+
class MutationRoute < Route
|
9
9
|
def query?
|
10
10
|
false
|
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
|