graphql_rails 1.2.3 → 2.1.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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +18 -0
- data/.hound.yml +1 -1
- data/.rubocop.yml +4 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +19 -0
- data/Gemfile +3 -3
- data/Gemfile.lock +27 -25
- data/docs/README.md +1 -1
- data/docs/components/model.md +107 -13
- data/docs/other_tools/schema_dump.md +8 -8
- data/lib/graphql_rails/attributes/attributable.rb +6 -14
- data/lib/graphql_rails/attributes/attribute.rb +10 -34
- data/lib/graphql_rails/attributes/attribute_configurable.rb +45 -0
- data/lib/graphql_rails/attributes/attribute_name_parser.rb +7 -7
- data/lib/graphql_rails/attributes/input_attribute.rb +20 -14
- data/lib/graphql_rails/attributes/input_type_parser.rb +5 -7
- data/lib/graphql_rails/attributes/type_parseable.rb +55 -19
- data/lib/graphql_rails/attributes/type_parser.rb +4 -9
- data/lib/graphql_rails/concerns/chainable_options.rb +49 -0
- data/lib/graphql_rails/controller/build_controller_action_resolver/controller_action_resolver.rb +3 -0
- data/lib/graphql_rails/decorator/relation_decorator.rb +2 -2
- data/lib/graphql_rails/input_configurable.rb +10 -8
- data/lib/graphql_rails/model/build_graphql_input_type.rb +4 -2
- data/lib/graphql_rails/model/configurable.rb +30 -8
- data/lib/graphql_rails/model/configuration.rb +26 -12
- data/lib/graphql_rails/model/find_or_build_graphql_type.rb +23 -21
- data/lib/graphql_rails/model/find_or_build_graphql_type_class.rb +2 -1
- data/lib/graphql_rails/model/input.rb +10 -26
- data/lib/graphql_rails/model.rb +1 -1
- data/lib/graphql_rails/query_runner.rb +9 -3
- data/lib/graphql_rails/router/route.rb +2 -2
- data/lib/graphql_rails/tasks/dump_graphql_schema.rb +18 -27
- data/lib/graphql_rails/tasks/dump_graphql_schemas.rb +57 -0
- data/lib/graphql_rails/tasks/schema.rake +8 -5
- data/lib/graphql_rails/types/argument_type.rb +12 -0
- data/lib/graphql_rails/types/field_type.rb +14 -0
- data/lib/graphql_rails/types/hidable_by_group.rb +32 -0
- data/lib/graphql_rails/types/object_type.rb +12 -0
- data/lib/graphql_rails/version.rb +1 -1
- metadata +15 -8
- data/.travis.yml +0 -5
@@ -4,22 +4,19 @@ module GraphqlRails
|
|
4
4
|
module Attributes
|
5
5
|
# contains info about single graphql input attribute
|
6
6
|
class InputAttribute
|
7
|
+
require 'graphql_rails/model/build_enum_type'
|
7
8
|
require_relative './input_type_parser'
|
8
9
|
require_relative './attribute_name_parser'
|
9
10
|
include Attributable
|
11
|
+
include AttributeConfigurable
|
10
12
|
|
11
|
-
|
13
|
+
chainable_option :subtype
|
14
|
+
chainable_option :enum
|
12
15
|
|
13
|
-
|
14
|
-
|
16
|
+
def initialize(name, config:)
|
17
|
+
@config = config
|
15
18
|
@initial_name = name
|
16
|
-
@initial_type = type
|
17
|
-
@description = description
|
18
|
-
@options = options
|
19
|
-
@subtype = subtype
|
20
|
-
@required = required
|
21
19
|
end
|
22
|
-
# rubocop:enable Metrics/ParameterLists
|
23
20
|
|
24
21
|
def input_argument_args
|
25
22
|
type = raw_input_type || input_type_parser.input_type_arg
|
@@ -28,7 +25,7 @@ module GraphqlRails
|
|
28
25
|
end
|
29
26
|
|
30
27
|
def input_argument_options
|
31
|
-
{ required: required?, description: description, camelize: false }
|
28
|
+
{ required: required?, description: description, camelize: false, groups: groups }
|
32
29
|
end
|
33
30
|
|
34
31
|
def paginated?
|
@@ -37,7 +34,7 @@ module GraphqlRails
|
|
37
34
|
|
38
35
|
private
|
39
36
|
|
40
|
-
attr_reader :initial_name, :
|
37
|
+
attr_reader :initial_name, :config
|
41
38
|
|
42
39
|
def attribute_name_parser
|
43
40
|
@attribute_name_parser ||= AttributeNameParser.new(
|
@@ -51,14 +48,23 @@ module GraphqlRails
|
|
51
48
|
|
52
49
|
def input_type_parser
|
53
50
|
@input_type_parser ||= begin
|
54
|
-
initial_parseable_type =
|
51
|
+
initial_parseable_type = type || enum_type || attribute_name_parser.graphql_type
|
55
52
|
InputTypeParser.new(initial_parseable_type, subtype: subtype)
|
56
53
|
end
|
57
54
|
end
|
58
55
|
|
56
|
+
def enum_type
|
57
|
+
return if enum.blank?
|
58
|
+
|
59
|
+
::GraphqlRails::Model::BuildEnumType.call(
|
60
|
+
"#{config.name}_#{initial_name}_enum",
|
61
|
+
allowed_values: enum
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
59
65
|
def raw_input_type
|
60
|
-
return
|
61
|
-
return
|
66
|
+
return type if type.is_a?(GraphQL::InputObjectType)
|
67
|
+
return type.graphql_input_type if type.is_a?(Model::Input)
|
62
68
|
end
|
63
69
|
end
|
64
70
|
end
|
@@ -15,12 +15,6 @@ module GraphqlRails
|
|
15
15
|
@subtype = subtype
|
16
16
|
end
|
17
17
|
|
18
|
-
def graphql_type
|
19
|
-
return nil if unparsed_type.nil?
|
20
|
-
|
21
|
-
partly_parsed_type || parsed_type
|
22
|
-
end
|
23
|
-
|
24
18
|
def input_type_arg
|
25
19
|
if list?
|
26
20
|
list_type_arg
|
@@ -34,7 +28,11 @@ module GraphqlRails
|
|
34
28
|
attr_reader :unparsed_type, :subtype
|
35
29
|
|
36
30
|
def unwrapped_type
|
37
|
-
raw_unwrapped_type ||
|
31
|
+
raw_unwrapped_type ||
|
32
|
+
unwrapped_scalar_type ||
|
33
|
+
unwrapped_model_input_type ||
|
34
|
+
graphql_type_object ||
|
35
|
+
raise_not_supported_type_error
|
38
36
|
end
|
39
37
|
|
40
38
|
def raw_unwrapped_type
|
@@ -16,18 +16,27 @@ module GraphqlRails
|
|
16
16
|
'int' => GraphQL::Types::Int,
|
17
17
|
'integer' => GraphQL::Types::Int,
|
18
18
|
|
19
|
+
'big_int' => GraphQL::Types::BigInt,
|
20
|
+
'bigint' => GraphQL::Types::BigInt,
|
21
|
+
|
22
|
+
'float' => GraphQL::Types::Float,
|
23
|
+
'double' => GraphQL::Types::Float,
|
24
|
+
'decimal' => GraphQL::Types::Float,
|
25
|
+
|
26
|
+
'bool' => GraphQL::Types::Boolean,
|
27
|
+
'boolean' => GraphQL::Types::Boolean,
|
28
|
+
|
19
29
|
'string' => GraphQL::Types::String,
|
20
30
|
'str' => GraphQL::Types::String,
|
21
31
|
'text' => GraphQL::Types::String,
|
22
|
-
'time' => GraphQL::Types::String,
|
23
|
-
'date' => GraphQL::Types::String,
|
24
32
|
|
25
|
-
'
|
26
|
-
'boolean' => GraphQL::Types::Boolean,
|
33
|
+
'date' => GraphQL::Types::ISO8601Date,
|
27
34
|
|
28
|
-
'
|
29
|
-
'
|
30
|
-
'
|
35
|
+
'time' => GraphQL::Types::ISO8601DateTime,
|
36
|
+
'datetime' => GraphQL::Types::ISO8601DateTime,
|
37
|
+
'date_time' => GraphQL::Types::ISO8601DateTime,
|
38
|
+
|
39
|
+
'json' => GraphQL::Types::JSON
|
31
40
|
}.freeze
|
32
41
|
|
33
42
|
WRAPPER_TYPES = [
|
@@ -43,10 +52,16 @@ module GraphqlRails
|
|
43
52
|
GraphQL::InputObjectType
|
44
53
|
].freeze
|
45
54
|
|
55
|
+
PARSEABLE_RAW_GRAPHQL_TYPES = [
|
56
|
+
GraphQL::Schema::Object,
|
57
|
+
GraphQL::Schema::Scalar,
|
58
|
+
GraphQL::Schema::Enum
|
59
|
+
].freeze
|
60
|
+
|
46
61
|
RAW_GRAPHQL_TYPES = (WRAPPER_TYPES + GRAPHQL_BASE_TYPES).freeze
|
47
62
|
|
48
63
|
def unwrapped_scalar_type
|
49
|
-
TYPE_MAPPING[nullable_inner_name.downcase
|
64
|
+
TYPE_MAPPING[nullable_inner_name.downcase]
|
50
65
|
end
|
51
66
|
|
52
67
|
def raw_graphql_type?
|
@@ -58,25 +73,46 @@ module GraphqlRails
|
|
58
73
|
end
|
59
74
|
|
60
75
|
def core_scalar_type?
|
61
|
-
unwrapped_scalar_type.
|
76
|
+
unwrapped_scalar_type.present?
|
62
77
|
end
|
63
78
|
|
64
79
|
def graphql_model
|
65
|
-
|
66
|
-
|
67
|
-
unparsed_type
|
68
|
-
else
|
69
|
-
nullable_inner_name.safe_constantize
|
70
|
-
end
|
71
|
-
|
72
|
-
return if type_class.nil?
|
73
|
-
return unless type_class < GraphqlRails::Model
|
80
|
+
extract_type_class_if { |type| graphql_model?(type) }
|
81
|
+
end
|
74
82
|
|
75
|
-
|
83
|
+
def graphql_type_object
|
84
|
+
type_object = extract_type_class_if { |type| graphql_type_object?(type) }
|
85
|
+
type_object || graphql_model&.graphql&.graphql_type
|
76
86
|
end
|
77
87
|
|
78
88
|
protected
|
79
89
|
|
90
|
+
def extract_type_class_if
|
91
|
+
return unparsed_type if yield(unparsed_type)
|
92
|
+
|
93
|
+
type_class = nullable_inner_name.safe_constantize
|
94
|
+
return type_class if yield(type_class)
|
95
|
+
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
|
99
|
+
def graphql_model?(type_class)
|
100
|
+
type_class.is_a?(Class) && type_class < GraphqlRails::Model
|
101
|
+
end
|
102
|
+
|
103
|
+
def graphql_type_object?(type_class)
|
104
|
+
return false unless type_class.is_a?(Class)
|
105
|
+
|
106
|
+
PARSEABLE_RAW_GRAPHQL_TYPES.any? { |parent_type| type_class < parent_type }
|
107
|
+
end
|
108
|
+
|
109
|
+
def applicable_graphql_type?(type)
|
110
|
+
return false unless type.is_a?(Class)
|
111
|
+
return true if type < GraphqlRails::Model
|
112
|
+
|
113
|
+
false
|
114
|
+
end
|
115
|
+
|
80
116
|
def unwrap_type(type)
|
81
117
|
unwrappable = type
|
82
118
|
unwrappable = unwrappable.of_type while wrapped_type?(unwrappable)
|
@@ -51,7 +51,9 @@ module GraphqlRails
|
|
51
51
|
def paginated_type_arg
|
52
52
|
return graphql_model.graphql.connection_type if graphql_model
|
53
53
|
|
54
|
-
|
54
|
+
error_message = "Unable to paginate #{unparsed_type.inspect}. " \
|
55
|
+
'Pagination is only supported for models which include GraphqlRails::Model'
|
56
|
+
raise NotSupportedFeature, error_message
|
55
57
|
end
|
56
58
|
|
57
59
|
def list_type_arg
|
@@ -107,14 +109,7 @@ module GraphqlRails
|
|
107
109
|
end
|
108
110
|
|
109
111
|
def type_by_name
|
110
|
-
unwrapped_scalar_type ||
|
111
|
-
end
|
112
|
-
|
113
|
-
def unwrapped_model_type
|
114
|
-
type_class = graphql_model
|
115
|
-
return unless type_class
|
116
|
-
|
117
|
-
type_class.graphql.graphql_type
|
112
|
+
unwrapped_scalar_type || graphql_type_object || raise_not_supported_type_error
|
118
113
|
end
|
119
114
|
end
|
120
115
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphqlRails
|
4
|
+
# Allows defining methods chained way
|
5
|
+
module ChainableOptions
|
6
|
+
NOT_SET = Object.new
|
7
|
+
|
8
|
+
# nodoc
|
9
|
+
module ClassMethods
|
10
|
+
def chainable_option(option_name, default: nil)
|
11
|
+
define_method(option_name) do |value = NOT_SET|
|
12
|
+
get_or_set_chainable_option(option_name, value, default: default)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.included(base)
|
18
|
+
base.extend(ClassMethods)
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize_copy(other)
|
22
|
+
super
|
23
|
+
@chainable_option = other.instance_variable_get(:@chainable_option).dup
|
24
|
+
end
|
25
|
+
|
26
|
+
def with(**options)
|
27
|
+
options.each do |method_name, args|
|
28
|
+
send_args = [method_name]
|
29
|
+
send_args << args if method(method_name).parameters.present?
|
30
|
+
public_send(*send_args)
|
31
|
+
end
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def fetch_chainable_option(option_name, *default, &block)
|
38
|
+
@chainable_option.fetch(option_name.to_sym, *default, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_or_set_chainable_option(option_name, value = NOT_SET, default: nil)
|
42
|
+
@chainable_option ||= {}
|
43
|
+
return fetch_chainable_option(option_name, default) if value == NOT_SET
|
44
|
+
|
45
|
+
@chainable_option[option_name.to_sym] = value
|
46
|
+
self
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/graphql_rails/controller/build_controller_action_resolver/controller_action_resolver.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'graphql_rails/controller/request'
|
4
|
+
require 'graphql_rails/types/argument_type'
|
4
5
|
|
5
6
|
module GraphqlRails
|
6
7
|
class Controller
|
@@ -8,6 +9,8 @@ module GraphqlRails
|
|
8
9
|
# Resolver which includes controller specific methods.
|
9
10
|
# Used to simplify resolver build for each controller action
|
10
11
|
class ControllerActionResolver < GraphQL::Schema::Resolver
|
12
|
+
argument_class(GraphqlRails::Types::ArgumentType)
|
13
|
+
|
11
14
|
def self.controller(controller_class = nil)
|
12
15
|
@controller = controller_class if controller_class
|
13
16
|
@controller
|
@@ -5,7 +5,7 @@ module GraphqlRails
|
|
5
5
|
# wrapps active record relation and returns decorated object instead
|
6
6
|
class RelationDecorator
|
7
7
|
delegate :map, :each, to: :to_a
|
8
|
-
delegate :limit_value, :offset_value, :count, :size, to: :relation
|
8
|
+
delegate :limit_value, :offset_value, :count, :size, :empty?, to: :relation
|
9
9
|
|
10
10
|
def self.decorates?(object)
|
11
11
|
(defined?(ActiveRecord) && object.is_a?(ActiveRecord::Relation)) ||
|
@@ -24,7 +24,7 @@ module GraphqlRails
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
%i[first second last].each do |method_name|
|
27
|
+
%i[first second last find find_by].each do |method_name|
|
28
28
|
define_method method_name do |*args, &block|
|
29
29
|
decoratable_object_method(method_name, *args, &block)
|
30
30
|
end
|
@@ -3,9 +3,14 @@
|
|
3
3
|
module GraphqlRails
|
4
4
|
# contains configuration options related with inputs
|
5
5
|
module InputConfigurable
|
6
|
-
def permit(*
|
7
|
-
|
8
|
-
|
6
|
+
def permit(*args)
|
7
|
+
args.each do |arg|
|
8
|
+
if arg.is_a? Hash
|
9
|
+
arg.each { |attribute, type| permit_input(attribute, type: type) }
|
10
|
+
else
|
11
|
+
permit_input(arg)
|
12
|
+
end
|
13
|
+
end
|
9
14
|
self
|
10
15
|
end
|
11
16
|
|
@@ -37,11 +42,8 @@ module GraphqlRails
|
|
37
42
|
end
|
38
43
|
|
39
44
|
def build_input_attribute(name, options: {}, **other_options)
|
40
|
-
|
41
|
-
|
42
|
-
options: input_attribute_options.merge(options),
|
43
|
-
**other_options
|
44
|
-
)
|
45
|
+
input_options = input_attribute_options.merge(options)
|
46
|
+
Attributes::InputAttribute.new(name.to_s, config: self).with(options: input_options, **other_options)
|
45
47
|
end
|
46
48
|
end
|
47
49
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'graphql_rails/types/argument_type'
|
4
|
+
require 'graphql_rails/concerns/service'
|
5
|
+
|
3
6
|
module GraphqlRails
|
4
7
|
module Model
|
5
8
|
# stores information about model specific config, like attributes and types
|
6
9
|
class BuildGraphqlInputType
|
7
|
-
require 'graphql_rails/concerns/service'
|
8
|
-
|
9
10
|
include ::GraphqlRails::Service
|
10
11
|
|
11
12
|
def initialize(name:, description: nil, attributes:)
|
@@ -20,6 +21,7 @@ module GraphqlRails
|
|
20
21
|
type_attributes = attributes
|
21
22
|
|
22
23
|
Class.new(GraphQL::Schema::InputObject) do
|
24
|
+
argument_class(GraphqlRails::Types::ArgumentType)
|
23
25
|
graphql_name(type_name)
|
24
26
|
description(type_description)
|
25
27
|
|
@@ -2,25 +2,47 @@
|
|
2
2
|
|
3
3
|
module GraphqlRails
|
4
4
|
module Model
|
5
|
-
#
|
6
|
-
#
|
5
|
+
# Contains methods which are shared between various configurations.
|
6
|
+
#
|
7
|
+
# Expects `default_name` to be defined.
|
8
|
+
# Expects `build_attribute(attr_name)` method to be defined.
|
7
9
|
module Configurable
|
10
|
+
require 'active_support/concern'
|
11
|
+
require 'graphql_rails/concerns/chainable_options'
|
12
|
+
|
13
|
+
extend ActiveSupport::Concern
|
14
|
+
|
15
|
+
included do
|
16
|
+
include GraphqlRails::ChainableOptions
|
17
|
+
|
18
|
+
chainable_option :description
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize_copy(other)
|
22
|
+
super
|
23
|
+
@type_name = nil
|
24
|
+
@attributes = other.attributes.transform_values(&:dup)
|
25
|
+
end
|
26
|
+
|
8
27
|
def attributes
|
9
28
|
@attributes ||= {}
|
10
29
|
end
|
11
30
|
|
12
|
-
def name(
|
13
|
-
|
14
|
-
@name || default_name
|
31
|
+
def name(*args)
|
32
|
+
get_or_set_chainable_option(:name, *args) || default_name
|
15
33
|
end
|
16
34
|
|
17
35
|
def type_name
|
18
36
|
@type_name ||= "#{name.camelize}Type#{SecureRandom.hex}"
|
19
37
|
end
|
20
38
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
39
|
+
def attribute(attribute_name, **attribute_options)
|
40
|
+
key = attribute_name.to_s
|
41
|
+
|
42
|
+
attributes[key] ||= build_attribute(attribute_name).tap do |new_attribute|
|
43
|
+
new_attribute.with(**attribute_options) unless attribute_options.empty?
|
44
|
+
yield(new_attribute) if block_given?
|
45
|
+
end
|
24
46
|
end
|
25
47
|
end
|
26
48
|
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'graphql_rails/attributes'
|
4
4
|
require 'graphql_rails/model/find_or_build_graphql_type'
|
5
|
-
require 'graphql_rails/model/build_enum_type'
|
6
5
|
require 'graphql_rails/model/input'
|
7
6
|
require 'graphql_rails/model/configurable'
|
8
7
|
require 'graphql_rails/model/build_connection_type'
|
@@ -11,6 +10,7 @@ module GraphqlRails
|
|
11
10
|
module Model
|
12
11
|
# stores information about model specific config, like attributes and types
|
13
12
|
class Configuration
|
13
|
+
include ChainableOptions
|
14
14
|
include Configurable
|
15
15
|
|
16
16
|
def initialize(model_class)
|
@@ -22,22 +22,14 @@ module GraphqlRails
|
|
22
22
|
@connection_type = nil
|
23
23
|
@graphql_type = nil
|
24
24
|
@input = other.instance_variable_get(:@input)&.transform_values(&:dup)
|
25
|
-
@attributes = other.instance_variable_get(:@attributes)&.transform_values(&:dup)
|
26
25
|
end
|
27
26
|
|
28
27
|
def attribute(attribute_name, **attribute_options)
|
29
28
|
key = attribute_name.to_s
|
30
29
|
|
31
|
-
attributes[key] ||=
|
32
|
-
|
33
|
-
|
34
|
-
attribute_options.each do |method_name, args|
|
35
|
-
send_args = [method_name]
|
36
|
-
send_args << args if attribute.method(method_name).parameters.present?
|
37
|
-
attribute.public_send(*send_args)
|
38
|
-
end
|
39
|
-
|
40
|
-
yield(attribute) if block_given?
|
30
|
+
attributes[key] ||= build_attribute(attribute_name).tap do |new_attribute|
|
31
|
+
new_attribute.with(**attribute_options)
|
32
|
+
yield(new_attribute) if block_given?
|
41
33
|
end
|
42
34
|
end
|
43
35
|
|
@@ -68,13 +60,35 @@ module GraphqlRails
|
|
68
60
|
@connection_type ||= BuildConnectionType.call(graphql_type)
|
69
61
|
end
|
70
62
|
|
63
|
+
def with_ensured_fields!
|
64
|
+
return self if @graphql_type.blank?
|
65
|
+
|
66
|
+
reset_graphql_type if attributes.any? && graphql_type.fields.length != attributes.length
|
67
|
+
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
71
|
private
|
72
72
|
|
73
73
|
attr_reader :model_class
|
74
74
|
|
75
|
+
def build_attribute(attribute_name)
|
76
|
+
Attributes::Attribute.new(attribute_name)
|
77
|
+
end
|
78
|
+
|
75
79
|
def default_name
|
76
80
|
@default_name ||= model_class.name.split('::').last
|
77
81
|
end
|
82
|
+
|
83
|
+
def reset_graphql_type
|
84
|
+
@graphql_type = FindOrBuildGraphqlType.call(
|
85
|
+
name: name,
|
86
|
+
description: description,
|
87
|
+
attributes: attributes,
|
88
|
+
type_name: type_name,
|
89
|
+
force_define_attributes: true
|
90
|
+
)
|
91
|
+
end
|
78
92
|
end
|
79
93
|
end
|
80
94
|
end
|
@@ -10,20 +10,21 @@ module GraphqlRails
|
|
10
10
|
|
11
11
|
include ::GraphqlRails::Service
|
12
12
|
|
13
|
-
def initialize(name:, description:, attributes:, type_name:)
|
13
|
+
def initialize(name:, description:, attributes:, type_name:, force_define_attributes: false)
|
14
14
|
@name = name
|
15
15
|
@description = description
|
16
16
|
@attributes = attributes
|
17
17
|
@type_name = type_name
|
18
|
+
@force_define_attributes = force_define_attributes
|
18
19
|
end
|
19
20
|
|
20
21
|
def call
|
21
|
-
klass.tap { add_fields_to_graphql_type if new_class? }
|
22
|
+
klass.tap { add_fields_to_graphql_type if new_class? || force_define_attributes }
|
22
23
|
end
|
23
24
|
|
24
25
|
private
|
25
26
|
|
26
|
-
attr_reader :name, :description, :attributes, :type_name
|
27
|
+
attr_reader :name, :description, :attributes, :type_name, :force_define_attributes
|
27
28
|
|
28
29
|
delegate :klass, :new_class?, to: :type_class_finder
|
29
30
|
|
@@ -36,29 +37,30 @@ module GraphqlRails
|
|
36
37
|
end
|
37
38
|
|
38
39
|
def add_fields_to_graphql_type
|
39
|
-
|
40
|
+
scalar_attributes, dynamic_attributes = attributes.values.partition(&:scalar_type?)
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
42
|
+
AddFieldsToGraphqlType.call(klass: klass, attributes: scalar_attributes)
|
43
|
+
dynamic_attributes.each { |attribute| find_or_build_dynamic_type(attribute) }
|
44
|
+
AddFieldsToGraphqlType.call(klass: klass, attributes: dynamic_attributes)
|
50
45
|
end
|
51
46
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
attribute.graphql_model.graphql.type_name
|
59
|
-
)
|
47
|
+
def find_or_build_dynamic_type(attribute)
|
48
|
+
graphql_model = attribute.graphql_model
|
49
|
+
if graphql_model
|
50
|
+
find_or_build_graphql_model_type(graphql_model)
|
51
|
+
else
|
52
|
+
AddFieldsToGraphqlType.call(klass: klass, attributes: [attribute])
|
60
53
|
end
|
61
54
|
end
|
55
|
+
|
56
|
+
def find_or_build_graphql_model_type(graphql_model)
|
57
|
+
self.class.call(
|
58
|
+
name: graphql_model.graphql.name,
|
59
|
+
description: graphql_model.graphql.description,
|
60
|
+
attributes: graphql_model.graphql.attributes,
|
61
|
+
type_name: graphql_model.graphql.type_name
|
62
|
+
)
|
63
|
+
end
|
62
64
|
end
|
63
65
|
end
|
64
66
|
end
|
@@ -5,6 +5,7 @@ module GraphqlRails
|
|
5
5
|
# Initializes class to define graphql type and fields.
|
6
6
|
class FindOrBuildGraphqlTypeClass
|
7
7
|
require 'graphql_rails/concerns/service'
|
8
|
+
require 'graphql_rails/types/object_type'
|
8
9
|
|
9
10
|
include ::GraphqlRails::Service
|
10
11
|
|
@@ -32,7 +33,7 @@ module GraphqlRails
|
|
32
33
|
graphql_type_name = name
|
33
34
|
graphql_type_description = description
|
34
35
|
|
35
|
-
graphql_type_klass = Class.new(
|
36
|
+
graphql_type_klass = Class.new(GraphqlRails::Types::ObjectType) do
|
36
37
|
graphql_name(graphql_type_name)
|
37
38
|
description(graphql_type_description)
|
38
39
|
end
|
@@ -1,58 +1,42 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'graphql_rails/model/build_graphql_input_type'
|
4
|
-
require 'graphql_rails/model/configurable'
|
5
|
-
|
6
3
|
module GraphqlRails
|
7
4
|
module Model
|
8
5
|
# stores information about model input specific config, like attributes and types
|
9
6
|
class Input
|
7
|
+
require 'graphql_rails/concerns/chainable_options'
|
8
|
+
require 'graphql_rails/model/configurable'
|
9
|
+
require 'graphql_rails/model/build_graphql_input_type'
|
10
|
+
|
10
11
|
include Configurable
|
11
12
|
|
13
|
+
chainable_option :enum
|
14
|
+
|
12
15
|
def initialize(model_class, input_name_suffix)
|
13
16
|
@model_class = model_class
|
14
17
|
@input_name_suffix = input_name_suffix
|
15
18
|
end
|
16
19
|
|
17
|
-
def initialize_copy(other)
|
18
|
-
super
|
19
|
-
@attributes = other.instance_variable_get(:@attributes)&.transform_values(&:dup)
|
20
|
-
end
|
21
|
-
|
22
20
|
def graphql_input_type
|
23
21
|
@graphql_input_type ||= BuildGraphqlInputType.call(
|
24
22
|
name: name, description: description, attributes: attributes
|
25
23
|
)
|
26
24
|
end
|
27
25
|
|
28
|
-
def attribute(attribute_name, type: nil, enum: nil, **attribute_options)
|
29
|
-
input_type = attribute_type(attribute_name, type: type, enum: enum, **attribute_options)
|
30
|
-
|
31
|
-
attributes[attribute_name.to_s] = Attributes::InputAttribute.new(
|
32
|
-
attribute_name, type: input_type, **attribute_options
|
33
|
-
)
|
34
|
-
end
|
35
|
-
|
36
26
|
private
|
37
27
|
|
38
28
|
attr_reader :input_name_suffix, :model_class
|
39
29
|
|
30
|
+
def build_attribute(attribute_name)
|
31
|
+
Attributes::InputAttribute.new(attribute_name, config: self)
|
32
|
+
end
|
33
|
+
|
40
34
|
def default_name
|
41
35
|
@default_name ||= begin
|
42
36
|
suffix = input_name_suffix ? input_name_suffix.to_s.camelize : ''
|
43
37
|
"#{model_class.name.split('::').last}#{suffix}Input"
|
44
38
|
end
|
45
39
|
end
|
46
|
-
|
47
|
-
def attribute_type(attribute_name, type:, enum:, description: nil, **_other)
|
48
|
-
return type unless enum
|
49
|
-
|
50
|
-
BuildEnumType.call(
|
51
|
-
"#{name}_#{attribute_name}_enum",
|
52
|
-
allowed_values: enum,
|
53
|
-
description: description
|
54
|
-
)
|
55
|
-
end
|
56
40
|
end
|
57
41
|
end
|
58
42
|
end
|