-
Notifications
You must be signed in to change notification settings - Fork 11
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
[Networking] - useAPIClients Init Custom Hook + Auth changes to enable mutations/subscriptions #1068
base: drew-hart--auth-federatedsignin
Are you sure you want to change the base?
[Networking] - useAPIClients Init Custom Hook + Auth changes to enable mutations/subscriptions #1068
Conversation
@@ -27,18 +27,20 @@ const theme = createTheme({ | |||
|
|||
function App() { | |||
const [alert, setAlert] = useState<Alert | null>(null); | |||
const apiClients = new APIClients(Environment.Developing); | |||
const alertContext = { | |||
alert, | |||
setAlert, | |||
}; | |||
|
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.
useApiClient custom hook for initialization of AuthAPIClient first, and then other APIClients
@@ -4,7 +4,7 @@ import { styled } from "@mui/system"; | |||
import { Link } from "react-router-dom"; |
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.
central_v2 will be reworked in a subsequent set of PRs in Q3 (Design is still working on it)
@@ -0,0 +1,5 @@ | |||
{ |
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 role is required to ensure that the createGame
Lambda function is explicitly provided with its Execution Role. See the Wiki article for a link to the AWS docs
@@ -7,12 +7,12 @@ $util.qr($ctx.stash.put("hasAuth", true)) | |||
$util.unauthorized() | |||
#end |
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 there is a bug with Amplify that requires us to manually set these resolvers. Pretty annoying so I'll talk to our AWS contact next time. See Wiki for a link to an Amplify issue that confirms this and recommends the below edits to resolve
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.
aww yeah this sounds like a bug as these are auto-gen and we'd need to keep updating them after each code changes
@@ -1,10 +1,18 @@ | |||
type Mutation { | |||
createGameSessionFromTemplate(input: CreateGameSessionFromTemplateInput!): String @function(name: "createGame-${env}") | |||
createGameSessionFromTemplate(input: CreateGameSessionFromTemplateInput!): 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.
@auth directive required for custom mutations and subscriptions as well as the @types
@@ -44,11 +45,13 @@ export interface IQueryParameters { | |||
|
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.
pass AuthAPIClient to other APIClients to provide auth to the API requests
@@ -0,0 +1,25 @@ | |||
import { useState, useEffect } from 'react'; |
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.
custom Hook to provide proper sequence for APIClient init. First init AuthAPIClients and then pass those clients down into the other APIClients
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.
Great job on this, this looks great, and I love how you have organized the files and methods ❤️
@@ -7,12 +7,12 @@ $util.qr($ctx.stash.put("hasAuth", true)) | |||
$util.unauthorized() | |||
#end |
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.
aww yeah this sounds like a bug as these are auto-gen and we'd need to keep updating them after each code changes
|
||
async function createAndSignRequest(query, variables) { | ||
const credentials = await defaultProvider()(); |
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.
Two API calls here? If so, makes sense to break it into two lines IMO. But if it's a common pattern in JS, then lets leave it
private constructor(env: Environment, authClient: IAuthAPIClient) { | ||
this.auth = authClient; | ||
this.gameTemplate = new GameTemplateAPIClient(env, this.auth); | ||
this.questionTemplate = new QuestionTemplateAPIClient(env, this.auth); | ||
this.gameQuestions = new GameQuestionsAPIClient(env, this.auth); | ||
this.gameSession = new GameSessionAPIClient(env, this.auth); | ||
this.question = new QuestionAPIClient(env, this.auth); | ||
this.team = new TeamAPIClient(env, this.auth); | ||
this.teamMember = new TeamMemberAPIClient(env, this.auth); | ||
this.teamAnswer = new TeamAnswerAPIClient(env, this.auth); | ||
} |
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.
If this list grows or becomes hard to maintain, we can probably write a script to auto-gen this and this whole file actually. Following the name XXXAPIClient
for any API client would help with this as the script could just find all XXXAPIClient
files and then generate this file and include one line for each. So lets make sure we follow the same naming pattern for any new api client we might add in future
static async create(env: Environment): Promise<APIClients> { | ||
const authClient = new AuthAPIClient(); | ||
await authClient.init(); // Ensure the auth client is initialized | ||
return new APIClients(env, authClient); |
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, wondering if we should do lazy loading / singleton here instead of create
method as create
can be called multiple times to create new instances which I doubt it's what we want. This is very minor feedback so we can figure it out later as some also suggest against singleton pattern when possible
@@ -57,7 +60,21 @@ export abstract class BaseAPIClient { | |||
query: any, | |||
options?: GraphQLOptions | |||
): Promise<GraphQLResult<T>> { | |||
const response = client.graphql({query: query, variables: options, authMode: "userPool" as GraphQLAuthMode}) as unknown; | |||
const authMode = this.auth.isUserAuth ? "userPool" : "iam"; |
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.
Always recommended to use an enum with associated string values, especially in cases like this that same strings are referenced in multiple locations. Other pattern is just creating a static const variable and then use that. This reduces typo issues and also if you need to add any new cases or something, you have one place to add and then compiler would throw an 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.
Another comment here is that maybe we can create a method that returns authMode
, something like:
const getAuthMode => {
this.auth.isUserAuth ? "userPool" : "iam";
}
I know it's juts a one liner abstraction, but I think it makes the code more readable and moves the focus to what the actual method is doing rather than thinking why authMode
is userPool
vs. iam
etc
configAmplify(awsconfig: any): void { | ||
Amplify.configure(awsconfig); | ||
// change userPools auth storage to cookies so that auth persists across central/host apps for signed-in teachers | ||
cognitoUserPoolsTokenProvider.setKeyValueStorage(new CookieStorage()); |
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, I need to read on it, but looks like there're some other storages as well like browser storage or session storage. I don't think we've tested our product in cookie-less environment, but worth to think about what would happen if cookie is disabled in browser. I remember back in the day, some were disabling it for security reasons or tracking purposes.
const authClient = new AuthAPIClient(); | ||
await authClient.init(); // Ensure the auth client is initialized |
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.
Why not combining these together and create a method like AuthAPIClient.create
? That way, we can mark the constructor
as private
so it indicates that `AuthClient can't be instantiated through constructor
@@ -1,3 +1,4 @@ | |||
import { Subscription } from 'rxjs'; // this is used in aws-amplify to handle subscriptions |
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.
Nice find!
): Promise<any> { | ||
) { |
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.
Intentionally removing the return type?
@@ -1,6 +1,7 @@ | |||
|
|||
export interface IAuthAPIClient { | |||
isUserAuth: boolean; | |||
init(): Promise<void>; |
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.
See the comment above regarding combining this with constructor into one method
This completes the work on the
Auth
layer. For a full description of this layer, as well as an overview of the new API client and a step-by-step review of the setup process, see the below doc in the Wiki:https://github.com/rightoneducation/righton-app/wiki/Backend-%E2%80%90-Authentication-and-Authorization
The PRs break down to:
-> [Backend] - Google OAuth Integration #988
->-> [Central_v2] - Container for authorization #1013
->->-> [Authentication/Authorization] - Networking updates #1058
->->->-> [Networking] - Upgrade Amplify v5 to v6 #1059
->->->->-> [Networking] - AuthAPIClient #1060
->->->->->-> [Networking] - useAPIClients Init Custom Hook + Auth changes to enable mutations/subscriptions
Review Cheat Sheet
Hey @maniramezan , this set of PR's is a bit confusing for two reasons:
Therefore:
Hopefully, this makes your job a bit easier when reviewing this stuff. Definitely let me know if anything is confusing though!