elasticgraph-apollo 0.18.0.0

Sign up to get free protection for your applications and to get access to all the features.
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