-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Update AuthUser to offer a better DX #1915
Conversation
39f81eb
to
316bc30
Compare
@@ -88,20 +87,5 @@ type Context<Entities extends _Entity[]> = Expand<{ | |||
{=# isAuthEnabled =} | |||
type ContextWithUser<Entities extends _Entity[]> = Expand<Context<Entities> & { user?: AuthUser }> | |||
|
|||
// TODO: This type must match the logic in auth/session.js (if we remove the |
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 TODO is now resolved yay! Since the type and the runtime value are based on the same logic.
@@ -43,3 +46,13 @@ emailAuthProvider = | |||
{ E._providerId = fromJust $ makeProviderId "email", | |||
E._displayName = "Email and password" | |||
} | |||
|
|||
getEnabledAuthProvidersJson :: AS.Auth.Auth -> Aeson.Value |
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.
Generating this JSON in one place instead of three.
) { | ||
const { auth, ...rest } = user | ||
const identities = { | ||
{=# enabledProviders.isEmailAuthEnabled =} |
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.
Generating fields based on the enabled auth methods.
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.
Took a quick look, looks good in general, left some comments!
But I will let @sodic do a proper review.
How would you describe the crux of this PR regarding its impact on the user? getFirstProviderId
is now method on user
. You also have createAuthUser
which helps with something from what I saw. Something else? This is only part of the Auth DX improvements right?
return { | ||
...rest, | ||
identities, | ||
getFirstProviderUserId: () => getFirstProviderUserId(user), |
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.
Aha, so this is now method on the user!
import express from 'express' | ||
|
||
export const fooBar: FooBar = (_req, res, context) => { | ||
const username = getFirstProviderUserId(context?.user) ?? 'Anonymous' | ||
const username = context.user?.getFirstProviderUserId() |
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.
Ok cool this is nicer now!
What does getFirstProviderUserId
promise? If I have multiple providers, it will return id from one of them. And it will be consistent in the order, right? What if I add a new provider? Maybe this ID will become different, right? That might be ok,I am just curious.
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 will return the first available provider :D the order might change with if you add a new provider.
I predict that this method will be mostly used by people who only have one provider anyways or just want to display something identifying the user. For proper "production usage" I suspect people will want to put some sort of an "email" or "username" on the User
object anyways.
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 agree! We should make sure to mention this in the docs of this function, to make it clear how it is intended to be used, and what the pitfalls might be, and why they should therefore potentially put stuff on User
instead. I don't think this stuff will be clear to a new Wasp user.
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 mentioned putting stuff on User
here: https://github.com/wasp-lang/wasp/pull/1915/files#diff-1da5418e699dcbebea8ab516c4e60235efd7191abad17057916ca5f9bba2ddb1R263
But I didn't comment on the pitfalls of the getFirstProviderUserId
method.
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'm a big fan of this change!
Left a couple of comments to discuss.
@@ -3,7 +3,7 @@ import { deserialize as superjsonDeserialize } from 'superjson' | |||
import { useQuery, addMetadataToQuery } from 'wasp/client/operations' | |||
import { api, handleApiError } from 'wasp/client/api' | |||
import { HttpMethod } from 'wasp/client' | |||
import type { AuthUser } from './types' | |||
import type { AuthUser } from 'wasp/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.
We're phasing out package imports in the SDK (as of very recently 😄), so let's not add new ones:
Context: #1922
} | ||
|
||
return newUser | ||
return createAuthUser(newUser as 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.
This is something the users will have to do, I assume?
If that's the case, can we avoid the assertion?
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 wanted to get this code to work, I wanted to make it work without an assertion but it proved difficult. I'll give it an another try.
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've now hidden this API since it would expose some server imports on the client. Now this test is written a bit differently with the {} as AuthUser
method which works well for these kinds of tests.
@@ -8,7 +7,7 @@ export const webSocketFn: WebSocketDefinition< | |||
InterServerEvents | |||
> = (io, context) => { | |||
io.on('connection', (socket) => { | |||
const username = getFirstProviderUserId(socket.data.user) ?? 'Unknown' | |||
const username = socket.data.user?.getFirstProviderUserId() ?? 'Unknown' |
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.
How will we decide whether we want methods or getters? Is there a rule of thumb in the JS world?
Getters are nicer, and look less bureaucratic, but if some contain arguments, then the API looks janky.
Also, what if one of the getters that doesn't need any arguments today ends up needing arguments in the future?
Then again, getters are nicer and look less like java bureaucratic.
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 was using a rule of thumb: if it's a property that they would be able to get just by going through the raw DB entity, I put a getter. If it's something special like getting the first available provider, I used a function.
The options seem to be:
user.getIdentities().email
user.identities.email
user.firstProviderId
user.getFirstProviderId()
The combination felt okay to me, as user.firstProviderId
feels off since the jobs of getting the first provider id feels dynamic.
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 like this logic, but I do have to admit I am also not quite used to using JS getters, so I can imagine somebody possibly convincing me the other way. But with knowledge I have, I like it.
if (user.identities.google !== null) { | ||
return `Google user ${user.identities.google.id}` |
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.
Much better!
b03e3ff
to
6e843a4
Compare
Docs are ready for review. I've tried to rewrite them to fit the new API and explain some extra stuff. |
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.
@infomiho I gave it a look, looks fine in general! Seems like there was no big structural changes, you mostly covered the change in the API + added the migration instructions?
It can be a bit hard to review docs sometimes, even harder than code, because in code I can really on Typescript / Haskell to know that stuff is vibing together, and I can somehow easier get a bigger picture, in docs it is a bit harder to figure out what was supposed to change, what is the context of the rest of the docs, ... . What might be helpful when doing docs change like this is if you wrote a tiny bit about what you changed, from the very high level, to give me an idea of what I am looking at, what should I focus on. You did that somewhat "rewrite ... new API ... explain extra stuff", so maybe that is it, not sure, but for example I would to hear if you want me to look at something specific, maybe what is that new stuff, ... .
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.
@infomiho responded to comments!
474c5fa
to
1e9969b
Compare
Signed-off-by: Mihovil Ilakovac <[email protected]>
Signed-off-by: Mihovil Ilakovac <[email protected]>
Signed-off-by: Mihovil Ilakovac <[email protected]>
Signed-off-by: Mihovil Ilakovac <[email protected]>
1e9969b
to
cd46f61
Compare
Note
This is probably need to be a breaking change to make the API clean.