graphql_rails 0.8.0 → 1.2.2
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.
- checksums.yaml +4 -4
- data/.hound.yml +1 -0
- data/.rubocop.yml +3 -3
- data/.ruby-version +1 -1
- data/.travis.yml +2 -2
- data/CHANGELOG.md +31 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +147 -124
- data/docs/README.md +24 -8
- data/docs/_sidebar.md +3 -0
- data/docs/components/controller.md +194 -21
- data/docs/components/model.md +193 -5
- data/docs/components/routes.md +28 -0
- data/docs/getting_started/quick_start.md +10 -3
- data/docs/index.html +1 -1
- data/docs/other_tools/query_runner.md +49 -0
- data/docs/other_tools/schema_dump.md +29 -0
- data/docs/testing/testing.md +3 -1
- data/graphql_rails.gemspec +5 -5
- data/lib/generators/graphql_rails/install_generator.rb +50 -0
- data/lib/generators/graphql_rails/templates/example_users_controller.erb +19 -0
- data/lib/generators/graphql_rails/templates/graphql_application_controller.erb +8 -0
- data/lib/generators/graphql_rails/templates/graphql_controller.erb +20 -0
- data/lib/generators/graphql_rails/templates/graphql_router.erb +19 -0
- data/lib/generators/graphql_rails/templates/graphql_router_spec.erb +21 -0
- data/lib/graphql_rails.rb +2 -0
- data/lib/graphql_rails/attributes/attributable.rb +20 -21
- data/lib/graphql_rails/attributes/attribute.rb +41 -4
- data/lib/graphql_rails/attributes/attribute_name_parser.rb +4 -4
- data/lib/graphql_rails/attributes/input_attribute.rb +24 -10
- data/lib/graphql_rails/attributes/input_type_parser.rb +24 -46
- data/lib/graphql_rails/attributes/type_parseable.rb +132 -0
- data/lib/graphql_rails/attributes/type_parser.rb +58 -54
- data/lib/graphql_rails/concerns/service.rb +19 -0
- data/lib/graphql_rails/controller.rb +26 -22
- data/lib/graphql_rails/controller/action.rb +12 -67
- data/lib/graphql_rails/controller/action_configuration.rb +70 -34
- data/lib/graphql_rails/controller/build_controller_action_resolver.rb +52 -0
- data/lib/graphql_rails/controller/build_controller_action_resolver/controller_action_resolver.rb +28 -0
- data/lib/graphql_rails/controller/configuration.rb +56 -5
- data/lib/graphql_rails/controller/log_controller_action.rb +11 -6
- data/lib/graphql_rails/controller/request.rb +29 -8
- data/lib/graphql_rails/controller/request/format_errors.rb +58 -0
- data/lib/graphql_rails/decorator/relation_decorator.rb +1 -5
- data/lib/graphql_rails/errors/custom_execution_error.rb +22 -0
- data/lib/graphql_rails/errors/execution_error.rb +6 -7
- data/lib/graphql_rails/errors/system_error.rb +14 -0
- data/lib/graphql_rails/errors/validation_error.rb +1 -5
- data/lib/graphql_rails/input_configurable.rb +47 -0
- data/lib/graphql_rails/model.rb +19 -4
- data/lib/graphql_rails/model/add_fields_to_graphql_type.rb +45 -0
- data/lib/graphql_rails/model/build_connection_type.rb +52 -0
- data/lib/graphql_rails/model/{configuration → build_connection_type}/count_items.rb +5 -5
- data/lib/graphql_rails/model/build_enum_type.rb +39 -10
- data/lib/graphql_rails/model/build_graphql_input_type.rb +8 -4
- data/lib/graphql_rails/model/call_graphql_model_method.rb +72 -0
- data/lib/graphql_rails/model/configurable.rb +6 -2
- data/lib/graphql_rails/model/configuration.rb +11 -10
- data/lib/graphql_rails/model/find_or_build_graphql_type.rb +64 -0
- data/lib/graphql_rails/model/find_or_build_graphql_type_class.rb +46 -0
- data/lib/graphql_rails/model/input.rb +10 -6
- data/lib/graphql_rails/query_runner.rb +68 -0
- data/lib/graphql_rails/railtie.rb +10 -0
- data/lib/graphql_rails/router.rb +40 -13
- data/lib/graphql_rails/router/resource_routes_builder.rb +10 -9
- data/lib/graphql_rails/router/route.rb +21 -6
- data/lib/graphql_rails/router/schema_builder.rb +30 -11
- data/lib/graphql_rails/rspec_controller_helpers.rb +6 -4
- data/lib/graphql_rails/tasks/dump_graphql_schema.rb +57 -0
- data/lib/graphql_rails/tasks/schema.rake +14 -0
- data/lib/graphql_rails/version.rb +1 -1
- metadata +48 -21
- data/lib/graphql_rails/controller/controller_function.rb +0 -50
- data/lib/graphql_rails/controller/format_results.rb +0 -36
- data/lib/graphql_rails/model/build_graphql_type.rb +0 -37
data/lib/graphql_rails/model.rb
CHANGED
@@ -17,10 +17,6 @@ module GraphqlRails
|
|
17
17
|
#
|
18
18
|
# YourModel.new.graphql_type # => type with [:id, :title] attributes
|
19
19
|
module Model
|
20
|
-
def self.included(base)
|
21
|
-
base.extend(ClassMethods)
|
22
|
-
end
|
23
|
-
|
24
20
|
# static methods for GraphqlRails::Model
|
25
21
|
module ClassMethods
|
26
22
|
def inherited(subclass)
|
@@ -36,5 +32,24 @@ module GraphqlRails
|
|
36
32
|
@graphql
|
37
33
|
end
|
38
34
|
end
|
35
|
+
|
36
|
+
def self.included(base)
|
37
|
+
base.extend(ClassMethods)
|
38
|
+
end
|
39
|
+
|
40
|
+
def graphql_context
|
41
|
+
@graphql_context
|
42
|
+
end
|
43
|
+
|
44
|
+
def graphql_context=(value)
|
45
|
+
@graphql_context = value
|
46
|
+
end
|
47
|
+
|
48
|
+
def with_graphql_context(graphql_context)
|
49
|
+
self.graphql_context = graphql_context
|
50
|
+
yield(self)
|
51
|
+
ensure
|
52
|
+
self.graphql_context = nil
|
53
|
+
end
|
39
54
|
end
|
40
55
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
module Model
|
5
|
+
# Adds graphql attributes as graphql fields to given graphql schema object.
|
6
|
+
class AddFieldsToGraphqlType
|
7
|
+
require 'graphql_rails/concerns/service'
|
8
|
+
require 'graphql_rails/model/call_graphql_model_method'
|
9
|
+
|
10
|
+
include ::GraphqlRails::Service
|
11
|
+
|
12
|
+
def initialize(klass:, attributes:)
|
13
|
+
@klass = klass
|
14
|
+
@attributes = attributes
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
attributes.each { |attribute| define_graphql_field(attribute) }
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :attributes, :klass
|
24
|
+
|
25
|
+
def define_graphql_field(attribute) # rubocop:disable Metrics/MethodLength)
|
26
|
+
klass.class_eval do
|
27
|
+
field(*attribute.field_args, **attribute.field_options) do
|
28
|
+
attribute.attributes.values.each do |arg_attribute|
|
29
|
+
argument(*arg_attribute.input_argument_args, **arg_attribute.input_argument_options)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
define_method(attribute.field_name) do |**kwargs|
|
34
|
+
CallGraphqlModelMethod.call(
|
35
|
+
model: object,
|
36
|
+
attribute_config: attribute,
|
37
|
+
method_keyword_arguments: kwargs,
|
38
|
+
graphql_context: context
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'graphql'
|
4
|
+
require 'graphql_rails/model/build_connection_type/count_items'
|
5
|
+
|
6
|
+
module GraphqlRails
|
7
|
+
module Model
|
8
|
+
# builds connection type from graphql type with some extra attributes
|
9
|
+
class BuildConnectionType
|
10
|
+
require 'graphql_rails/concerns/service'
|
11
|
+
|
12
|
+
include ::GraphqlRails::Service
|
13
|
+
|
14
|
+
attr_reader :initial_type
|
15
|
+
|
16
|
+
def initialize(initial_type)
|
17
|
+
@initial_type = initial_type
|
18
|
+
end
|
19
|
+
|
20
|
+
def call
|
21
|
+
build_connection_type
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def build_connection_type
|
27
|
+
edge_type = build_edge_type
|
28
|
+
type = initial_type
|
29
|
+
Class.new(GraphQL::Types::Relay::BaseConnection) do
|
30
|
+
graphql_name("#{type.graphql_name}Connection")
|
31
|
+
edge_type(edge_type)
|
32
|
+
|
33
|
+
field :total, Integer, null: false
|
34
|
+
|
35
|
+
def total
|
36
|
+
CountItems.call(self)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_edge_type
|
42
|
+
type = initial_type
|
43
|
+
|
44
|
+
Class.new(GraphQL::Types::Relay::BaseEdge) do
|
45
|
+
graphql_name("#{type.graphql_name}Edge")
|
46
|
+
|
47
|
+
node_type(type)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -2,15 +2,15 @@
|
|
2
2
|
|
3
3
|
module GraphqlRails
|
4
4
|
module Model
|
5
|
-
class
|
5
|
+
class BuildConnectionType
|
6
6
|
# Used when generating ConnectionType.
|
7
7
|
# It handles all the logic which is related with counting total items
|
8
8
|
class CountItems
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
require 'graphql_rails/concerns/service'
|
10
|
+
|
11
|
+
include ::GraphqlRails::Service
|
12
12
|
|
13
|
-
def initialize(graphql_object
|
13
|
+
def initialize(graphql_object)
|
14
14
|
@graphql_object = graphql_object
|
15
15
|
end
|
16
16
|
|
@@ -7,9 +7,10 @@ module GraphqlRails
|
|
7
7
|
module Model
|
8
8
|
# contains info about single graphql attribute
|
9
9
|
class BuildEnumType
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
class InvalidEnum < GraphqlRails::Error; end
|
11
|
+
require 'graphql_rails/concerns/service'
|
12
|
+
|
13
|
+
include ::GraphqlRails::Service
|
13
14
|
|
14
15
|
def initialize(name, allowed_values:, description: nil)
|
15
16
|
@name = name
|
@@ -18,22 +19,50 @@ module GraphqlRails
|
|
18
19
|
end
|
19
20
|
|
20
21
|
def call
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
validate
|
23
|
+
build_enum
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
attr_reader :name, :allowed_values, :description
|
29
|
+
|
30
|
+
def validate
|
31
|
+
return if allowed_values.is_a?(Array) && !allowed_values.empty?
|
32
|
+
|
33
|
+
validate_enum_type
|
34
|
+
validate_enum_content
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate_enum_type
|
38
|
+
return if allowed_values.is_a?(Array)
|
39
|
+
|
40
|
+
raise InvalidEnum, "Enum must be instance of Array, but instance of #{allowed_values.class} was given"
|
41
|
+
end
|
42
|
+
|
43
|
+
def validate_enum_content
|
44
|
+
return unless allowed_values.empty?
|
45
|
+
|
46
|
+
raise InvalidEnum, 'At lest one enum option must be given'
|
47
|
+
end
|
48
|
+
|
49
|
+
def formatted_name
|
50
|
+
name.to_s.camelize
|
51
|
+
end
|
24
52
|
|
53
|
+
def build_enum(allowed_values: self.allowed_values, enum_name: formatted_name, enum_description: description)
|
25
54
|
Class.new(GraphQL::Schema::Enum) do
|
26
55
|
allowed_values.each do |allowed_value|
|
27
56
|
graphql_name(enum_name)
|
28
57
|
description(enum_description) if enum_description
|
29
58
|
value(allowed_value.to_s.underscore.upcase, value: allowed_value)
|
30
59
|
end
|
60
|
+
|
61
|
+
def self.inspect
|
62
|
+
"#{GraphQL::Schema::Enum}(#{graphql_name})"
|
63
|
+
end
|
31
64
|
end
|
32
65
|
end
|
33
|
-
|
34
|
-
protected
|
35
|
-
|
36
|
-
attr_reader :name, :allowed_values, :description
|
37
66
|
end
|
38
67
|
end
|
39
68
|
end
|
@@ -4,9 +4,9 @@ module GraphqlRails
|
|
4
4
|
module Model
|
5
5
|
# stores information about model specific config, like attributes and types
|
6
6
|
class BuildGraphqlInputType
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
require 'graphql_rails/concerns/service'
|
8
|
+
|
9
|
+
include ::GraphqlRails::Service
|
10
10
|
|
11
11
|
def initialize(name:, description: nil, attributes:)
|
12
12
|
@name = name
|
@@ -24,7 +24,11 @@ module GraphqlRails
|
|
24
24
|
description(type_description)
|
25
25
|
|
26
26
|
type_attributes.each_value do |type_attribute|
|
27
|
-
argument(*type_attribute.input_argument_args)
|
27
|
+
argument(*type_attribute.input_argument_args, **type_attribute.input_argument_options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.inspect
|
31
|
+
"#{GraphQL::Schema::InputObject}(#{graphql_name})"
|
28
32
|
end
|
29
33
|
end
|
30
34
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
module Model
|
5
|
+
# Executes model method and adds additional meta data if needed
|
6
|
+
class CallGraphqlModelMethod
|
7
|
+
require 'graphql_rails/concerns/service'
|
8
|
+
|
9
|
+
include ::GraphqlRails::Service
|
10
|
+
|
11
|
+
PAGINATION_KEYS = %i[before after first last].freeze
|
12
|
+
|
13
|
+
def initialize(model:, method_keyword_arguments:, graphql_context:, attribute_config:)
|
14
|
+
@model = model
|
15
|
+
@method_keyword_arguments = method_keyword_arguments
|
16
|
+
@graphql_context = graphql_context
|
17
|
+
@attribute_config = attribute_config
|
18
|
+
end
|
19
|
+
|
20
|
+
def call
|
21
|
+
with_graphql_context do
|
22
|
+
run_method
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :model, :attribute_config, :graphql_context, :method_keyword_arguments
|
29
|
+
|
30
|
+
def run_method
|
31
|
+
if custom_keyword_arguments.empty?
|
32
|
+
model.send(method_name)
|
33
|
+
else
|
34
|
+
formatted_arguments = formatted_method_input(custom_keyword_arguments)
|
35
|
+
model.send(method_name, **formatted_arguments)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def formatted_method_input(keyword_arguments)
|
40
|
+
keyword_arguments.transform_values do |input_argument|
|
41
|
+
formatted_method_input_argument(input_argument)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def formatted_method_input_argument(argument)
|
46
|
+
return argument.to_h if argument.is_a?(GraphQL::Schema::InputObject)
|
47
|
+
|
48
|
+
argument
|
49
|
+
end
|
50
|
+
|
51
|
+
def method_name
|
52
|
+
attribute_config.property
|
53
|
+
end
|
54
|
+
|
55
|
+
def paginated?
|
56
|
+
attribute_config.paginated?
|
57
|
+
end
|
58
|
+
|
59
|
+
def custom_keyword_arguments
|
60
|
+
return method_keyword_arguments unless paginated?
|
61
|
+
|
62
|
+
method_keyword_arguments.except(*PAGINATION_KEYS)
|
63
|
+
end
|
64
|
+
|
65
|
+
def with_graphql_context
|
66
|
+
return yield unless model.respond_to?(:with_graphql_context)
|
67
|
+
|
68
|
+
model.with_graphql_context(graphql_context) { yield }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -9,11 +9,15 @@ module GraphqlRails
|
|
9
9
|
@attributes ||= {}
|
10
10
|
end
|
11
11
|
|
12
|
-
def name(
|
13
|
-
@name =
|
12
|
+
def name(graphql_name = nil)
|
13
|
+
@name = graphql_name if graphql_name
|
14
14
|
@name || default_name
|
15
15
|
end
|
16
16
|
|
17
|
+
def type_name
|
18
|
+
@type_name ||= "#{name.camelize}Type#{SecureRandom.hex}"
|
19
|
+
end
|
20
|
+
|
17
21
|
def description(new_description = nil)
|
18
22
|
@description = new_description if new_description
|
19
23
|
@description
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'graphql_rails/attributes'
|
4
|
-
require 'graphql_rails/model/
|
4
|
+
require 'graphql_rails/model/find_or_build_graphql_type'
|
5
5
|
require 'graphql_rails/model/build_enum_type'
|
6
6
|
require 'graphql_rails/model/input'
|
7
7
|
require 'graphql_rails/model/configurable'
|
8
|
-
require 'graphql_rails/model/
|
8
|
+
require 'graphql_rails/model/build_connection_type'
|
9
9
|
|
10
10
|
module GraphqlRails
|
11
11
|
module Model
|
@@ -32,7 +32,9 @@ module GraphqlRails
|
|
32
32
|
|
33
33
|
attributes[key].tap do |attribute|
|
34
34
|
attribute_options.each do |method_name, args|
|
35
|
-
|
35
|
+
send_args = [method_name]
|
36
|
+
send_args << args if attribute.method(method_name).parameters.present?
|
37
|
+
attribute.public_send(*send_args)
|
36
38
|
end
|
37
39
|
|
38
40
|
yield(attribute) if block_given?
|
@@ -54,17 +56,16 @@ module GraphqlRails
|
|
54
56
|
end
|
55
57
|
|
56
58
|
def graphql_type
|
57
|
-
@graphql_type ||=
|
58
|
-
name: name,
|
59
|
+
@graphql_type ||= FindOrBuildGraphqlType.call(
|
60
|
+
name: name,
|
61
|
+
description: description,
|
62
|
+
attributes: attributes,
|
63
|
+
type_name: type_name
|
59
64
|
)
|
60
65
|
end
|
61
66
|
|
62
67
|
def connection_type
|
63
|
-
@connection_type ||=
|
64
|
-
graphql_type.define_connection do
|
65
|
-
field :total, types.Int, resolve: CountItems
|
66
|
-
end
|
67
|
-
end
|
68
|
+
@connection_type ||= BuildConnectionType.call(graphql_type)
|
68
69
|
end
|
69
70
|
|
70
71
|
private
|
@@ -0,0 +1,64 @@
|
|
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 FindOrBuildGraphqlType
|
7
|
+
require 'graphql_rails/concerns/service'
|
8
|
+
require 'graphql_rails/model/find_or_build_graphql_type_class'
|
9
|
+
require 'graphql_rails/model/add_fields_to_graphql_type'
|
10
|
+
|
11
|
+
include ::GraphqlRails::Service
|
12
|
+
|
13
|
+
def initialize(name:, description:, attributes:, type_name:)
|
14
|
+
@name = name
|
15
|
+
@description = description
|
16
|
+
@attributes = attributes
|
17
|
+
@type_name = type_name
|
18
|
+
end
|
19
|
+
|
20
|
+
def call
|
21
|
+
klass.tap { add_fields_to_graphql_type if new_class? }
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :name, :description, :attributes, :type_name
|
27
|
+
|
28
|
+
delegate :klass, :new_class?, to: :type_class_finder
|
29
|
+
|
30
|
+
def type_class_finder
|
31
|
+
@type_class_finder ||= FindOrBuildGraphqlTypeClass.new(
|
32
|
+
name: name,
|
33
|
+
type_name: type_name,
|
34
|
+
description: description
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_fields_to_graphql_type
|
39
|
+
AddFieldsToGraphqlType.call(klass: klass, attributes: attributes.values.select(&:scalar_type?))
|
40
|
+
|
41
|
+
attributes.values.reject(&:scalar_type?).tap do |dynamic_attributes|
|
42
|
+
find_or_build_dynamic_graphql_types(dynamic_attributes) do |name, description, attributes, type_name|
|
43
|
+
self.class.call(
|
44
|
+
name: name, description: description,
|
45
|
+
attributes: attributes, type_name: type_name
|
46
|
+
)
|
47
|
+
end
|
48
|
+
AddFieldsToGraphqlType.call(klass: klass, attributes: dynamic_attributes)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def find_or_build_dynamic_graphql_types(dynamic_attributes)
|
53
|
+
dynamic_attributes.each do |attribute|
|
54
|
+
yield(
|
55
|
+
attribute.graphql_model.graphql.name,
|
56
|
+
attribute.graphql_model.graphql.description,
|
57
|
+
attribute.graphql_model.graphql.attributes,
|
58
|
+
attribute.graphql_model.graphql.type_name
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|