elasticgraph-apollo 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 (36) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +63 -0
  4. data/apollo_tests_implementation/Dockerfile +66 -0
  5. data/apollo_tests_implementation/Gemfile +27 -0
  6. data/apollo_tests_implementation/Rakefile +22 -0
  7. data/apollo_tests_implementation/config/products_schema.rb +173 -0
  8. data/apollo_tests_implementation/config/settings.yaml +34 -0
  9. data/apollo_tests_implementation/config.ru +122 -0
  10. data/apollo_tests_implementation/docker-compose.yaml +18 -0
  11. data/apollo_tests_implementation/lib/test_implementation_extension.rb +58 -0
  12. data/apollo_tests_implementation/wait_for_datastore.sh +17 -0
  13. data/elasticgraph-apollo.gemspec +27 -0
  14. data/lib/elastic_graph/apollo/graphql/engine_extension.rb +52 -0
  15. data/lib/elastic_graph/apollo/graphql/entities_field_resolver.rb +305 -0
  16. data/lib/elastic_graph/apollo/graphql/http_endpoint_extension.rb +45 -0
  17. data/lib/elastic_graph/apollo/graphql/service_field_resolver.rb +30 -0
  18. data/lib/elastic_graph/apollo/schema_definition/api_extension.rb +385 -0
  19. data/lib/elastic_graph/apollo/schema_definition/apollo_directives.rb +119 -0
  20. data/lib/elastic_graph/apollo/schema_definition/argument_extension.rb +20 -0
  21. data/lib/elastic_graph/apollo/schema_definition/entity_type_extension.rb +30 -0
  22. data/lib/elastic_graph/apollo/schema_definition/enum_type_extension.rb +23 -0
  23. data/lib/elastic_graph/apollo/schema_definition/enum_value_extension.rb +20 -0
  24. data/lib/elastic_graph/apollo/schema_definition/factory_extension.rb +104 -0
  25. data/lib/elastic_graph/apollo/schema_definition/field_extension.rb +59 -0
  26. data/lib/elastic_graph/apollo/schema_definition/graphql_sdl_enumerator_extension.rb +69 -0
  27. data/lib/elastic_graph/apollo/schema_definition/input_type_extension.rb +20 -0
  28. data/lib/elastic_graph/apollo/schema_definition/interface_type_extension.rb +25 -0
  29. data/lib/elastic_graph/apollo/schema_definition/object_type_extension.rb +28 -0
  30. data/lib/elastic_graph/apollo/schema_definition/scalar_type_extension.rb +23 -0
  31. data/lib/elastic_graph/apollo/schema_definition/state_extension.rb +23 -0
  32. data/lib/elastic_graph/apollo/schema_definition/union_type_extension.rb +20 -0
  33. data/script/boot_eg_apollo_implementation +22 -0
  34. data/script/export_docker_env_vars.sh +15 -0
  35. data/script/test_compatibility +54 -0
  36. metadata +472 -0
@@ -0,0 +1,104 @@
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/apollo/schema_definition/argument_extension"
10
+ require "elastic_graph/apollo/schema_definition/enum_type_extension"
11
+ require "elastic_graph/apollo/schema_definition/enum_value_extension"
12
+ require "elastic_graph/apollo/schema_definition/field_extension"
13
+ require "elastic_graph/apollo/schema_definition/graphql_sdl_enumerator_extension"
14
+ require "elastic_graph/apollo/schema_definition/input_type_extension"
15
+ require "elastic_graph/apollo/schema_definition/interface_type_extension"
16
+ require "elastic_graph/apollo/schema_definition/object_type_extension"
17
+ require "elastic_graph/apollo/schema_definition/scalar_type_extension"
18
+ require "elastic_graph/apollo/schema_definition/union_type_extension"
19
+
20
+ module ElasticGraph
21
+ module Apollo
22
+ module SchemaDefinition
23
+ # Extension module applied to `ElasticGraph::SchemaDefinition::Factory` to add Apollo tagging support.
24
+ module FactoryExtension
25
+ # Steep has a hard type with the arg splats here.
26
+ __skip__ = def new_field(**kwargs)
27
+ super(**kwargs) do |field|
28
+ field.extend FieldExtension
29
+ yield field if block_given?
30
+ end
31
+ end
32
+
33
+ def new_graphql_sdl_enumerator(all_types_except_root_query_type)
34
+ super.tap do |enum|
35
+ enum.extend GraphQLSDLEnumeratorExtension
36
+ end
37
+ end
38
+
39
+ def new_argument(field, name, value_type)
40
+ super(field, name, value_type) do |type|
41
+ type.extend ArgumentExtension
42
+ yield type if block_given?
43
+ end
44
+ end
45
+
46
+ def new_enum_type(name)
47
+ super(name) do |type|
48
+ type.extend EnumTypeExtension
49
+ yield type
50
+ end
51
+ end
52
+
53
+ def new_enum_value(name, original_name)
54
+ super(name, original_name) do |type|
55
+ type.extend EnumValueExtension
56
+ yield type if block_given?
57
+ end
58
+ end
59
+
60
+ def new_input_type(name)
61
+ super(name) do |type|
62
+ type.extend InputTypeExtension
63
+ yield type
64
+ end
65
+ end
66
+
67
+ def new_interface_type(name)
68
+ super(name) do |type|
69
+ type.extend InterfaceTypeExtension
70
+ yield type
71
+ end
72
+ end
73
+
74
+ # Here we override `object_type` in order to automatically add the apollo `@key` directive to indexed types.
75
+ def new_object_type(name)
76
+ super(name) do |raw_type|
77
+ raw_type.extend ObjectTypeExtension
78
+ type = raw_type # : ElasticGraph::SchemaDefinition::SchemaElements::ObjectType & ObjectTypeExtension
79
+
80
+ yield type if block_given?
81
+
82
+ if type.indexed? && type.graphql_fields_by_name.key?("id")
83
+ type.apollo_key fields: "id"
84
+ end
85
+ end
86
+ end
87
+
88
+ def new_scalar_type(name)
89
+ super(name) do |type|
90
+ type.extend ScalarTypeExtension
91
+ yield type
92
+ end
93
+ end
94
+
95
+ def new_union_type(name)
96
+ super(name) do |type|
97
+ type.extend UnionTypeExtension
98
+ yield type
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,59 @@
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/apollo/schema_definition/apollo_directives"
10
+
11
+ module ElasticGraph
12
+ module Apollo
13
+ module SchemaDefinition
14
+ # Extension module applied to `ElasticGraph::SchemaDefinition::SchemaElements::Field` to add Apollo tagging support.
15
+ module FieldExtension
16
+ include ApolloDirectives::Authenticated
17
+ include ApolloDirectives::External
18
+ include ApolloDirectives::Inaccessible
19
+ include ApolloDirectives::Override
20
+ include ApolloDirectives::Policy
21
+ include ApolloDirectives::Provides
22
+ include ApolloDirectives::Requires
23
+ include ApolloDirectives::RequiresScopes
24
+ include ApolloDirectives::Shareable
25
+ include ApolloDirectives::Tag
26
+
27
+ # Extension method designed to support Apollo's contract variant tagging:
28
+ #
29
+ # https://www.apollographql.com/docs/studio/contracts/
30
+ #
31
+ # Calling this method on a field will cause the field to be tagged and also every schema element derived from
32
+ # the field (e.g. the filter field, the aggregation field, an the sort order enum), ensuring that all capabilities
33
+ # related to the field are available in the contract variant.
34
+ def tag_with(tag_name)
35
+ on_each_generated_schema_element do |element|
36
+ needs_tagging =
37
+ if element.is_a?(ElasticGraph::SchemaDefinition::SchemaElements::SortOrderEnumValue)
38
+ # Each sort order enum value is based on a full field path (e.g. `parentField_subField_furtherNestedField_ASC`).
39
+ # We must only tag the enum if each part of the full field path is also tagged. In this example, we should only
40
+ # tag the enum value if `parentField`, `subField`, and `furtherNestedField` are all tagged.
41
+ element.sort_order_field_path.all? { |f| FieldExtension.tagged_with?(f, tag_name) }
42
+ else
43
+ true
44
+ end
45
+
46
+ if needs_tagging && !FieldExtension.tagged_with?(element, tag_name)
47
+ (_ = element).apollo_tag name: tag_name
48
+ end
49
+ end
50
+ end
51
+
52
+ # Helper method that indicates if the given schema element has a specific tag.
53
+ def self.tagged_with?(element, tag_name)
54
+ element.directives.any? { |dir| dir.name == "tag" && dir.arguments == {name: tag_name} }
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,69 @@
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 Apollo
11
+ module SchemaDefinition
12
+ # Module designed to be extended onto an `ElasticGraph::SchemaDefinition::GraphQLSDLEnumerator`
13
+ # instance to customize the schema artifacts to support Apollo.
14
+ module GraphQLSDLEnumeratorExtension
15
+ def root_query_type
16
+ super.tap do |type_or_nil|
17
+ # @type var type: ElasticGraph::SchemaDefinition::SchemaElements::ObjectType
18
+ type = _ = type_or_nil
19
+
20
+ if schema_def_state.object_types_by_name.values.any?(&:indexed?)
21
+ type.field "_entities", "[_Entity]!" do |f|
22
+ f.documentation <<~EOS
23
+ A field required by the [Apollo Federation subgraph
24
+ spec](https://www.apollographql.com/docs/federation/subgraph-spec/#query_entities):
25
+
26
+ > The graph router uses this root-level `Query` field to directly fetch fields of entities defined by a subgraph.
27
+ >
28
+ > This field must take a `representations` argument of type `[_Any!]!` (a non-nullable list of non-nullable
29
+ > [`_Any` scalars](https://www.apollographql.com/docs/federation/subgraph-spec/#scalar-_any)). Its return type must be `[_Entity]!` (a non-nullable list of _nullable_
30
+ > objects that belong to the [`_Entity` union](https://www.apollographql.com/docs/federation/subgraph-spec/#union-_entity)).
31
+ >
32
+ > Each entry in the `representations` list must be validated with the following rules:
33
+ >
34
+ > - A representation must include a `__typename` string field.
35
+ > - A representation must contain all fields included in the fieldset of a `@key` directive applied to the corresponding entity definition.
36
+ >
37
+ > For details, see [Resolving entity fields with `Query._entities`](https://www.apollographql.com/docs/federation/subgraph-spec/#resolving-entity-fields-with-query_entities).
38
+
39
+ Not intended for use by clients other than Apollo.
40
+ EOS
41
+
42
+ f.argument "representations", "[_Any!]!" do |a|
43
+ a.documentation <<~EOS
44
+ A list of entity data blobs from other apollo subgraphs. For more information (and
45
+ to see an example of what form this argument takes), see the [Apollo Federation subgraph
46
+ spec](https://www.apollographql.com/docs/federation/subgraph-spec/#resolve-requests-for-entities).
47
+ EOS
48
+ end
49
+ end
50
+ end
51
+
52
+ type.field "_service", "_Service!" do |f|
53
+ f.documentation <<~EOS
54
+ A field required by the [Apollo Federation subgraph
55
+ spec](https://www.apollographql.com/docs/federation/subgraph-spec/#query_service):
56
+
57
+ > This field of the root `Query` type must return a non-nullable [`_Service` type](https://www.apollographql.com/docs/federation/subgraph-spec/#type-_service).
58
+
59
+ > For details, see [Enhanced introspection with `Query._service`](https://www.apollographql.com/docs/federation/subgraph-spec/#enhanced-introspection-with-query_service).
60
+
61
+ Not intended for use by clients other than Apollo.
62
+ EOS
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,20 @@
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/apollo/schema_definition/apollo_directives"
10
+
11
+ module ElasticGraph
12
+ module Apollo
13
+ module SchemaDefinition
14
+ module InputTypeExtension
15
+ include ApolloDirectives::Inaccessible
16
+ include ApolloDirectives::Tag
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
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/apollo/schema_definition/apollo_directives"
10
+
11
+ module ElasticGraph
12
+ module Apollo
13
+ module SchemaDefinition
14
+ module InterfaceTypeExtension
15
+ include ApolloDirectives::Authenticated
16
+ include ApolloDirectives::Extends
17
+ include ApolloDirectives::Inaccessible
18
+ include ApolloDirectives::Key
19
+ include ApolloDirectives::Policy
20
+ include ApolloDirectives::RequiresScopes
21
+ include ApolloDirectives::Tag
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
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/apollo/schema_definition/apollo_directives"
10
+
11
+ module ElasticGraph
12
+ module Apollo
13
+ module SchemaDefinition
14
+ module ObjectTypeExtension
15
+ include ApolloDirectives::Authenticated
16
+ include ApolloDirectives::Extends
17
+ include ApolloDirectives::External
18
+ include ApolloDirectives::Inaccessible
19
+ include ApolloDirectives::InterfaceObject
20
+ include ApolloDirectives::Key
21
+ include ApolloDirectives::Policy
22
+ include ApolloDirectives::RequiresScopes
23
+ include ApolloDirectives::Shareable
24
+ include ApolloDirectives::Tag
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,23 @@
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/apollo/schema_definition/apollo_directives"
10
+
11
+ module ElasticGraph
12
+ module Apollo
13
+ module SchemaDefinition
14
+ module ScalarTypeExtension
15
+ include ApolloDirectives::Authenticated
16
+ include ApolloDirectives::Inaccessible
17
+ include ApolloDirectives::Policy
18
+ include ApolloDirectives::RequiresScopes
19
+ include ApolloDirectives::Tag
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
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 Apollo
11
+ module SchemaDefinition
12
+ # Extension module applied to `ElasticGraph::SchemaDefinition::State` to support extra Apollo state.
13
+ module StateExtension
14
+ # @dynamic apollo_directive_definitions, apollo_directive_definitions=
15
+ attr_accessor :apollo_directive_definitions
16
+
17
+ def self.extended(state)
18
+ state.apollo_directive_definitions = [] # Ensure it's never `nil`.
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
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/apollo/schema_definition/apollo_directives"
10
+
11
+ module ElasticGraph
12
+ module Apollo
13
+ module SchemaDefinition
14
+ module UnionTypeExtension
15
+ include ApolloDirectives::Inaccessible
16
+ include ApolloDirectives::Tag
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Abort script at first error, when a command exits with non-zero status.
4
+ # Verbose form of `set -e`.
5
+ set -o errexit
6
+
7
+ # Attempt to use undefined variable outputs error message, and forces an exit
8
+ # Verbose form of `set -u`.
9
+ set -o nounset
10
+
11
+ # If set, the return value of a pipeline is the value of the last (rightmost)
12
+ # command to exit with a non-zero status, or zero if all commands in the
13
+ # pipeline exit successfully.
14
+ set -o pipefail
15
+
16
+ # Print a trace of simple commands.
17
+ # Verbose form of `set -x`.
18
+ set -o xtrace
19
+
20
+ script_dir=$(dirname $0)
21
+ source $script_dir/export_docker_env_vars.sh
22
+ docker compose -f $script_dir/../apollo_tests_implementation/docker-compose.yaml up --build
@@ -0,0 +1,15 @@
1
+ script_dir=$(dirname $0)
2
+
3
+ # Use the current max Elasticsearch version we test against.
4
+ export VERSION=$(ruby -ryaml -e "puts YAML.load_file('$script_dir/../../config/tested_database_versions.yaml').fetch('elasticsearch').max_by { |v| Gem::Version.new(v) }")
5
+
6
+ # Use the same Ruby version in the docker container as what we are currently using.
7
+ export RUBY_VERSION=$(ruby -e "puts RUBY_VERSION")
8
+
9
+ # Call the ENV "apollo" instead of "test" or "local" to avoid interference with
10
+ # the Elasticsearch container booted for those envs.
11
+ export ENV=apollo
12
+
13
+ # Apollo federation version used to generate the schema artifacts.
14
+ # TODO: Move to v2.6 once it is supported by the test suite.
15
+ export TARGET_APOLLO_FEDERATION_VERSION=2.3
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Abort script at first error, when a command exits with non-zero status.
4
+ # Verbose form of `set -e`.
5
+ set -o errexit
6
+
7
+ # Attempt to use undefined variable outputs error message, and forces an exit
8
+ # Verbose form of `set -u`.
9
+ set -o nounset
10
+
11
+ # If set, the return value of a pipeline is the value of the last (rightmost)
12
+ # command to exit with a non-zero status, or zero if all commands in the
13
+ # pipeline exit successfully.
14
+ set -o pipefail
15
+
16
+ # Print a trace of simple commands.
17
+ # Verbose form of `set -x`.
18
+ set -o xtrace
19
+
20
+ script_dir=$(dirname $0)
21
+
22
+ # Default to the Apollo federation version 2.6, as higher versions are not supported by the test suite properly.
23
+ apollo_federation_version=${1:-2.6}
24
+
25
+ # Latest version as of 2024-05-08. Gotten from:
26
+ # https://www.npmjs.com/package/@apollo/federation-subgraph-compatibility?activeTab=versions
27
+ apollo_subgraph_tests_version=2.2.0
28
+
29
+ TARGET_APOLLO_FEDERATION_VERSION=$apollo_federation_version bundle exec rake \
30
+ --rakefile $script_dir/../apollo_tests_implementation/Rakefile schema_artifacts:dump
31
+
32
+ source $script_dir/export_docker_env_vars.sh
33
+
34
+ # Running the tests produces some artifacts we don't want at our project root, so we run it from a tmp directory.
35
+ rm -rf $script_dir/../../tmp/apollo_compat_test
36
+ mkdir -p $script_dir/../../tmp/apollo_compat_test
37
+
38
+ pushd $script_dir/../../tmp/apollo_compat_test
39
+
40
+ # The latest apollo/federation-subgraph-compatibility tests target federation v2.3, and
41
+ # when we target lower federation versions (e.g. v2.0), we get warnings from some of the tests.
42
+ # So we only want to `--failOnWarning` when targeting federation versions higher versions
43
+ # (e.g. v2.3, v2.5, v2.6).
44
+ additional_flags=$([ "$apollo_federation_version" == "2.0" ] && echo "" || echo "--failOnWarning" )
45
+
46
+ TARGET_APOLLO_FEDERATION_VERSION=2.3 npx --yes \
47
+ @apollo/federation-subgraph-compatibility@${apollo_subgraph_tests_version} docker \
48
+ --compose ../../elasticgraph-apollo/apollo_tests_implementation/docker-compose.yaml \
49
+ --path /graphql \
50
+ --schema ../../elasticgraph-apollo/apollo_tests_implementation/config/schema/artifacts/schema.graphql \
51
+ --debug \
52
+ --failOnRequired $additional_flags
53
+
54
+ popd