User HasOne Guest
Every User automatically creates a Guest Each User can have at most one Guest (unique index on user_id foreign key) Sync association types
Git Log
commit b32af276ce0bd9aa8f9c2257b20299dc8cdc2f1a
Author: Daniel Nelson <844258+daniel-nelson@users.noreply.github.com>
Date: Sat Nov 8 10:48:20 2025 -0600
User HasOne Guest
Every User automatically creates a Guest
Each User can have at most one Guest (unique index on user_id foreign key)
Sync association types
Run migration:
```console
yarn psy db:migrate
```
Run model specs:
```console
yarn uspec spec/unit/models
```
If migrations had already been run, after adding
a new association, one could run `sync` instead
(`db:migrate` runs `sync` implicitly):
```console
yarn psy sync
```
Diff from 8cd7e10
diff --git a/api/spec/unit/models/User.spec.ts b/api/spec/unit/models/User.spec.ts
index 06dac32..3bdfe80 100644
--- a/api/spec/unit/models/User.spec.ts
+++ b/api/spec/unit/models/User.spec.ts
@@ -1,3 +1,14 @@
+import Guest from '@models/Guest.js'
+import createUser from '@spec/factories/UserFactory.js'
+
describe('User', () => {
- it.todo('add a test here to get started building User')
+ context('upon creation', () => {
+ it('creates a guest for this user and brings it into scope on the newly created user', async () => {
+ const user = await createUser()
+
+ expect(user.guest instanceof Guest).toBe(true)
+ const guest = await user.associationQuery('guest').first()
+ expect(guest instanceof Guest).toBe(true)
+ })
+ })
})
diff --git a/api/src/app/models/User.ts b/api/src/app/models/User.ts
index 0c415a7..7de8b28 100644
--- a/api/src/app/models/User.ts
+++ b/api/src/app/models/User.ts
@@ -1,6 +1,7 @@
+import ApplicationModel from '@models/ApplicationModel.js'
+import Guest from '@models/Guest.js'
import { Decorators } from '@rvoh/dream'
import { DreamColumn } from '@rvoh/dream/types'
-import ApplicationModel from '@models/ApplicationModel.js'
const deco = new Decorators<typeof User>()
@@ -13,4 +14,12 @@ export default class User extends ApplicationModel {
public email: DreamColumn<User, 'email'>
public createdAt: DreamColumn<User, 'createdAt'>
public updatedAt: DreamColumn<User, 'updatedAt'>
+
+ @deco.AfterCreate()
+ public async createGuest(this: User) {
+ this.guest = await this.createAssociation('guest')
+ }
+
+ @deco.HasOne('Guest')
+ public guest: Guest
}
diff --git a/api/src/db/migrations/1762620340186-create-guest.ts b/api/src/db/migrations/1762620340186-create-guest.ts
index 3edfe7e1..2229e08 100644
--- a/api/src/db/migrations/1762620340186-create-guest.ts
+++ b/api/src/db/migrations/1762620340186-create-guest.ts
@@ -1,24 +1,20 @@
-import { Kysely, sql } from 'kysely'
+import { Kysely } from 'kysely'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('guests')
.addColumn('id', 'bigserial', col => col.primaryKey())
- .addColumn('user_id', 'bigint', col => col.references('users.id').onDelete('restrict').notNull())
+ .addColumn('user_id', 'bigint', col => col.references('users.id').onDelete('restrict').notNull().unique())
.addColumn('created_at', 'timestamp', col => col.notNull())
.addColumn('updated_at', 'timestamp', col => col.notNull())
.execute()
- await db.schema
- .createIndex('guests_user_id')
- .on('guests')
- .column('user_id')
- .execute()
+ await db.schema.createIndex('guests_user_id').on('guests').column('user_id').execute()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export async function down(db: Kysely<any>): Promise<void> {
await db.schema.dropIndex('guests_user_id').execute()
await db.schema.dropTable('guests').execute()
-}
\ No newline at end of file
+}