-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
446 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
## GraphQL::Stitching::HttpExecutable | ||
|
||
A `HttpExecutable` provides an out-of-the-box convenience for sending HTTP post requests to a remote location, or a base class for your own implementation with [GraphQL multipart uploads](https://github.com/jaydenseric/graphql-multipart-request-spec?tab=readme-ov-file#multipart-form-field-structure). | ||
|
||
```ruby | ||
exe = GraphQL::Stitching::HttpExecutable.new( | ||
url: "http://localhost:3001", | ||
headers: { | ||
"Authorization" => "..." | ||
} | ||
) | ||
``` | ||
|
||
### GraphQL Uploads via multipart forms | ||
|
||
The [GraphQL Upload Spec](https://github.com/jaydenseric/graphql-multipart-request-spec) defines a multipart form structure for submitting GraphQL requests that include file upload attachments. It is possible to flow these requests through a stitched schema using the following steps: | ||
|
||
1. File uploads must be submitted to stitching as basic GraphQL variables with `Tempfile` values assigned. The simplest way to recieve this input is to install [apollo_upload_server](https://github.com/jetruby/apollo_upload_server-ruby) into your stitching app's middleware so that multipart form submissions arrive unpackaged and in the expected format. | ||
|
||
```ruby | ||
client.execute( | ||
"mutation($file: Upload) { upload(file: $file) }", | ||
variables: { "file" => Tempfile.new(...) } | ||
) | ||
``` | ||
|
||
2. Stitching will route the request and its variables as normal. Then it's up to `HttpExecutable` to re-package any upload variables into the multipart form spec before sending them upstream. This is enabled with an `upload_types` parameter to tell the executable what scalar names must be extracted: | ||
|
||
```ruby | ||
|
||
client = GraphQL::Stitching::Client.new(locations: { | ||
products: { | ||
schema: GraphQL::Schema.from_definition(...), | ||
executable: GraphQL::Stitching::HttpExecutable.new( | ||
url: "http://localhost:3000", | ||
upload_types: ["Upload"], # << extract "Upload" scalars into multipart forms | ||
), | ||
}, | ||
showtimes: { | ||
schema: GraphQL::Schema.from_definition(...), | ||
executable: GraphQL::Stitching::HttpExecutable.new( | ||
url: "http://localhost:3001" | ||
), | ||
}, | ||
}) | ||
``` | ||
|
||
Note that `upload_types` adds request processing, so it should only be enabled for locations that actually recieve file uploads. Those locations can again leverage [apollo_upload_server](https://github.com/jetruby/apollo_upload_server-ruby) to unpack the multipart form sent by stitching. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# frozen_string_literal: true | ||
|
||
source 'https://rubygems.org' | ||
|
||
gem 'rack' | ||
gem 'rackup' | ||
gem 'foreman' | ||
gem 'graphql' | ||
gem 'apollo_upload_server', '2.1' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
gateway: bundle exec ruby gateway.rb | ||
remote: bundle exec ruby remote.rb |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
```shell | ||
curl -X POST http://localhost:3000 \ | ||
-H 'Content-Type: application/json' \ | ||
-d '{"query":"{ gateway remote }"}' | ||
``` | ||
|
||
```shell | ||
curl http://localhost:3000 \ | ||
-H 'Content-Type: multipart/form-data' \ | ||
-F operations='{ "query": "mutation ($file: Upload!) { gateway upload(file: $file) }", "variables": { "file": null } }' \ | ||
-F map='{ "0": ["variables.file"] }' \ | ||
-F [email protected] | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Hello World! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'rackup' | ||
require 'json' | ||
require 'graphql' | ||
require_relative '../../lib/graphql/stitching' | ||
require_relative './helpers' | ||
|
||
class StitchedApp | ||
def initialize | ||
@client = GraphQL::Stitching::Client.new(locations: { | ||
gateway: { | ||
schema: GatewaySchema, | ||
}, | ||
remote: { | ||
schema: RemoteSchema, | ||
executable: GraphQL::Stitching::HttpExecutable.new( | ||
url: "http://localhost:3001", | ||
upload_types: ["Upload"] | ||
), | ||
}, | ||
}) | ||
end | ||
|
||
def call(env) | ||
params = apollo_upload_server_middleware_params(env) | ||
result = @client.execute( | ||
query: params["query"], | ||
variables: params["variables"], | ||
operation_name: params["operationName"], | ||
) | ||
|
||
[200, {"content-type" => "application/json"}, [JSON.generate(result)]] | ||
end | ||
end | ||
|
||
Rackup::Handler.default.run(StitchedApp.new, :Port => 3000) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'action_dispatch' | ||
require 'apollo_upload_server/graphql_data_builder' | ||
require 'apollo_upload_server/upload' | ||
|
||
# ApolloUploadServer middleware only modifies Rails request params; | ||
# for simple Rack apps we need to extract the behavior. | ||
def apollo_upload_server_middleware_params(env) | ||
req = ActionDispatch::Request.new(env) | ||
if env['CONTENT_TYPE'].to_s.include?('multipart/form-data') | ||
ApolloUploadServer::GraphQLDataBuilder.new(strict_mode: true).call(req.params) | ||
else | ||
req.params | ||
end | ||
end | ||
|
||
# Gateway local schema | ||
class GatewaySchema < GraphQL::Schema | ||
class Query < GraphQL::Schema::Object | ||
field :gateway, Boolean, null: false | ||
|
||
def gateway | ||
true | ||
end | ||
end | ||
|
||
class Mutation < GraphQL::Schema::Object | ||
field :gateway, Boolean, null: false | ||
|
||
def gateway | ||
true | ||
end | ||
end | ||
|
||
query Query | ||
mutation Mutation | ||
end | ||
|
||
# Remote local schema, with file upload | ||
class RemoteSchema < GraphQL::Schema | ||
class Query < GraphQL::Schema::Object | ||
field :remote, Boolean, null: false | ||
|
||
def remote | ||
true | ||
end | ||
end | ||
|
||
class Mutation < GraphQL::Schema::Object | ||
field :upload, String, null: true do | ||
argument :file, ApolloUploadServer::Upload, required: true | ||
end | ||
|
||
def upload(file:) | ||
file.read | ||
end | ||
end | ||
|
||
query Query | ||
mutation Mutation | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'rackup' | ||
require 'json' | ||
require 'graphql' | ||
require_relative './helpers' | ||
|
||
class RemoteApp | ||
def call(env) | ||
params = apollo_upload_server_middleware_params(env) | ||
result = RemoteSchema.execute( | ||
query: params["query"], | ||
variables: params["variables"], | ||
operation_name: params["operationName"], | ||
) | ||
|
||
[200, {"content-type" => "application/json"}, [JSON.generate(result)]] | ||
end | ||
end | ||
|
||
Rackup::Handler.default.run(RemoteApp.new, :Port => 3001) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.