elasticgraph-schema_definition 0.18.0.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.
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