Skip to content
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

Fix the auth helpers to only require fields they need from AuthIdentity #2057

Merged
merged 6 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions waspc/data/Generator/templates/sdk/wasp/auth/user.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
{{={= =}=}}
import { type {= authIdentityEntityName =} } from '../entities/index.js'
import { type ProviderName } from '../server/_types/index.js'
import type { AuthUserData, AuthUser } from '../server/auth/user.js'
import type {
AuthUserData,
AuthUser,
UserEntityWithAuth,
} from '../server/auth/user.js'

/**
* We split the user.ts code into two files to avoid some server-only
* code (Oslo's hashing functions) being imported on the client.
*/
import { type UserEntityWithAuth } from '../server/auth/user.js'

// PUBLIC API
export function getEmail(user: UserEntityWithAuth): string | null {
Expand Down Expand Up @@ -49,7 +51,7 @@ function makeAuthUser(data: AuthUserData): AuthUser {
};
}

function findUserIdentity(user: UserEntityWithAuth, providerName: ProviderName): {= authIdentityEntityName =} | null {
function findUserIdentity(user: UserEntityWithAuth, providerName: ProviderName): UserEntityWithAuth['auth']['identities'][number] | null {
if (!user.auth) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

return null;
}
Expand Down
37 changes: 28 additions & 9 deletions waspc/data/Generator/templates/sdk/wasp/server/auth/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type AuthUser = AuthUserData & {
*
* TODO: Change this once/if we switch to strict mode. https://github.com/wasp-lang/wasp/issues/1938
*/
export type AuthUserData = Omit<UserEntityWithAuth, '{= authFieldOnUserEntityName =}'> & {
export type AuthUserData = Omit<CompleteUserEntityWithAuth, '{= authFieldOnUserEntityName =}'> & {
identities: {
{=# enabledProviders.isEmailAuthEnabled =}
email: Expand<UserFacingProviderData<'email'>> | null
Expand All @@ -54,17 +54,36 @@ type UserFacingProviderData<PN extends ProviderName> = {
} & Omit<PossibleProviderData[PN], 'hashedPassword'>

// PRIVATE API
export type UserEntityWithAuth = {= userEntityName =} & {
{= authFieldOnUserEntityName =}: AuthEntityWithIdentities | null
}
export type CompleteUserEntityWithAuth =
MakeUserEntityWithAuth<CompleteAuthEntityWithIdentities>

// PRIVATE API
export type CompleteAuthEntityWithIdentities =
MakeAuthEntityWithIdentities<{= authIdentityEntityName =}>

// PRIVATE API
export type AuthEntityWithIdentities = {= authEntityName =} & {
{= identitiesFieldOnAuthEntityName =}: {= authIdentityEntityName =}[]
/**
* User entity with all of the auth related data that's needed for the user facing
* helper functions like `getUsername` and `getEmail`.
*/
export type UserEntityWithAuth = MakeUserEntityWithAuth<
MakeAuthEntityWithIdentities<
// It's constructed like the Complete* types, but only with the fields needed
// for the user facing functions.
Pick<{= authIdentityEntityName =}, 'providerName' | 'providerUserId'>
>
>

type MakeUserEntityWithAuth<AuthType> = {= userEntityName =} & {
{= authFieldOnUserEntityName =}: AuthType | null
}

type MakeAuthEntityWithIdentities<IdentityType> = {= authEntityName =} & {
{= identitiesFieldOnAuthEntityName =}: IdentityType[]
}

// PRIVATE API
export function createAuthUserData(user: UserEntityWithAuth): AuthUserData {
export function createAuthUserData(user: CompleteUserEntityWithAuth): AuthUserData {
const { {= authFieldOnUserEntityName =}, ...rest } = user
if (!{= authFieldOnUserEntityName =}) {
throw new Error(`🐝 Error: trying to create a user without auth data.
Expand Down Expand Up @@ -94,7 +113,7 @@ This should never happen, but it did which means there is a bug in the code.`)
}

function getProviderInfo<PN extends ProviderName>(
auth: AuthEntityWithIdentities,
auth: CompleteAuthEntityWithIdentities,
providerName: PN
):
| UserFacingProviderData<PN>
Expand All @@ -112,7 +131,7 @@ function getProviderInfo<PN extends ProviderName>(
}

function getIdentity(
auth: AuthEntityWithIdentities,
auth: CompleteAuthEntityWithIdentities,
providerName: ProviderName
): {= authIdentityEntityName =} | null {
return auth.{= identitiesFieldOnAuthEntityName =}.find((i) => i.providerName === providerName) ?? null
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion waspc/examples/crud-testing/main.wasp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
app crudTesting {
wasp: {
version: "^0.13.0"
version: "^0.14.0"
},
head: [
"<link rel=\"stylesheet\" href=\"https://unpkg.com/mvp.css@1.12/mvp.css\">"
Expand Down
37 changes: 23 additions & 14 deletions waspc/headless-test/examples/todoApp/src/client/Todo.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Link } from 'wasp/client/router'
import { type Task } from 'wasp/entities'

import {
useAction,
Expand All @@ -13,12 +12,15 @@ import {
} from 'wasp/client/operations'

import React, { useState, FormEventHandler, ChangeEventHandler } from 'react'
import { getEmail } from 'wasp/auth'

type NonEmptyArray<T> = [T, ...T[]]

type TaskWithUser = Awaited<ReturnType<typeof getTasks>>[number]

export function areThereAnyTasks(
tasks: Task[] | undefined
): tasks is NonEmptyArray<Task> {
tasks: TaskWithUser[] | undefined
): tasks is NonEmptyArray<TaskWithUser> {
return !!(tasks && tasks.length > 0)
}

Expand Down Expand Up @@ -55,7 +57,7 @@ const Todo = () => {
)
}

const Footer = ({ tasks }: { tasks: NonEmptyArray<Task> }) => {
const Footer = ({ tasks }: { tasks: NonEmptyArray<TaskWithUser> }) => {
const numCompletedTasks = tasks.filter((t) => t.isDone).length
const numUncompletedTasks = tasks.filter((t) => !t.isDone).length

Expand Down Expand Up @@ -83,7 +85,7 @@ const Footer = ({ tasks }: { tasks: NonEmptyArray<Task> }) => {
)
}

const Tasks = ({ tasks }: { tasks: NonEmptyArray<Task> }) => {
const Tasks = ({ tasks }: { tasks: NonEmptyArray<TaskWithUser> }) => {
return (
<div>
<table className="border-separate border-spacing-2">
Expand All @@ -97,9 +99,9 @@ const Tasks = ({ tasks }: { tasks: NonEmptyArray<Task> }) => {
)
}

type UpdateTaskIsDonePayload = Pick<Task, 'id' | 'isDone'>
type UpdateTaskIsDonePayload = Pick<TaskWithUser, 'id' | 'isDone'>

const TaskView = ({ task }: { task: Task }) => {
const TaskView = ({ task }: { task: TaskWithUser }) => {
const updateTaskIsDoneOptimistically = useAction(updateTaskIsDone, {
optimisticUpdates: [
{
Expand All @@ -114,7 +116,7 @@ const TaskView = ({ task }: { task: Task }) => {
)
}
},
} as OptimisticUpdateDefinition<UpdateTaskIsDonePayload, Task[]>,
} as OptimisticUpdateDefinition<UpdateTaskIsDonePayload, TaskWithUser[]>,
],
})
const handleTaskIsDoneChange: ChangeEventHandler<HTMLInputElement> = async (
Expand All @@ -130,6 +132,8 @@ const TaskView = ({ task }: { task: Task }) => {
}
}

const email = getEmail(task.user)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the helper in the headless tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the getEmail helper in headless tests app to test it in headless tests :)


return (
<tr>
<td>
Expand All @@ -143,7 +147,7 @@ const TaskView = ({ task }: { task: Task }) => {
</td>
<td>
<Link to="/task/:id" params={{ id: task.id }}>
{task.description}
{task.description} {email && `by ${email}`}
</Link>
</td>
</tr>
Expand All @@ -158,21 +162,26 @@ const NewTaskForm = () => {
{
getQuerySpecifier: () => [getTasks],
updateQuery: (newTask, oldTasks) => {
const newTaskWithUser = {
...newTask,
user: {},
} as TaskWithUser

if (oldTasks === undefined) {
// cache is empty
return [newTask as Task]
return [newTaskWithUser]
} else {
return [...oldTasks, newTask as Task]
return [...oldTasks, newTaskWithUser]
}
},
} as OptimisticUpdateDefinition<
Pick<Task, 'isDone' | 'description'>,
Task[]
Pick<TaskWithUser, 'isDone' | 'description'>,
TaskWithUser[]
>,
],
})

const createNewTask = async (description: Task['description']) => {
const createNewTask = async (description: TaskWithUser['description']) => {
const task = { isDone: false, description }
await createTaskFn(task)
}
Expand Down
Loading
Loading