-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(api): add custom endpoint support to API #14086
Conversation
of testing depends on creating appsync connections to nowhere
const memoKey = appSyncGraphqlEndpoint ?? 'none'; | ||
const realtimeProvider = | ||
this.appSyncRealTime.get(memoKey) ?? new AWSAppSyncRealTimeProvider(); | ||
this.appSyncRealTime.set(memoKey, realtimeProvider); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This overwrites the memoKey, even when we just retrieved the provider from the set doesn't it? It's probably not doing harm, but seems like an unnecessary operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I weighed it against a conditional check to do the same. This seemed simpler to read. But, I don't mind changing it if you disagree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The conditional check seems like fewer moving parts or things to go wrong, but I also don't think I can come up with an example of when this would fail us, so this is a nit at best.
if (!client[__endpoint]) { | ||
if (isApiGraphQLConfig(apiGraphqlConfig)) { | ||
addSchemaToClient(client, apiGraphqlConfig, getInternals); | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old code would always either run addSchemaToClient
or generateModelsPropertyOnAmplifyConfigure
where the new code could run neither when there is an endpoint in the params and we don't raise the auth errors above, right?
Is it a problem to return a client without running one of these two functions?
Thinking over the change, probably not since we don't have a schema or models to bind in these additional endpoint use-cases. Interesting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's very deliberately skipping both to avoid attaching .models
etc. and potentially causing confusion there — if someone were logging out the client and poking around for example, they'd see methods from their MIS that have nothing to do with the endpoint.
if (client[__endpoint]) { | ||
if (!client[__authMode]) { | ||
throw new Error( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This error logic is repeated from above, is other similar code shared in some way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably more LOC to do so to accommodate the slightly different error messaging. Is it worth it to you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit. Probably not worth it.
? { | ||
query: TYPED_GQL_STRING | DocumentNode; | ||
variables?: GraphQLVariablesV6<FALLBACK_TYPES, TYPED_GQL_STRING>; | ||
authMode?: GraphQLAuthMode; | ||
apiKey?: string; | ||
authToken?: string; | ||
/** | ||
* @deprecated This property should not be used | ||
*/ | ||
userAgentSuffix?: string; | ||
} | ||
: | ||
| { | ||
query: TYPED_GQL_STRING | DocumentNode; | ||
variables?: GraphQLVariablesV6<FALLBACK_TYPES, TYPED_GQL_STRING>; | ||
authMode?: never; | ||
apiKey?: never; | ||
authToken?: string; | ||
/** | ||
* @deprecated This property should not be used | ||
*/ | ||
userAgentSuffix?: string; | ||
} | ||
| { | ||
query: TYPED_GQL_STRING | DocumentNode; | ||
variables?: GraphQLVariablesV6<FALLBACK_TYPES, TYPED_GQL_STRING>; | ||
authMode: 'apiKey'; | ||
apiKey: string; | ||
authToken?: string; | ||
/** | ||
* @deprecated This property should not be used | ||
*/ | ||
userAgentSuffix?: string; | ||
} | ||
| { | ||
query: TYPED_GQL_STRING | DocumentNode; | ||
variables?: GraphQLVariablesV6<FALLBACK_TYPES, TYPED_GQL_STRING>; | ||
authMode: Exclude<GraphQLAuthMode, 'apiKey'>; | ||
apiKey?: never; | ||
authToken?: string; | ||
/** | ||
* @deprecated This property should not be used | ||
*/ | ||
userAgentSuffix?: string; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this be better expressed as a generic where we pass in override types to we can repeat it with different overrides to create this union? This seems like an easy point to accidentally inject errors trying to make simple modifications all spelled out like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, I did go down the generic route and found that TS was too confused to offer suggestions while typing (pressing ctrl+space in particular). I'm not sure if I made a mistake, but I then had to go on a small crusade to simplify the types down into the "dumbest" possible versions of themselves and it seemed to contribute to resolving the issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. Don't think we have the prettify wrapper here, but I think that might improve the CX on a more concise solution. Not sure where I land on this, this seems pretty long form.
// attempting to connect to nowhere. So, I'm treating as the opposite of | ||
// a Chesterton's fence for now. (A fence I shouldn't build, because I don't | ||
// know why somethings depends on its absence!) | ||
const memoKey = appSyncGraphqlEndpoint ?? 'none'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this memoKey sufficient? Wondering if a websocket might ever be accessed with different authtypes to achieve different permissions where both would have the same endpoint (or something similar)
I think in all case, overriding a graphql calls authtype works, but maybe a client with authtype A will be recreated with authtype B and the dev will be surprised that they get the original instance with type A...
Any other config that might be needed in the memoKey to avoid surprises?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should be OK. We have normally used a single socket for all connections regardless of the authmode needed by the subscription itself. In chatting with Ivan about how to do this, I think he leaned towards keeping connections pooled by client — in most cases, I think that's slightly better for customers. Fewer local resources consumed at the very least, etc.. It's marginal. I wouldn't die on that hill.
If it causes issues, I think this behavior is pretty OK for us to change later.
const memoKey = appSyncGraphqlEndpoint ?? 'none'; | ||
const realtimeProvider = | ||
this.appSyncRealTime.get(memoKey) ?? new AWSAppSyncRealTimeProvider(); | ||
this.appSyncRealTime.set(memoKey, realtimeProvider); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The conditional check seems like fewer moving parts or things to go wrong, but I also don't think I can come up with an example of when this would fail us, so this is a nit at best.
if (client[__endpoint]) { | ||
if (!client[__authMode]) { | ||
throw new Error( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit. Probably not worth it.
? { | ||
query: TYPED_GQL_STRING | DocumentNode; | ||
variables?: GraphQLVariablesV6<FALLBACK_TYPES, TYPED_GQL_STRING>; | ||
authMode?: GraphQLAuthMode; | ||
apiKey?: string; | ||
authToken?: string; | ||
/** | ||
* @deprecated This property should not be used | ||
*/ | ||
userAgentSuffix?: string; | ||
} | ||
: | ||
| { | ||
query: TYPED_GQL_STRING | DocumentNode; | ||
variables?: GraphQLVariablesV6<FALLBACK_TYPES, TYPED_GQL_STRING>; | ||
authMode?: never; | ||
apiKey?: never; | ||
authToken?: string; | ||
/** | ||
* @deprecated This property should not be used | ||
*/ | ||
userAgentSuffix?: string; | ||
} | ||
| { | ||
query: TYPED_GQL_STRING | DocumentNode; | ||
variables?: GraphQLVariablesV6<FALLBACK_TYPES, TYPED_GQL_STRING>; | ||
authMode: 'apiKey'; | ||
apiKey: string; | ||
authToken?: string; | ||
/** | ||
* @deprecated This property should not be used | ||
*/ | ||
userAgentSuffix?: string; | ||
} | ||
| { | ||
query: TYPED_GQL_STRING | DocumentNode; | ||
variables?: GraphQLVariablesV6<FALLBACK_TYPES, TYPED_GQL_STRING>; | ||
authMode: Exclude<GraphQLAuthMode, 'apiKey'>; | ||
apiKey?: never; | ||
authToken?: string; | ||
/** | ||
* @deprecated This property should not be used | ||
*/ | ||
userAgentSuffix?: string; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. Don't think we have the prettify wrapper here, but I think that might improve the CX on a more concise solution. Not sure where I land on this, this seems pretty long form.
Description of changes
Adds optional
endpoint
andapiKey
properties togenerateClient()
options, allowing customers to use Amplify to connect to any accessible GraphQL API from within an app.Also adds support for subscribing to multiple endpoints simultaneously.
When an alternative
endpoint
is provided:authMode
must be explicitly set. Omitting it is both a type error and runtime error.authMode: "apiKey"
requires thatapiKey
also be explicitly set; whether this mode is set ingenerateClient()
or.graphql()
downstream. Omitting it is both a type error and runtime error.Issue #, if available
Description of how you validated changes
Checklist
yarn test
passesChecklist for repo maintainers
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.