Make communicating with Apollo's commercial products into ordinary plugins#4453
Make communicating with Apollo's commercial products into ordinary plugins#4453glasser merged 35 commits intorelease-2.18.0from
Conversation
|
One note: I definitely want to make the tests not print a zillion |
2faa90d to
3d61611
Compare
69b390f to
526443a
Compare
|
Added a small number of explicit |
|
Naming thought: ApolloConfigInput becomes ApolloConfig, ApolloConfig becomes ApolloContext? |
|
Updated this PR's description to be broader than the first step (the existing commit's description maintains) and added a checklist. |
f0b08ea to
0d94ec6
Compare
|
I'm kinda unsure about naming for the plugins. Our whole-package plugins export them as default exports, so they don't really have names. |
| headers: { | ||
| 'user-agent': 'apollo-engine-reporting', | ||
| 'x-api-key': this.apiKey, | ||
| 'x-api-key': this.apolloConfig.key!, |
There was a problem hiding this comment.
Non-blocking / nit: this.apolloConfig is actually more of a WithRequired<ApolloConfig, 'key'> due to the assertion in the constructor. Refining that type would mean we don't have to assert its existence here or anywhere else it's being used.
| // mode and warn about deprecation | ||
| if (apollo) { | ||
| throw new Error("You cannot provide both `engine` and `apollo` to `new ApolloServer()`. " + | ||
| "For details on how to migrate all of your options out of `engine`, see FIXME(no-engine) URL MISSING"); |
There was a problem hiding this comment.
I know this is WIP, just noting FIXMEs for later.
There was a problem hiding this comment.
Thanks. It's in the PR description checklist too.
| // FIXME(no-engine): This should be changed to check for a deprecation | ||
| // warning for any use of `engine` (which we can't really do until splitting | ||
| // out the plugins). | ||
| it.skip('spits out a deprecation warning when passed a schemaTag in construction', () => { |
| @@ -112,32 +116,47 @@ describe('environment variables', () => { | |||
| it('constructs a reporting agent with the ENGINE_API_KEY (deprecated) environment variable and warns', async () => { | |||
| // set the variables | |||
| process.env.ENGINE_API_KEY = 'just:fake:stuff'; | |||
There was a problem hiding this comment.
Nothing you've done here, but is this polluting the global test env? Do we need to delete it as cleanup? I'm actually not sure if jest persists process.env between tests.
There was a problem hiding this comment.
I had the same thought, but there's a beforeEach/afterEach above.
There was a problem hiding this comment.
@trevor-scheer fwiw, I think process.env within the same Jest test-runner worker would be persisted (i.e., would not be reset to its state at startup), yes. (But the beforeEach / afterEach is the key here.)
| process.env.ENGINE_API_KEY = 'just:fake:stuff'; | ||
| process.env.APOLLO_KEY = 'also:fake:stuff'; |
There was a problem hiding this comment.
Same concern here, maybe.
| throw new Error( | ||
| 'Cannot set more than one of apollo.graphVariant, ' + | ||
| 'engine.graphVariant, and engine.schemaTag. Please use apollo.graphVariant.', | ||
| ); | ||
| } | ||
| apolloConfig.graphVariant = engine.graphVariant; | ||
| } else if (typeof engine === 'object' && engine.schemaTag) { | ||
| // No need to warn here, because ApolloServer's constructor should warn about | ||
| // the existence of `engine` at all. | ||
| apolloConfig.graphVariant = engine.schemaTag; | ||
| } else if (APOLLO_GRAPH_VARIANT) { | ||
| if (ENGINE_SCHEMA_TAG) { | ||
| throw new Error( | ||
| '`APOLLO_GRAPH_VARIANT` and `ENGINE_SCHEMA_TAG` (deprecated) environment variables must not both be set.', | ||
| ); |
There was a problem hiding this comment.
Do we want to warn and carry on gracefully in these throw cases? Or are we throwing because if the values are different we don't know which is the right one to pick?
I know this complicates the logic a bit but we could carry on if:
- they're identical (safe)
- we just decide that newer config option takes more precedence (arguably less safe)
There was a problem hiding this comment.
This throw is preserving existing behavior. Has anyone complained about it yet?
In general I think "make sure untouched configuration still works" is a good goal but "let people write confusing config that is half old and half new" isn't positive.
There was a problem hiding this comment.
I think the half-old/half-new requirement was because of the apollo CLI (or other tooling) respecting one set of environment variables and Server respecting something different. We can probably leave this now that I think the CLI is sorted out.
| ); | ||
| } | ||
|
|
||
| const executableSchema = overrideReportedSchema ?? printSchema(schema); |
There was a problem hiding this comment.
Executable schema generally means an actual GraphQLSchema, so the naming here is a bit misleading. printedSchema?
There was a problem hiding this comment.
For some reason, executableSchema is the name of the parameter in our GraphQL API. So I think matching is good?
| const schemaReporter = new SchemaReporter({ | ||
| serverInfo, | ||
| schemaSdl: executableSchema, | ||
| apiKey: apollo.key!, |
There was a problem hiding this comment.
Seems like this shouldn't be necessary, we assert apollo.key's existence above.
There was a problem hiding this comment.
It is necessary (presumably TypeScript isn't positive that apollo.key isn't from some sort of getter?) but I can fix it in another way by saving to a const.
| @@ -0,0 +1,271 @@ | |||
| // This class is a helper for ApoloServerPluginUsageReporting and | |||
There was a problem hiding this comment.
| // This class is a helper for ApoloServerPluginUsageReporting and | |
| // This class is a helper for ApolloServerPluginUsageReporting and |
3d61611 to
67dec32
Compare
9692ee5 to
9c352bb
Compare
abernix
left a comment
There was a problem hiding this comment.
Thank you so much for putting in the effort to organize and de-Engine-ize this code!
I've left a number of comments throughout, but I think my notable points are around the:
- Un-conditional importing of modules which were previously avoided when Apollo Cloud services were disabled. This includes Node.js built-in APIs (like
crypto,zlib,os) but also heavier dependencies, likeapollo-reporting-protobuf. Not only had we switched to conditionally importing these some time ago (to support non-Node.js runtimes, like JavaScriptCore and V8), but also to improve the cold-boot time in Worker/Edge/Lambda-type environments. - The
__internal_plugin_id__(and if there is another way)
| @@ -112,32 +116,47 @@ describe('environment variables', () => { | |||
| it('constructs a reporting agent with the ENGINE_API_KEY (deprecated) environment variable and warns', async () => { | |||
| // set the variables | |||
| process.env.ENGINE_API_KEY = 'just:fake:stuff'; | |||
There was a problem hiding this comment.
@trevor-scheer fwiw, I think process.env within the same Jest test-runner worker would be persisted (i.e., would not be reset to its state at startup), yes. (But the beforeEach / afterEach is the key here.)
| throw new Error( | ||
| 'Cannot set more than one of apollo.graphVariant, ' + | ||
| 'engine.graphVariant, and engine.schemaTag. Please use apollo.graphVariant.', | ||
| ); | ||
| } | ||
| apolloConfig.graphVariant = engine.graphVariant; | ||
| } else if (typeof engine === 'object' && engine.schemaTag) { | ||
| // No need to warn here, because ApolloServer's constructor should warn about | ||
| // the existence of `engine` at all. | ||
| apolloConfig.graphVariant = engine.schemaTag; | ||
| } else if (APOLLO_GRAPH_VARIANT) { | ||
| if (ENGINE_SCHEMA_TAG) { | ||
| throw new Error( | ||
| '`APOLLO_GRAPH_VARIANT` and `ENGINE_SCHEMA_TAG` (deprecated) environment variables must not both be set.', | ||
| ); |
There was a problem hiding this comment.
I think the half-old/half-new requirement was because of the apollo CLI (or other tooling) respecting one set of environment variables and Server respecting something different. We can probably leave this now that I think the CLI is sorted out.
| const { cache: apqCache = requestOptions.cache!, ...apqOtherOptions } = | ||
| requestOptions.persistedQueries || Object.create(null); |
There was a problem hiding this comment.
Looks a lot like Prettier ran on this file and made a bunch of changes. This one in particular certainly is questionable.
There was a problem hiding this comment.
Ah sorry, yeah, I am planning to de-prettier files that are not new (however, I am using prettier on all new files because as a multi-lingual developer I no longer possess the ability to use my brain to make choices about formatting in N different languages).
There was a problem hiding this comment.
PR is now de-prettiered (or well, the part outside of core/src/plugin).
| const message = "This data graph is missing a valid configuration."; | ||
| this.logger.error(message + " " + (err && err.message || err)); | ||
| const message = 'This data graph is missing a valid configuration.'; | ||
| this.logger.error(message + ' ' + ((err && err.message) || err)); | ||
| throw new Error( | ||
| message + " More details may be available in the server logs."); |
There was a problem hiding this comment.
Not sure how you feel about backing these out, but it would be nice.
| import os from 'os'; | ||
| import { gzip } from 'zlib'; | ||
| // FIXME(no-engine): make sure these deps are declared | ||
| import retry from 'async-retry'; | ||
| import { defaultEngineReportingSignature } from 'apollo-graphql'; | ||
| import { | ||
| Report, | ||
| ReportHeader, | ||
| Trace, | ||
| TracesAndStats, | ||
| } from 'apollo-reporting-protobuf'; |
There was a problem hiding this comment.
Just my same concern about Node.js APIs being now in the direct line of imports / module resolution.
67dec32 to
dc5b8f5
Compare
dd2e81f to
73700cb
Compare
|
@abernix This is moderately close to "code complete" (I've finished and fixed up the commit that gets rid of One thing I just learned is how the Lambda/Cloud Functions/Azure ApolloServer constructors default
Does that sound like a reasonable thing to have in the API? I think it may be helpful to other plugins as well. Do you have a better name in mind? |
73700cb to
4953b86
Compare
This option was previously not explicitly documented except on the plugins page. Add a basic description and mention that the built-in plugins enable themselves in some contexts. (I didn't mention that the JSON tracing and cache-control plugins work the same way; the fact that those features are implemented as plugins is an internal implementation as those plugins are not directly exposed to users.) There are many other ApolloServer options not mentioned on this reference page! In case you are curious, they are: - cache - cacheControl - dataSources - executor - extensions - fieldResolver - gateway - modules - parseOptions - tracing - uploads
I dropped the reference to debugPrintReports (which is still in the plugin reference) because it barely works (the actual traces are not included).
This unifies all of our APIs behind `*.api.apollographql.com` and removes references to the obsolete names "engine", "apollodata", and "edge server reporting".
I was originally planning to log a deprecation warning when the `engine` option is passed, but I currently think that's overkill. Migrating isn't particularly hard but it's also not particularly urgent for current users, as long as new users don't have to be exposed to the old API. Instead restore the previous behavior of logging a deprecation warning for `engine.schemaTag` specifically. Opinions welcome.
Primarily docs, comments, and strings changes. Fix an URL added incorrectly in this PR. Remove a comment mentioning GraphQLServerOptions.reporting which was removed in v2.15.0.
Delete maskErrorDetails integration test and replace it with a unit test.
it's helpful to have an actionable message when something goes wrong aside from a simple "contact support" if there is a way to achieve the action that caused the error. in this case, the user is likely trying to set up Apollo Studio with their service, which they can do if they follow along with the documentation. this change adds a link to said documentation to immediately unblock users who run into this error.
(By avery and glasser!)
This doesn't change the run-time mechanism but does remove it from the public ApolloServerPlugin interface while still allowing for type safety within this package.
There are very few uses of `logger.info` in Apollo Server (though custom plugins could use it). This mostly means that startup logs in the plugins will be visible by default. Users can override this by providing their own `logger`.
The Apollo API has recently changed such that `reportServerInfo` will now return error for schemas that don't parse or validate. This PR changes `apollo-engine-reporting` to check in the `EngineReportingAgent` constructor whether override schemas successfully parse and validate, so that we may return early before talking to the Apollo API. (Originally #4492 by @sachindshinde, merged directly into release-2.18.0; because the main change being released with v2.18 is #4453 which entirely replaced the file which that PR changed, this is being reconstructed as part of PR #4453.)
8a17501 to
a897b59
Compare
Currently, the EngineReportingAgent is deeply embedded in ApolloServer, configured via a special
engineoption tonew ApolloServer. Additionally, Engine is no longer the name of the product.This PR will remove this special-casing and replace EngineReportingAgent with a few standard ApolloServerPlugins. It will replace the
engineconstructor argument with anapolloargument that's used only to configure the API key and graph ID/variant, not for detailed configuration of reporting semantics. Other than thisapolloargument (which will also be passed to the plugin's serverWillStart hook) and backwards-compatibility handling, the only special treatment of these Apollo platform plugins will be that if you do actively configure an Apollo API key and don't set up a usage reporting plugin yourself or actively disable it, a plugin will be set up with the default configuration.apolloconstructor argument and use it in existing plugins/gatewayengineoptionapollo, etc)enginein the repocurrentinto apollo-server more than it already is@apollo/federationand@apollo/gatewayfrom theapollo-serverrepository. federation#134 merges?)