elasticgraph-schema_definition 0.18.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +7 -0
  4. data/elasticgraph-schema_definition.gemspec +26 -0
  5. data/lib/elastic_graph/schema_definition/api.rb +359 -0
  6. data/lib/elastic_graph/schema_definition/factory.rb +506 -0
  7. data/lib/elastic_graph/schema_definition/indexing/derived_fields/append_only_set.rb +79 -0
  8. data/lib/elastic_graph/schema_definition/indexing/derived_fields/field_initializer_support.rb +59 -0
  9. data/lib/elastic_graph/schema_definition/indexing/derived_fields/immutable_value.rb +99 -0
  10. data/lib/elastic_graph/schema_definition/indexing/derived_fields/min_or_max_value.rb +62 -0
  11. data/lib/elastic_graph/schema_definition/indexing/derived_indexed_type.rb +346 -0
  12. data/lib/elastic_graph/schema_definition/indexing/event_envelope.rb +74 -0
  13. data/lib/elastic_graph/schema_definition/indexing/field.rb +181 -0
  14. data/lib/elastic_graph/schema_definition/indexing/field_reference.rb +51 -0
  15. data/lib/elastic_graph/schema_definition/indexing/field_type/enum.rb +65 -0
  16. data/lib/elastic_graph/schema_definition/indexing/field_type/object.rb +113 -0
  17. data/lib/elastic_graph/schema_definition/indexing/field_type/scalar.rb +51 -0
  18. data/lib/elastic_graph/schema_definition/indexing/field_type/union.rb +70 -0
  19. data/lib/elastic_graph/schema_definition/indexing/index.rb +318 -0
  20. data/lib/elastic_graph/schema_definition/indexing/json_schema_field_metadata.rb +34 -0
  21. data/lib/elastic_graph/schema_definition/indexing/json_schema_with_metadata.rb +234 -0
  22. data/lib/elastic_graph/schema_definition/indexing/list_counts_mapping.rb +53 -0
  23. data/lib/elastic_graph/schema_definition/indexing/relationship_resolver.rb +96 -0
  24. data/lib/elastic_graph/schema_definition/indexing/rollover_config.rb +25 -0
  25. data/lib/elastic_graph/schema_definition/indexing/update_target_factory.rb +54 -0
  26. data/lib/elastic_graph/schema_definition/indexing/update_target_resolver.rb +195 -0
  27. data/lib/elastic_graph/schema_definition/json_schema_pruner.rb +61 -0
  28. data/lib/elastic_graph/schema_definition/mixins/can_be_graphql_only.rb +31 -0
  29. data/lib/elastic_graph/schema_definition/mixins/has_derived_graphql_type_customizations.rb +119 -0
  30. data/lib/elastic_graph/schema_definition/mixins/has_directives.rb +65 -0
  31. data/lib/elastic_graph/schema_definition/mixins/has_documentation.rb +74 -0
  32. data/lib/elastic_graph/schema_definition/mixins/has_indices.rb +281 -0
  33. data/lib/elastic_graph/schema_definition/mixins/has_readable_to_s_and_inspect.rb +46 -0
  34. data/lib/elastic_graph/schema_definition/mixins/has_subtypes.rb +116 -0
  35. data/lib/elastic_graph/schema_definition/mixins/has_type_info.rb +181 -0
  36. data/lib/elastic_graph/schema_definition/mixins/implements_interfaces.rb +122 -0
  37. data/lib/elastic_graph/schema_definition/mixins/supports_default_value.rb +47 -0
  38. data/lib/elastic_graph/schema_definition/mixins/supports_filtering_and_aggregation.rb +267 -0
  39. data/lib/elastic_graph/schema_definition/mixins/verifies_graphql_name.rb +38 -0
  40. data/lib/elastic_graph/schema_definition/rake_tasks.rb +190 -0
  41. data/lib/elastic_graph/schema_definition/results.rb +404 -0
  42. data/lib/elastic_graph/schema_definition/schema_artifact_manager.rb +482 -0
  43. data/lib/elastic_graph/schema_definition/schema_elements/argument.rb +56 -0
  44. data/lib/elastic_graph/schema_definition/schema_elements/built_in_types.rb +1541 -0
  45. data/lib/elastic_graph/schema_definition/schema_elements/deprecated_element.rb +21 -0
  46. data/lib/elastic_graph/schema_definition/schema_elements/directive.rb +40 -0
  47. data/lib/elastic_graph/schema_definition/schema_elements/enum_type.rb +189 -0
  48. data/lib/elastic_graph/schema_definition/schema_elements/enum_value.rb +73 -0
  49. data/lib/elastic_graph/schema_definition/schema_elements/enum_value_namer.rb +89 -0
  50. data/lib/elastic_graph/schema_definition/schema_elements/enums_for_indexed_types.rb +82 -0
  51. data/lib/elastic_graph/schema_definition/schema_elements/field.rb +1085 -0
  52. data/lib/elastic_graph/schema_definition/schema_elements/field_path.rb +112 -0
  53. data/lib/elastic_graph/schema_definition/schema_elements/field_source.rb +16 -0
  54. data/lib/elastic_graph/schema_definition/schema_elements/graphql_sdl_enumerator.rb +113 -0
  55. data/lib/elastic_graph/schema_definition/schema_elements/input_field.rb +31 -0
  56. data/lib/elastic_graph/schema_definition/schema_elements/input_type.rb +60 -0
  57. data/lib/elastic_graph/schema_definition/schema_elements/interface_type.rb +72 -0
  58. data/lib/elastic_graph/schema_definition/schema_elements/list_counts_state.rb +40 -0
  59. data/lib/elastic_graph/schema_definition/schema_elements/object_type.rb +53 -0
  60. data/lib/elastic_graph/schema_definition/schema_elements/relationship.rb +218 -0
  61. data/lib/elastic_graph/schema_definition/schema_elements/scalar_type.rb +310 -0
  62. data/lib/elastic_graph/schema_definition/schema_elements/sort_order_enum_value.rb +36 -0
  63. data/lib/elastic_graph/schema_definition/schema_elements/sub_aggregation_path.rb +66 -0
  64. data/lib/elastic_graph/schema_definition/schema_elements/type_namer.rb +237 -0
  65. data/lib/elastic_graph/schema_definition/schema_elements/type_reference.rb +353 -0
  66. data/lib/elastic_graph/schema_definition/schema_elements/type_with_subfields.rb +579 -0
  67. data/lib/elastic_graph/schema_definition/schema_elements/union_type.rb +157 -0
  68. data/lib/elastic_graph/schema_definition/scripting/file_system_repository.rb +77 -0
  69. data/lib/elastic_graph/schema_definition/scripting/script.rb +48 -0
  70. data/lib/elastic_graph/schema_definition/scripting/scripts/field/as_day_of_week.painless +24 -0
  71. data/lib/elastic_graph/schema_definition/scripting/scripts/field/as_time_of_day.painless +41 -0
  72. data/lib/elastic_graph/schema_definition/scripting/scripts/filter/by_time_of_day.painless +22 -0
  73. data/lib/elastic_graph/schema_definition/scripting/scripts/update/index_data.painless +93 -0
  74. data/lib/elastic_graph/schema_definition/state.rb +212 -0
  75. data/lib/elastic_graph/schema_definition/test_support.rb +113 -0
  76. metadata +513 -0
@@ -0,0 +1,21 @@
1
+ # Copyright 2024 Block, Inc.
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+ #
7
+ # frozen_string_literal: true
8
+
9
+ module ElasticGraph
10
+ module SchemaDefinition
11
+ module SchemaElements
12
+ # @private
13
+ DeprecatedElement = ::Data.define(:schema_def_state, :name, :defined_at, :defined_via) do
14
+ # @implements DeprecatedElement
15
+ def description
16
+ "`#{defined_via}` at #{defined_at.path}:#{defined_at.lineno}"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,40 @@
1
+ # Copyright 2024 Block, Inc.
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+ #
7
+ # frozen_string_literal: true
8
+
9
+ require "elastic_graph/schema_definition/mixins/verifies_graphql_name"
10
+ require "elastic_graph/support/graphql_formatter"
11
+
12
+ module ElasticGraph
13
+ module SchemaDefinition
14
+ module SchemaElements
15
+ # Represents a [GraphQL directive](https://spec.graphql.org/October2021/#sec-Language.Directives).
16
+ #
17
+ # @!attribute [r] name
18
+ # @return [String] name of the directive
19
+ # @!attribute [r] arguments
20
+ # @return [Hash<Symbol, Object>] directive arguments
21
+ # @!parse class Directive < ::Data; end
22
+ class Directive < ::Data.define(:name, :arguments)
23
+ prepend Mixins::VerifiesGraphQLName
24
+
25
+ # @return [String] GraphQL SDL form of the directive
26
+ def to_sdl
27
+ %(@#{name}#{Support::GraphQLFormatter.format_args(**arguments)})
28
+ end
29
+
30
+ # Duplicates this directive on another GraphQL schema element.
31
+ #
32
+ # @param element [Argument, EnumType, EnumValue, Field, ScalarType, TypeWithSubfields, UnionType] schema element
33
+ # @return [void]
34
+ def duplicate_on(element)
35
+ element.directive name, arguments
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,189 @@
1
+ # Copyright 2024 Block, Inc.
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+ #
7
+ # frozen_string_literal: true
8
+
9
+ require "elastic_graph/schema_artifacts/runtime_metadata/enum"
10
+ require "elastic_graph/schema_definition/indexing/field_type/enum"
11
+ require "elastic_graph/schema_definition/mixins/can_be_graphql_only"
12
+ require "elastic_graph/schema_definition/mixins/has_derived_graphql_type_customizations"
13
+ require "elastic_graph/schema_definition/mixins/has_directives"
14
+ require "elastic_graph/schema_definition/mixins/has_documentation"
15
+ require "elastic_graph/schema_definition/mixins/has_readable_to_s_and_inspect"
16
+ require "elastic_graph/schema_definition/mixins/verifies_graphql_name"
17
+
18
+ module ElasticGraph
19
+ module SchemaDefinition
20
+ module SchemaElements
21
+ # {include:API#enum_type}
22
+ #
23
+ # @example Define an enum type
24
+ # ElasticGraph.define_schema do |schema|
25
+ # schema.enum_type "Currency" do |t|
26
+ # # in the block, `t` is an EnumType
27
+ # t.value "USD"
28
+ # end
29
+ # end
30
+ #
31
+ # @!attribute [r] schema_def_state
32
+ # @return [State] state of the schema
33
+ # @!attribute [rw] type_ref
34
+ # @private
35
+ # @!attribute [rw] for_output
36
+ # @return [Boolean] `true` if this enum is used for both input and output; `false` if it is for input only
37
+ # @!attribute [r] values_by_name
38
+ # @return [Hash<String, EnumValue>] map of enum values, keyed by name
39
+ class EnumType < Struct.new(:schema_def_state, :type_ref, :for_output, :values_by_name)
40
+ # @dynamic type_ref, graphql_only?
41
+ prepend Mixins::VerifiesGraphQLName
42
+ include Mixins::CanBeGraphQLOnly
43
+ include Mixins::HasDocumentation
44
+ include Mixins::HasDirectives
45
+ include Mixins::HasDerivedGraphQLTypeCustomizations
46
+ include Mixins::HasReadableToSAndInspect.new { |e| e.name }
47
+
48
+ # @private
49
+ def initialize(schema_def_state, name)
50
+ # @type var values_by_name: ::Hash[::String, EnumValue]
51
+ values_by_name = {}
52
+ super(schema_def_state, schema_def_state.type_ref(name).to_final_form, true, values_by_name)
53
+
54
+ # :nocov: -- currently all invocations have a block
55
+ yield self if block_given?
56
+ # :nocov:
57
+ end
58
+
59
+ # @return [String] name of the enum type
60
+ def name
61
+ type_ref.name
62
+ end
63
+
64
+ # @return [TypeReference] reference to `AggregatedValues` type to use for this enum.
65
+ def aggregated_values_type
66
+ schema_def_state.type_ref("NonNumeric").as_aggregated_values
67
+ end
68
+
69
+ # Defines an enum value for the current enum type.
70
+ #
71
+ # @param value_name [String] name of the enum value
72
+ # @yield [EnumValue] enum value so it can be further customized
73
+ # @return [void]
74
+ #
75
+ # @example Define an enum type with multiple enum values
76
+ # ElasticGraph.define_schema do |schema|
77
+ # schema.enum_type "Currency" do |t|
78
+ # t.value "USD" do |v|
79
+ # v.documentation "US Dollars."
80
+ # end
81
+ #
82
+ # t.value "JPY" do |v|
83
+ # v.documentation "Japanese Yen."
84
+ # end
85
+ # end
86
+ # end
87
+ def value(value_name, &block)
88
+ alternate_original_name = value_name
89
+ value_name = schema_def_state.enum_value_namer.name_for(name, value_name.to_s)
90
+
91
+ if values_by_name.key?(value_name)
92
+ raise SchemaError, "Duplicate value on Enum::Type #{name}: #{value_name}"
93
+ end
94
+
95
+ if value_name.length > DEFAULT_MAX_KEYWORD_LENGTH
96
+ raise SchemaError, "Enum value `#{name}.#{value_name}` is too long: it is #{value_name.length} characters but cannot exceed #{DEFAULT_MAX_KEYWORD_LENGTH} characters."
97
+ end
98
+
99
+ values_by_name[value_name] = schema_def_state.factory.new_enum_value(value_name, alternate_original_name, &block)
100
+ end
101
+
102
+ # Defines multiple enum values. In contrast to {#value}, the enum values cannot be customized
103
+ # further via a block.
104
+ #
105
+ # @param value_names [Array<String>] names of the enum values
106
+ # @return [void]
107
+ #
108
+ # @example Define an enum type with multiple enum values
109
+ # ElasticGraph.define_schema do |schema|
110
+ # schema.enum_type "Currency" do |t|
111
+ # t.values "USD", "JPY", "CAD", "GBP"
112
+ # end
113
+ # end
114
+ def values(*value_names)
115
+ value_names.flatten.each { |name| value(name) }
116
+ end
117
+
118
+ # @return [SchemaArtifacts::RuntimeMetadata::Enum::Type] runtime metadata for this enum type
119
+ def runtime_metadata
120
+ runtime_metadata_values_by_name = values_by_name
121
+ .transform_values(&:runtime_metadata)
122
+ .compact
123
+
124
+ SchemaArtifacts::RuntimeMetadata::Enum::Type.new(values_by_name: runtime_metadata_values_by_name)
125
+ end
126
+
127
+ # @return [String] GraphQL SDL form of the enum type
128
+ def to_sdl
129
+ if values_by_name.empty?
130
+ raise SchemaError, "Enum type #{name} has no values, but enums must have at least one value."
131
+ end
132
+
133
+ <<~EOS
134
+ #{formatted_documentation}enum #{name} #{directives_sdl(suffix_with: " ")}{
135
+ #{values_by_name.values.map(&:to_sdl).flat_map { |s| s.split("\n") }.join("\n ")}
136
+ }
137
+ EOS
138
+ end
139
+
140
+ # @private
141
+ def derived_graphql_types
142
+ # Derived GraphQL types must be generated for an output enum. For an enum type that is only
143
+ # used as an input, we do not need derived types.
144
+ return [] unless for_output
145
+
146
+ derived_scalar_types = schema_def_state.factory.new_scalar_type(name) do |t|
147
+ t.mapping type: "keyword"
148
+ t.json_schema type: "string"
149
+ t.graphql_only graphql_only?
150
+ end.derived_graphql_types
151
+
152
+ if (input_enum = as_input).equal?(self)
153
+ derived_scalar_types
154
+ else
155
+ [input_enum] + derived_scalar_types
156
+ end
157
+ end
158
+
159
+ # @return [Indexing::FieldType::Enum] indexing representation of this enum type
160
+ def to_indexing_field_type
161
+ Indexing::FieldType::Enum.new(values_by_name.keys)
162
+ end
163
+
164
+ # @return [false] enum types are never directly indexed
165
+ def indexed?
166
+ false
167
+ end
168
+
169
+ # @return [EnumType] converts the enum type to its input form for when different naming is used for input vs output enums.
170
+ def as_input
171
+ input_name = type_ref
172
+ .as_input_enum # To apply the configured format for input enums.
173
+ .to_final_form # To handle a type name override of the input enum.
174
+ .name
175
+
176
+ return self if input_name == name
177
+
178
+ schema_def_state.factory.new_enum_type(input_name) do |t|
179
+ t.for_output = false # flag that it's not used as an output enum, and therefore `derived_graphql_types` will be empty on it.
180
+ t.graphql_only true # input enums are always GraphQL-only.
181
+ t.documentation doc_comment
182
+ directives.each { |dir| dir.duplicate_on(t) }
183
+ values_by_name.each { |_, val| val.duplicate_on(t) }
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,73 @@
1
+ # Copyright 2024 Block, Inc.
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+ #
7
+ # frozen_string_literal: true
8
+
9
+ require "elastic_graph/schema_definition/mixins/has_directives"
10
+ require "elastic_graph/schema_definition/mixins/has_documentation"
11
+ require "elastic_graph/schema_definition/mixins/has_readable_to_s_and_inspect"
12
+ require "elastic_graph/schema_definition/mixins/verifies_graphql_name"
13
+
14
+ module ElasticGraph
15
+ module SchemaDefinition
16
+ module SchemaElements
17
+ # Represents a value of a [GraphQL enum type](https://spec.graphql.org/October2021/#sec-Enums).
18
+ #
19
+ # @!attribute [r] schema_def_state
20
+ # @return [State] state of the schema
21
+ # @!attribute [r] name
22
+ # @return [String] name of the value
23
+ # @!attribute [r] runtime_metadata
24
+ # @return [SchemaElements::RuntimeMetadata::Enum::Value] runtime metadata
25
+ class EnumValue < Struct.new(:schema_def_state, :name, :runtime_metadata)
26
+ prepend Mixins::VerifiesGraphQLName
27
+ include Mixins::HasDocumentation
28
+ include Mixins::HasDirectives
29
+ include Mixins::HasReadableToSAndInspect.new { |v| v.name }
30
+
31
+ # @private
32
+ def initialize(schema_def_state, name, original_name)
33
+ runtime_metadata = SchemaArtifacts::RuntimeMetadata::Enum::Value.new(
34
+ sort_field: nil,
35
+ datastore_value: nil,
36
+ datastore_abbreviation: nil,
37
+ alternate_original_name: (original_name if original_name != name)
38
+ )
39
+
40
+ super(schema_def_state, name, runtime_metadata)
41
+ yield self
42
+ end
43
+
44
+ # @return [String] GraphQL SDL form of the enum value
45
+ def to_sdl
46
+ "#{formatted_documentation}#{name}#{directives_sdl(prefix_with: " ")}"
47
+ end
48
+
49
+ # Duplicates this enum value on another {EnumType}.
50
+ #
51
+ # @param other_enum_type [EnumType] enum type to duplicate this value onto
52
+ # @return [void]
53
+ def duplicate_on(other_enum_type)
54
+ other_enum_type.value name do |v|
55
+ v.documentation doc_comment
56
+ directives.each { |dir| dir.duplicate_on(v) }
57
+ v.update_runtime_metadata(**runtime_metadata.to_h)
58
+ end
59
+ end
60
+
61
+ # Updates the runtime metadata.
62
+ #
63
+ # @param [Hash<Symbol, Object>] updates to apply to the runtime metadata
64
+ # @return [void]
65
+ def update_runtime_metadata(**updates)
66
+ self.runtime_metadata = runtime_metadata.with(**updates)
67
+ end
68
+
69
+ private :runtime_metadata=
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,89 @@
1
+ # Copyright 2024 Block, Inc.
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+ #
7
+ # frozen_string_literal: true
8
+
9
+ require "elastic_graph/constants"
10
+ require "elastic_graph/error"
11
+ require "elastic_graph/support/hash_util"
12
+
13
+ module ElasticGraph
14
+ module SchemaDefinition
15
+ module SchemaElements
16
+ # Abstraction for generating names for GraphQL enum values. This allows users to customize the
17
+ # naming of our built-in enum values.
18
+ #
19
+ # @private
20
+ class EnumValueNamer < ::Struct.new(:overrides_by_type_name)
21
+ def initialize(overrides_by_type_name = {})
22
+ overrides_by_type_name = Support::HashUtil
23
+ .stringify_keys(overrides_by_type_name) # : ::Hash[::String, ::Hash[::String, ::String]]
24
+
25
+ @used_value_names_by_type_name = ::Hash.new { |h, k| h[k] = [] }
26
+ validate_overrides(overrides_by_type_name)
27
+ super(overrides_by_type_name: overrides_by_type_name)
28
+ end
29
+
30
+ # Returns the name that should be used for the given `type_name` and `value_name`.
31
+ def name_for(type_name, value_name)
32
+ @used_value_names_by_type_name[type_name] << value_name
33
+ overrides_by_type_name.dig(type_name, value_name) || value_name
34
+ end
35
+
36
+ # Returns the overrides that did not wind up being used. Unused overrides usually happen
37
+ # because of a typo, and can be safely removed.
38
+ def unused_overrides
39
+ overrides_by_type_name.filter_map do |type_name, overrides|
40
+ if @used_value_names_by_type_name.key?(type_name)
41
+ unused_overrides = overrides.except(*@used_value_names_by_type_name.fetch(type_name))
42
+ [type_name, unused_overrides] unless unused_overrides.empty?
43
+ else
44
+ [type_name, overrides]
45
+ end
46
+ end.to_h
47
+ end
48
+
49
+ # Full set of enum type and value names that were used. Can be used to provide suggestions
50
+ # for when there are `unused_overrides`.
51
+ def used_value_names_by_type_name
52
+ @used_value_names_by_type_name.dup
53
+ end
54
+
55
+ private
56
+
57
+ def validate_overrides(overrides_by_type_name)
58
+ duplicate_problems = overrides_by_type_name.flat_map do |type_name, overrides|
59
+ overrides
60
+ .group_by { |k, v| v }
61
+ .transform_values { |kv_pairs| kv_pairs.map(&:first) }
62
+ .select { |_, v| v.size > 1 }
63
+ .map do |override, source_names|
64
+ "Multiple `#{type_name}` enum value overrides (#{source_names.sort.join(", ")}) map to the same name (#{override}), which is not supported."
65
+ end
66
+ end
67
+
68
+ invalid_name_problems = overrides_by_type_name.flat_map do |type_name, overrides|
69
+ overrides.filter_map do |source_name, override|
70
+ unless GRAPHQL_NAME_PATTERN.match(override)
71
+ "`#{override}` (the override for `#{type_name}.#{source_name}`) is not a valid GraphQL type name. " +
72
+ GRAPHQL_NAME_VALIDITY_DESCRIPTION
73
+ end
74
+ end
75
+ end
76
+
77
+ notify_problems(duplicate_problems + invalid_name_problems)
78
+ end
79
+
80
+ def notify_problems(problems)
81
+ return if problems.empty?
82
+
83
+ raise ConfigError, "Provided `enum_value_overrides_by_type_name` have #{problems.size} problem(s):\n\n" \
84
+ "#{problems.map.with_index(1) { |problem, i| "#{i}. #{problem}" }.join("\n\n")}"
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,82 @@
1
+ # Copyright 2024 Block, Inc.
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+ #
7
+ # frozen_string_literal: true
8
+
9
+ require "elastic_graph/schema_artifacts/runtime_metadata/enum"
10
+ require "elastic_graph/schema_artifacts/runtime_metadata/sort_field"
11
+
12
+ module ElasticGraph
13
+ module SchemaDefinition
14
+ module SchemaElements
15
+ # Responsible for generating enum types based on specific indexed types.
16
+ #
17
+ # @private
18
+ class EnumsForIndexedTypes
19
+ def initialize(schema_def_state)
20
+ @schema_def_state = schema_def_state
21
+ end
22
+
23
+ # Generates a `SortOrder` enum type for the given indexed type.
24
+ def sort_order_enum_for(indexed_type)
25
+ return nil unless indexed_type.indexed?
26
+
27
+ build_enum(indexed_type, :sort_order, :sortable?, "sorted") do |enum_type, field_path|
28
+ value_name_parts = field_path.map(&:name)
29
+ index_field = field_path.map(&:name_in_index).join(".")
30
+
31
+ {asc: "ascending", desc: "descending"}.each do |dir, dir_description|
32
+ enum_type.value((value_name_parts + [dir.to_s.upcase]).join("_")) do |v|
33
+ v.update_runtime_metadata sort_field: SchemaArtifacts::RuntimeMetadata::SortField.new(index_field, dir)
34
+ v.documentation "Sorts #{dir_description} by the `#{graphql_field_path_description(field_path)}` field."
35
+
36
+ wrapped_enum_value = @schema_def_state.factory.new_sort_order_enum_value(v, field_path)
37
+
38
+ field_path.each do |field|
39
+ field.sort_order_enum_value_customizations.each { |block| block.call(wrapped_enum_value) }
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def build_enum(indexed_type, category, field_predicate, past_tense_verb, &block)
49
+ derived_type_ref = indexed_type.type_ref.as_static_derived_type(category)
50
+
51
+ enum = @schema_def_state.factory.new_enum_type(derived_type_ref.name) do |enum_type|
52
+ enum_type.documentation "Enumerates the ways `#{indexed_type.name}`s can be #{past_tense_verb}."
53
+ define_enum_values_for_type(enum_type, indexed_type, field_predicate, &block)
54
+ end.as_input
55
+
56
+ enum unless enum.values_by_name.empty?
57
+ end
58
+
59
+ def define_enum_values_for_type(enum_type, object_type, field_predicate, parents: [], &block)
60
+ object_type
61
+ .graphql_fields_by_name.values
62
+ .select(&field_predicate)
63
+ .each { |f| define_enum_values_for_field(enum_type, f, field_predicate, parents: parents, &block) }
64
+ end
65
+
66
+ def define_enum_values_for_field(enum_type, field, field_predicate, parents:, &block)
67
+ path = parents + [field]
68
+
69
+ if (object_type = field.type.fully_unwrapped.as_object_type)
70
+ define_enum_values_for_type(enum_type, object_type, field_predicate, parents: path, &block)
71
+ else
72
+ block.call(enum_type, path)
73
+ end
74
+ end
75
+
76
+ def graphql_field_path_description(field_path)
77
+ field_path.map(&:name).join(".")
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end