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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d47ce5fd25c5cc7a7f8d2f0b366b7f3831155283af52c20b1853a84249dd2c9b
4
+ data.tar.gz: cac5ed0af92ae555317bb208d7cdf4599421bccc4ebaa7db18899fb8621dceb2
5
+ SHA512:
6
+ metadata.gz: a547df1c78be131ad4799e475d9752569ac992459d3129b9cd3dfbfe2f529ecd3cd023032e33104c25c5bddeb623858b63d7e736f91dc54dd5ce54b05f68f83a
7
+ data.tar.gz: 182ee7cbc60949ee96c8c9055b65ed370b534355b64e54de6d240cf8a52f9c2778eac6e24fa1d606cdc176ba7eb1c0d7174a54b9d3e4de3f9413c51299d7fb09
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Block, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # ElasticGraph::Apollo
2
+
3
+ Implements the [Apollo Federation Subgraph Spec](https://www.apollographql.com/docs/federation/subgraph-spec/),
4
+ allowing an ElasticGraph application to be plugged into an Apollo-powered GraphQL server as a subgraph.
5
+
6
+ Note: this library only supports the v2 Federation specification.
7
+
8
+ ## Usage
9
+
10
+ First, add `elasticgraph-apollo` to your `Gemfile`:
11
+
12
+ ``` ruby
13
+ gem "elasticgraph-apollo"
14
+ ```
15
+
16
+ Finally, update your ElasticGraph schema artifact rake tasks in your `Rakefile`
17
+ so that `ElasticGraph::GraphQL::Apollo::SchemaDefinition::APIExtension` is
18
+ passed as one of the `extension_modules`:
19
+
20
+ ``` ruby
21
+ require "elastic_graph/schema_definition/rake_tasks"
22
+ require "elastic_graph/apollo/schema_definition/api_extension"
23
+
24
+ ElasticGraph::SchemaDefinition::RakeTasks.new(
25
+ schema_element_name_form: :snake_case,
26
+ index_document_sizes: true,
27
+ path_to_schema: "config/schema.rb",
28
+ schema_artifacts_directory: artifacts_dir,
29
+ extension_modules: [ElasticGraph::Apollo::SchemaDefinition::APIExtension]
30
+ )
31
+ ```
32
+
33
+ That's it!
34
+
35
+ ## Federation Version Support
36
+
37
+ This library supports multiple versions of Apollo federation. As of Jan. 2024, it supports:
38
+
39
+ * v2.0
40
+ * v2.3
41
+ * v2.5
42
+ * v2.6
43
+
44
+ By default, the newest version is targeted. If you need an older version (e.g. because your organization is
45
+ running an older Apollo version), you can configure it in your schema definition with:
46
+
47
+ ```ruby
48
+ schema.target_apollo_federation_version "2.3"
49
+ ```
50
+
51
+ ## Testing Notes
52
+
53
+ This project uses https://github.com/apollographql/apollo-federation-subgraph-compatibility
54
+ to verify compatibility with Apollo. Things to note:
55
+
56
+ - Run `elasticgraph-apollo/script/test_compatibility` to run the compatibility tests (the CI build runs this).
57
+ - Run `elasticgraph-apollo/script/boot_eg_apollo_implementation` to boot the ElasticGraph compatibility test implementation (can be useful for debugging `test_compatibility` failures).
58
+ - These scripts require some additional dependencies to be installed (such as `docker`, `node`, and `npm`).
59
+ - To get that to pass locally on my Mac, I had to enable the `Use Docker Compose V2` flag in Docker Desktop (under "Preferences -> General"). Without that checked, I got errors like this:
60
+
61
+ ```
62
+ ERROR: for apollo-federation-subgraph-compatibility_router_1 Cannot start service router: OCI runtime create failed: container_linux.go:380: starting container process caused: process_linux.go:545: container init caused: rootfs_linux.go:76: mounting "/host_mnt/Users/myron/Development/sq-elasticgraph-ruby/elasticgraph-apollo/vendor/apollo-federation-subgraph-compatibility/supergraph.graphql" to rootfs at "/etc/config/supergraph.graphql" caused: mount through procfd: not a directory: unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type
63
+ ```
@@ -0,0 +1,66 @@
1
+ ARG RUBY_VERSION
2
+ FROM ruby:${RUBY_VERSION}
3
+
4
+ ARG TARGET_APOLLO_FEDERATION_VERSION
5
+
6
+ WORKDIR /web
7
+
8
+ # Each of the elasticgraph gems this implementation depends on must be
9
+ # copied into the container so it's available for inclusion in the bundle.
10
+ # Note: we've observed that hidden files (like .rspec) are not copied by COPY.
11
+ # gemspec_helper enforces the presence of `.rspec`/`.yardopts`, so we have to copy them.
12
+ COPY elasticgraph-admin /web/elasticgraph-admin
13
+ COPY elasticgraph-admin/.rspec /web/elasticgraph-admin/.rspec
14
+ COPY elasticgraph-admin/.yardopts /web/elasticgraph-admin/.yardopts
15
+
16
+ COPY elasticgraph-apollo /web/elasticgraph-apollo
17
+ COPY elasticgraph-apollo/.rspec /web/elasticgraph-apollo/.rspec
18
+ COPY elasticgraph-apollo/.yardopts /web/elasticgraph-apollo/.yardopts
19
+
20
+ COPY elasticgraph-datastore_core /web/elasticgraph-datastore_core
21
+ COPY elasticgraph-datastore_core/.rspec /web/elasticgraph-datastore_core/.rspec
22
+ COPY elasticgraph-datastore_core/.yardopts /web/elasticgraph-datastore_core/.yardopts
23
+
24
+ COPY elasticgraph-elasticsearch /web/elasticgraph-elasticsearch
25
+ COPY elasticgraph-elasticsearch/.rspec /web/elasticgraph-elasticsearch/.rspec
26
+ COPY elasticgraph-elasticsearch/.yardopts /web/elasticgraph-elasticsearch/.yardopts
27
+
28
+ COPY elasticgraph-graphql /web/elasticgraph-graphql
29
+ COPY elasticgraph-graphql/.rspec /web/elasticgraph-graphql/.rspec
30
+ COPY elasticgraph-graphql/.yardopts /web/elasticgraph-graphql/.yardopts
31
+
32
+ COPY elasticgraph-indexer /web/elasticgraph-indexer
33
+ COPY elasticgraph-indexer/.rspec /web/elasticgraph-indexer/.rspec
34
+ COPY elasticgraph-indexer/.yardopts /web/elasticgraph-indexer/.yardopts
35
+
36
+ COPY elasticgraph-json_schema /web/elasticgraph-json_schema
37
+ COPY elasticgraph-json_schema/.rspec /web/elasticgraph-json_schema/.rspec
38
+ COPY elasticgraph-json_schema/.yardopts /web/elasticgraph-json_schema/.yardopts
39
+
40
+ COPY elasticgraph-rack /web/elasticgraph-rack
41
+ COPY elasticgraph-rack/.rspec /web/elasticgraph-rack/.rspec
42
+ COPY elasticgraph-rack/.yardopts /web/elasticgraph-rack/.yardopts
43
+
44
+ COPY elasticgraph-schema_artifacts /web/elasticgraph-schema_artifacts
45
+ COPY elasticgraph-schema_artifacts/.rspec /web/elasticgraph-schema_artifacts/.rspec
46
+ COPY elasticgraph-schema_artifacts/.yardopts /web/elasticgraph-schema_artifacts/.yardopts
47
+
48
+ COPY elasticgraph-schema_definition /web/elasticgraph-schema_definition
49
+ COPY elasticgraph-schema_definition/.rspec /web/elasticgraph-schema_definition/.rspec
50
+ COPY elasticgraph-schema_definition/.yardopts /web/elasticgraph-schema_definition/.yardopts
51
+
52
+ COPY elasticgraph-support /web/elasticgraph-support
53
+ COPY elasticgraph-support/.rspec /web/elasticgraph-support/.rspec
54
+ COPY elasticgraph-support/.yardopts /web/elasticgraph-support/.yardopts
55
+
56
+ # We also have to copy the implementation files (config, schema, etc) as well.
57
+ COPY elasticgraph-apollo/apollo_tests_implementation /web/
58
+
59
+ COPY gemspec_helper.rb /web/gemspec_helper.rb
60
+
61
+ # We need to install the bundle and generate our schema artifacts.
62
+ RUN bundle install
63
+ RUN bundle exec rake schema_artifacts:dump TARGET_APOLLO_FEDERATION_VERSION=${TARGET_APOLLO_FEDERATION_VERSION}
64
+
65
+ # Finally we can boot the app!
66
+ CMD ["bundle", "exec", "rackup", "--host", "0.0.0.0", "--port", "4001"]
@@ -0,0 +1,27 @@
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
+ source "https://rubygems.org"
10
+
11
+ %w[
12
+ admin
13
+ apollo
14
+ datastore_core
15
+ elasticsearch
16
+ graphql
17
+ indexer
18
+ json_schema
19
+ rack
20
+ schema_artifacts
21
+ schema_definition
22
+ support
23
+ ].each do |suffix|
24
+ gem "elasticgraph-#{suffix}", path: "elasticgraph-#{suffix}"
25
+ end
26
+
27
+ gem "rackup", "~> 2.1"
@@ -0,0 +1,22 @@
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/rake_tasks"
10
+ require "elastic_graph/apollo/schema_definition/api_extension"
11
+ require "pathname"
12
+
13
+ project_root = Pathname.new(__dir__)
14
+
15
+ ElasticGraph::SchemaDefinition::RakeTasks.new(
16
+ schema_element_name_form: :camelCase,
17
+ index_document_sizes: false,
18
+ path_to_schema: project_root / "config/products_schema.rb",
19
+ schema_artifacts_directory: project_root / "config/schema/artifacts",
20
+ extension_modules: [ElasticGraph::Apollo::SchemaDefinition::APIExtension],
21
+ enforce_json_schema_version: false
22
+ )
@@ -0,0 +1,173 @@
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 ApolloTestImpl
10
+ module GraphQLSDLEnumeratorExtension
11
+ # The `apollo-federation-subgraph-compatibility` project requires[^1] that each tested implementation provide
12
+ # specific `Query` fields:
13
+ #
14
+ # ```graphql
15
+ # type Query {
16
+ # product(id: ID!): Product
17
+ # deprecatedProduct(sku: String!, package: String!): DeprecatedProduct @deprecated(reason: "Use product query instead")
18
+ # }
19
+ # ```
20
+ #
21
+ # ElasticGraph automatically provides plural fields for our indexed types (e.g. `products` and `deprecatedProducts`).
22
+ # For the Apollo tests we need to additionally provide the two fields above. This hooks into the generation of the
23
+ # `Query` type to add the required fields.
24
+ #
25
+ # [^1]: https://github.com/apollographql/apollo-federation-subgraph-compatibility/blob/2.0.0/COMPATIBILITY.md#products-schema-to-be-implemented-by-library-maintainers
26
+ def root_query_type
27
+ super.tap do |type|
28
+ type.field "product", "Product" do |f|
29
+ f.argument "id", "ID!"
30
+ end
31
+
32
+ type.field "deprecatedProduct", "DeprecatedProduct" do |f|
33
+ f.argument "sku", "String!"
34
+ f.argument "package", "String!"
35
+ f.directive "deprecated", reason: "Use product query instead"
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ module SchemaDefFactoryExtension
42
+ def new_graphql_sdl_enumerator(all_types_except_root_query_type)
43
+ super(all_types_except_root_query_type).tap do |enum|
44
+ enum.extend GraphQLSDLEnumeratorExtension
45
+ end
46
+ end
47
+ end
48
+
49
+ federation_version = ENV["TARGET_APOLLO_FEDERATION_VERSION"]
50
+
51
+ # Note: this includes many "manual" schema elements (directives, raw SDL, etc) that the
52
+ # `elasticgraph-apollo` library will generate on our behalf in the future. For now, this
53
+ # includes all these schema elements just to make it as close as possible to an apollo
54
+ # compatible schema without further changes to elasticgraph-apollo.
55
+ #
56
+ # https://github.com/apollographql/apollo-federation-subgraph-compatibility/blob/2.0.0/COMPATIBILITY.md#products-schema-to-be-implemented-by-library-maintainers
57
+ ElasticGraph.define_schema do |schema|
58
+ schema.factory.extend SchemaDefFactoryExtension
59
+
60
+ schema.json_schema_version 1
61
+ schema.target_apollo_federation_version(federation_version) if federation_version
62
+
63
+ unless federation_version == "2.0"
64
+ schema.raw_sdl <<~EOS
65
+ extend schema
66
+ @link(url: "https://myspecs.dev/myCustomDirective/v1.0", import: ["@custom"])
67
+ @composeDirective(name: "@custom")
68
+
69
+ directive @custom on OBJECT
70
+ EOS
71
+ end
72
+
73
+ schema.object_type "Product" do |t|
74
+ t.directive "custom" unless federation_version == "2.0"
75
+ t.apollo_key fields: "sku package"
76
+ t.apollo_key fields: "sku variation { id }"
77
+
78
+ t.field "id", "ID!"
79
+ t.field "sku", "String"
80
+ t.field "package", "String"
81
+ t.field "variation", "ProductVariation"
82
+ t.field "dimensions", "ProductDimension"
83
+ t.field "createdBy", "User" do |f|
84
+ f.apollo_provides fields: "totalProductsCreated"
85
+ end
86
+ t.field "notes", "String" do |f|
87
+ f.tag_with "internal"
88
+ end
89
+ t.field "research", "[ProductResearch!]!" do |f|
90
+ f.mapping type: "object"
91
+ end
92
+
93
+ t.index "products"
94
+ end
95
+
96
+ schema.object_type "DeprecatedProduct" do |t|
97
+ t.apollo_key fields: "sku package"
98
+ t.field "id", "ID!", indexing_only: true
99
+ t.field "sku", "String!"
100
+ t.field "package", "String!"
101
+ t.field "reason", "String"
102
+ t.field "createdBy", "User"
103
+
104
+ t.index "deprecated_products"
105
+ end
106
+
107
+ schema.object_type "ProductVariation" do |t|
108
+ t.field "id", "ID!"
109
+ end
110
+
111
+ schema.object_type "ProductResearch" do |t|
112
+ t.apollo_key fields: "study { caseNumber }"
113
+ t.field "id", "ID!", indexing_only: true
114
+ t.field "study", "CaseStudy!"
115
+ t.field "outcome", "String"
116
+
117
+ t.index "product_research"
118
+ end
119
+
120
+ schema.object_type "CaseStudy" do |t|
121
+ t.field "caseNumber", "ID!"
122
+ t.field "description", "String"
123
+ end
124
+
125
+ schema.object_type "ProductDimension" do |t|
126
+ t.apollo_shareable
127
+ t.field "size", "String"
128
+ t.field "weight", "Float"
129
+ t.field "unit", "String" do |f|
130
+ f.apollo_inaccessible
131
+ end
132
+ end
133
+
134
+ schema.object_type "User" do |t|
135
+ t.apollo_extends
136
+ t.apollo_key fields: "email"
137
+ t.field "id", "ID!", indexing_only: true
138
+
139
+ t.field "averageProductsCreatedPerYear", "Int" do |f|
140
+ f.apollo_requires fields: "totalProductsCreated yearsOfEmployment"
141
+ end
142
+
143
+ t.field "email", "ID!" do |f|
144
+ f.apollo_external
145
+ end
146
+
147
+ t.field "name", "String" do |f|
148
+ f.apollo_override from: "users"
149
+ end
150
+
151
+ t.field "totalProductsCreated", "Int" do |f|
152
+ f.apollo_external
153
+ end
154
+
155
+ t.field "yearsOfEmployment", "Int!" do |f|
156
+ f.apollo_external
157
+ end
158
+
159
+ t.index "users"
160
+ end
161
+
162
+ unless federation_version == "2.0"
163
+ schema.object_type "Inventory" do |t|
164
+ t.apollo_interface_object
165
+ t.field "id", "ID!"
166
+ t.field "deprecatedProducts", "[DeprecatedProduct!]!" do |f|
167
+ f.mapping type: "object"
168
+ end
169
+ t.index "inventory"
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,34 @@
1
+ datastore:
2
+ client_faraday_adapter:
3
+ name: net_http
4
+ clusters:
5
+ main:
6
+ url: http://elasticsearch:9200
7
+ backend: elasticsearch
8
+ settings: {}
9
+ index_definitions:
10
+ products: &standard_index_settings
11
+ query_cluster: "main"
12
+ index_into_clusters: ["main"]
13
+ ignore_routing_values: []
14
+ custom_timestamp_ranges: []
15
+ setting_overrides: {}
16
+ setting_overrides_by_timestamp: {}
17
+ deprecated_products: *standard_index_settings
18
+ product_research: *standard_index_settings
19
+ users: *standard_index_settings
20
+ inventory: *standard_index_settings
21
+ log_traffic: true
22
+ max_client_retries: 3
23
+ logger:
24
+ device: stdout
25
+ indexer:
26
+ latency_slo_thresholds_by_timestamp_in_ms: {}
27
+ graphql:
28
+ default_page_size: 50
29
+ max_page_size: 500
30
+ extension_modules:
31
+ - require_path: ./lib/test_implementation_extension
32
+ extension_name: ApolloTestImplementationExtension
33
+ schema_artifacts:
34
+ directory: config/schema/artifacts
@@ -0,0 +1,122 @@
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/admin"
10
+ require "elastic_graph/graphql"
11
+ require "elastic_graph/indexer"
12
+ require "elastic_graph/rack/graphiql"
13
+ require "elastic_graph/indexer/test_support/converters"
14
+
15
+ admin = ElasticGraph::Admin.from_yaml_file("config/settings.yaml")
16
+ graphql = ElasticGraph::GraphQL.from_yaml_file("config/settings.yaml")
17
+ indexer = ElasticGraph::Indexer.from_yaml_file("config/settings.yaml")
18
+
19
+ admin.cluster_configurator.configure_cluster($stdout)
20
+
21
+ # Example records expected by the apollo-federation-subgraph-compatibility test suite. based on:
22
+ # https://github.com/apollographql/apollo-federation-subgraph-compatibility/blob/2.1.0/COMPATIBILITY.md#expected-data-sets
23
+ dimension = {
24
+ size: "small",
25
+ weight: 1,
26
+ unit: "kg"
27
+ }
28
+
29
+ user = {
30
+ id: "1",
31
+ averageProductsCreatedPerYear: 133,
32
+ email: "support@apollographql.com",
33
+ name: "Jane Smith",
34
+ totalProductsCreated: 1337,
35
+ yearsOfEmployment: 10
36
+ }
37
+
38
+ deprecated_product = {
39
+ id: "1",
40
+ sku: "apollo-federation-v1",
41
+ package: "@apollo/federation-v1",
42
+ reason: "Migrate to Federation V2",
43
+ createdBy: user
44
+ }
45
+
46
+ products_research = [
47
+ {
48
+ id: "1",
49
+ study: {
50
+ caseNumber: "1234",
51
+ description: "Federation Study"
52
+ },
53
+ outcome: nil
54
+ },
55
+ {
56
+ id: "2",
57
+ study: {
58
+ caseNumber: "1235",
59
+ description: "Studio Study"
60
+ },
61
+ outcome: nil
62
+ }
63
+ ]
64
+
65
+ products = [
66
+ {
67
+ id: "apollo-federation",
68
+ sku: "federation",
69
+ package: "@apollo/federation",
70
+ variation: {
71
+ id: "OSS"
72
+ },
73
+ dimensions: dimension,
74
+ research: [products_research[0]],
75
+ createdBy: user,
76
+ notes: nil
77
+ },
78
+ {
79
+ id: "apollo-studio",
80
+ sku: "studio",
81
+ package: "",
82
+ variation: {
83
+ id: "platform"
84
+ },
85
+ dimensions: dimension,
86
+ research: [products_research[1]],
87
+ createdBy: user,
88
+ notes: nil
89
+ }
90
+ ]
91
+
92
+ inventory = {
93
+ id: "apollo-oss",
94
+ deprecatedProducts: [deprecated_product]
95
+ }
96
+
97
+ records_by_type = {
98
+ "Product" => products,
99
+ "DeprecatedProduct" => [deprecated_product],
100
+ "ProductResearch" => products_research,
101
+ "User" => [user],
102
+ "Inventory" => [inventory]
103
+ }
104
+
105
+ events = records_by_type.flat_map do |type_name, records|
106
+ records = records.map.with_index do |record, index|
107
+ {
108
+ __typename: type_name,
109
+ __version: 1,
110
+ __json_schema_version: 1
111
+ }.merge(record)
112
+ end
113
+
114
+ ElasticGraph::Indexer::TestSupport::Converters.upsert_events_for_records(records)
115
+ end
116
+
117
+ indexer.processor.process(events, refresh_indices: true)
118
+
119
+ puts "Elasticsearch bootstrapping done. Booting the GraphQL server."
120
+
121
+ use Rack::ShowExceptions
122
+ run ElasticGraph::Rack::GraphiQL.new(graphql)
@@ -0,0 +1,18 @@
1
+ include:
2
+ - ../../elasticgraph-local/lib/elastic_graph/local/elasticsearch/docker-compose.yaml
3
+ services:
4
+ products:
5
+ build:
6
+ context: ../..
7
+ dockerfile: elasticgraph-apollo/apollo_tests_implementation/Dockerfile
8
+ args:
9
+ TARGET_APOLLO_FEDERATION_VERSION: ${TARGET_APOLLO_FEDERATION_VERSION}
10
+ RUBY_VERSION: ${RUBY_VERSION}
11
+ ports:
12
+ - 4001:4001
13
+ depends_on:
14
+ - elasticsearch
15
+ command: ["./wait_for_datastore.sh", "elasticsearch:9200", "bundle", "exec", "rackup", "--host", "0.0.0.0", "--port", "4001"]
16
+ volumes:
17
+ data01:
18
+ driver: local
@@ -0,0 +1,58 @@
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
+ # The `apollo-federation-subgraph-compatibility` project requires that each tested
10
+ # implementation provide a `Query.product(id: ID!): Product` field. ElasticGraph provides
11
+ # `Query.products(...): ProductConnection!` automatically. To be able to pass the tests,
12
+ # we need to provide the `product` field, even though ElasticGraph doesn't natively provide
13
+ # it.
14
+ #
15
+ # This defines an extension that injects a custom resolver that supports the field.
16
+ module ApolloTestImplementationExtension
17
+ def graphql_resolvers
18
+ @graphql_resolvers ||= [product_field_resolver] + super
19
+ end
20
+
21
+ def product_field_resolver
22
+ @product_field_resolver ||= ProductFieldResolver.new(
23
+ datastore_query_builder: datastore_query_builder,
24
+ product_index_def: datastore_core.index_definitions_by_name.fetch("products"),
25
+ datastore_router: datastore_search_router
26
+ )
27
+ end
28
+
29
+ class ProductFieldResolver
30
+ def initialize(datastore_query_builder:, product_index_def:, datastore_router:)
31
+ @datastore_query_builder = datastore_query_builder
32
+ @product_index_def = product_index_def
33
+ @datastore_router = datastore_router
34
+ end
35
+
36
+ def can_resolve?(field:, object:)
37
+ field.parent_type.name == :Query && field.name == :product
38
+ end
39
+
40
+ def resolve(field:, object:, args:, context:, lookahead:)
41
+ query = @datastore_query_builder.new_query(
42
+ search_index_definitions: [@product_index_def],
43
+ monotonic_clock_deadline: context[:monotonic_clock_deadline],
44
+ filter: {"id" => {"equalToAnyOf" => [args.fetch("id")]}},
45
+ individual_docs_needed: true,
46
+ requested_fields: %w[
47
+ id sku package notes
48
+ variation.id
49
+ dimensions.size dimensions.weight dimensions.unit
50
+ createdBy.averageProductsCreatedPerYear createdBy.email createdBy.name createdBy.totalProductsCreated createdBy.yearsOfEmployment
51
+ research.study.caseNumber research.study.description research.outcome
52
+ ]
53
+ )
54
+
55
+ @datastore_router.msearch([query]).fetch(query).documents.first
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Inspired by an example from the docker docks:
4
+ # https://docs.docker.com/compose/startup-order/
5
+
6
+ set -e
7
+
8
+ host="$1"
9
+ shift
10
+
11
+ until curl -f "$host"; do
12
+ >&2 echo "The datastore is unavailable - sleeping"
13
+ sleep 1
14
+ done
15
+
16
+ >&2 echo "The datastore is up - executing command"
17
+ exec "$@"
@@ -0,0 +1,27 @@
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_relative "../gemspec_helper"
10
+
11
+ ElasticGraphGemspecHelper.define_elasticgraph_gem(gemspec_file: __FILE__, category: :extension) do |spec, eg_version|
12
+ spec.summary = "An ElasticGraph extension that implements the Apollo federation spec."
13
+
14
+ spec.add_dependency "elasticgraph-graphql", eg_version
15
+ spec.add_dependency "elasticgraph-support", eg_version
16
+ spec.add_dependency "graphql", ">= 2.3.7", "< 2.4"
17
+ spec.add_dependency "apollo-federation", "~> 3.8"
18
+
19
+ # Note: technically, this is not purely a development dependency, but since `eg-schema_def`
20
+ # isn't intended to be used in production (or even included in a deployed bundle) we don't
21
+ # want to declare it as normal dependency here.
22
+ spec.add_development_dependency "elasticgraph-schema_definition", eg_version
23
+ spec.add_development_dependency "elasticgraph-admin", eg_version
24
+ spec.add_development_dependency "elasticgraph-elasticsearch", eg_version
25
+ spec.add_development_dependency "elasticgraph-opensearch", eg_version
26
+ spec.add_development_dependency "elasticgraph-indexer", eg_version
27
+ end