uniiverse / apollo-tracing-ruby

[Maintainers Wanted] Ruby implementation of GraphQL trace data in the Apollo Tracing format
MIT License
85 stars 10 forks source link

Support for multiple HTTP origins #4

Closed jaryl closed 6 years ago

jaryl commented 6 years ago

I have a use case where we have different schemas and endpoints within the same application. Specifically, we have a different GraphQL for each bounded context within our application.

However, when providing a configuration file with multiple HTTP origins, it will only take the last line in the configuration.

I believe that the proxy itself works this way, if an exact path match is found, it will forward to the last registered origin. Otherwise, it will simply proxy the request.

To illustrate, this is my configuration:

APOLLO_CONFIG = %Q(
{
  "apiKey": "#{ENV['APOLLO_API_KEY']}",
  "logging": { "level": "#{ENV['APOLLO_LOG_LEVEL']}" },
  "origins": [
    { "http": { "url": "http://#{ENV['HOST']}:#{ENV['PORT']}/api/endpoint_a/" } },
    { "http": { "url": "http://#{ENV['HOST']}:#{ENV['PORT']}/api/endpoint_b/" } }
  ],
  "frontends": [
    { "host": "#{ENV['APOLLO_PROXY_HOST']}", "port": #{ENV['APOLLO_PROXY_PORT']}, "endpoint": "/" }
  ]
}
).freeze

In this case, the frontend's endpoint is /, which will cause all the requests to proxy normally, but this is still a problem because then the tracing data is not stripped out.

If I provide one or more frontends that provide an exact match, like so:

{ "host": "#{ENV['APOLLO_PROXY_HOST']}", "port": #{ENV['APOLLO_PROXY_PORT']}, "endpoint": "/api/endpoint_a/" },
{ "host": "#{ENV['APOLLO_PROXY_HOST']}", "port": #{ENV['APOLLO_PROXY_PORT']}, "endpoint": "/api/endpoint_b/" }

Then as mentioned, only the last endpoint specific in origins is called.

What I would expect is that the proxy allows the configuration of multiple frontends and backends.

exAspArk commented 6 years ago

Hi @jaryl,

Thanks for opening the issue!

We use apollo-engine gem with a few endpoints with the URL paths which match each other. For example:

{
  "apiKey": "REPLACEME",
  "logging": { "level": "INFO" },
  "origins": [
    { "http": { "url": "http://localhost:3000/graphql/beta" } },
    { "http": { "url": "http://localhost:3000/graphql" } }
  ],
  "frontends": [
    { "host": "localhost", "port": 3001, "endpoint": "/graphql/beta" },
    { "host": "localhost", "port": 3001, "endpoint": "/graphql" }
  ]
}

And when I hit any of the frontend endpoints, the engine strips the tracing data from the response:

> bundle exec puma -p 3000

> curl -v http://localhost:3001/graphql/beta \                                                    
  -H "Content-Type: application/json" \
  -d @- << EOF
{
  "query": "query {
    profile(slug: \"asdf\") {
      id
    }
  }"
}
EOF

{"data":{"profile":{"id":"1234"}}}

Are you using the latest 1.3.0 version of the apollo-engine gem?

jaryl commented 6 years ago

Thanks @exAspArk for replying, I tried my best to conform to what you did, but it did not work for me.

When I curl at port 3000, this is what I get, which is expected.

* Connected to demo.lvh.me (127.0.0.1) port 3000 (#0)
> POST /api/catalog/ HTTP/1.1
> Host: demo.lvh.me:3000
> User-Agent: curl/7.54.0
> Accept: */*
> Content-Type: application/json
> Authorization: eyJhbGciOiJIUzI1NiJ9.eyJzdG9yZV9pZCI6IjU2YzQwOTNhLWJlNDUtNDViNC04Njg1LWUxNGM1MzIyZjQ4OCIsInNlY3JldCI6IjVjM2FkNWY4MWM0OTI4MmMxYWQ2ODlkMGYxYTA0MWRmNjg2OTY0YTQ1ZjE0NDdmYTQxM2RlMGI1ZDYyYmEwMzk2NTRkYzVlOGJiZTBiMDkyMzE3YTQwY2E1Zjk4MTMxYTEzNDg1OGFmYTRiM2RkY2MzOWIyODFmYWMyY2NiOWJhIiwiaWF0IjoxNTA3ODgyNTM1fQ.1AagXc39u1n6uHSBfHwOOxGtF3mwM4I5_WpaBfdnWro
> Content-Length: 163
>
* upload completely sent off: 163 out of 163 bytes
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< ETag: W/"5ca919328cc1879f18df94afd68394f0"
< Cache-Control: max-age=0, private, must-revalidate
< X-Request-Id: 9800b7ac-976d-4fa5-85c9-258586b71060
< X-Runtime: 0.014920
< Transfer-Encoding: chunked
<
* Connection #0 to host demo.lvh.me left intact
{"data":{"currentStore":{"products":[]}},"extensions":{"tracing":{"version":1,"startTime":"2017-11-27T04:59:32.689Z","endTime":"2017-11-27T04:59:32.693Z","duration":4364013,"execution":{"resolvers":[{"path":["currentStore"],"parentType":"Query","fieldName":"currentStore","returnType":"Store","startOffset":2290725,"duration":83208},{"path":["currentStore","products"],"parentType":"Store","fieldName":"products","returnType":"[Product!]","startOffset":2585887,"duration":395059}]}}}}

However, when I run curl at 3001, I get back with the same tracing data in the response, and an empty Apollo tracing account.

* Connected to demo.lvh.me (127.0.0.1) port 3001 (#0)
> POST /api/catalog/ HTTP/1.1
> Host: demo.lvh.me:3001
> User-Agent: curl/7.54.0
> Accept: */*
> Content-Type: application/json
> Authorization: eyJhbGciOiJIUzI1NiJ9.eyJzdG9yZV9pZCI6IjU2YzQwOTNhLWJlNDUtNDViNC04Njg1LWUxNGM1MzIyZjQ4OCIsInNlY3JldCI6IjVjM2FkNWY4MWM0OTI4MmMxYWQ2ODlkMGYxYTA0MWRmNjg2OTY0YTQ1ZjE0NDdmYTQxM2RlMGI1ZDYyYmEwMzk2NTRkYzVlOGJiZTBiMDkyMzE3YTQwY2E1Zjk4MTMxYTEzNDg1OGFmYTRiM2RkY2MzOWIyODFmYWMyY2NiOWJhIiwiaWF0IjoxNTA3ODgyNTM1fQ.1AagXc39u1n6uHSBfHwOOxGtF3mwM4I5_WpaBfdnWro
> Content-Length: 163
>
* upload completely sent off: 163 out of 163 bytes
< HTTP/1.1 200 OK
< Cache-Control: max-age=0, private, must-revalidate
< Content-Type: application/json; charset=utf-8
< Etag: W/"5db6b329ec195031b97a46a7129936bf"
< X-Request-Id: b5ed9ebe-74d2-41a0-857c-a4d4967520ce
< X-Runtime: 0.011972
< Date: Mon, 27 Nov 2017 04:59:48 GMT
< Content-Length: 484
<
* Connection #0 to host demo.lvh.me left intact
{"data":{"currentStore":{"products":[]}},"extensions":{"tracing":{"version":1,"startTime":"2017-11-27T04:59:48.568Z","endTime":"2017-11-27T04:59:48.570Z","duration":2815008,"execution":{"resolvers":[{"path":["currentStore"],"parentType":"Query","fieldName":"currentStore","returnType":"Store","startOffset":1479864,"duration":83208},{"path":["currentStore","products"],"parentType":"Store","fieldName":"products","returnType":"[Product!]","startOffset":1698017,"duration":195026}]}}}}

I am using lvh.me because we have subdomains, and each of our endpoints have a different schema. Either of this may be throwing the gem off but I am not sure which or why. I will do more troubleshooting soon, without a subdomain or with the same schema, etc.

exAspArk commented 6 years ago

I will do more troubleshooting soon, without a subdomain or with the same schema, etc.

I'll close the issue for now. Feel free to reopen once you have more details! :)