Skip to main content

SoftDelete

Commit Message

SoftDelete

```console
pnpm psy sync
pnpm uspec spec/unit/models/Place.spec.ts
pnpm uspec
```

SoftDelete works with `deletedAt`, which current generators include by default. If adding after the fact, generate a migration with an optional `deletedAt` datetime:

```console
pnpm psy g:migration add-deleted-at-to-rooms deleted_at:datetime:optional
```

To cascade deletion (regular or soft), add `dependent: destroy` to the relevant association declarations.

Changes

diff --git a/api/spec/unit/models/Place.spec.ts b/api/spec/unit/models/Place.spec.ts
index c4f54f5..6185ecc 100644
--- a/api/spec/unit/models/Place.spec.ts
+++ b/api/spec/unit/models/Place.spec.ts
@@ -1,7 +1,12 @@
+import HostPlace from '@models/HostPlace.js'
+import LocalizedText from '@models/LocalizedText.js'
+import Place from '@models/Place.js'
+import Room from '@models/Room.js'
import createHost from '@spec/factories/HostFactory.js'
import createHostPlace from '@spec/factories/HostPlaceFactory.js'
import createLocalizedText from '@spec/factories/LocalizedTextFactory.js'
import createPlace from '@spec/factories/PlaceFactory.js'
+import createKitchen from '@spec/factories/Room/KitchenFactory.js'

describe('Place', () => {
it('has many Hosts (through hostPlaces)', async () => {
@@ -38,4 +43,42 @@ describe('Place', () => {

expect(place.currentLocalizedText).toMatchDreamModel(esLocalizedText)
})
+
+ context('upon destruction', () => {
+ it('soft-deletes associated HostPlaces, Rooms, and LocalizedTexts', async () => {
+ const place = await createPlace()
+ const hostPlace = await createHostPlace({ place })
+ const room = await createKitchen({ place })
+ const placeText = await place.associationQuery('localizedTexts').firstOrFail()
+ const roomText = await room.associationQuery('localizedTexts').firstOrFail()
+
+ expect(await Place.where({ id: place.id }).exists()).toBe(true)
+ expect(await HostPlace.where({ id: hostPlace.id }).exists()).toBe(true)
+ expect(await Room.where({ id: room.id }).exists()).toBe(true)
+ expect(await LocalizedText.where({ id: placeText.id }).exists()).toBe(true)
+ expect(await LocalizedText.where({ id: roomText.id }).exists()).toBe(true)
+
+ await place.destroy()
+
+ const placeQuery = Place.where({ id: place.id })
+ expect(await placeQuery.exists()).toBe(false)
+ expect(await placeQuery.removeAllDefaultScopes().exists()).toBe(true)
+
+ const hostPlaceQuery = HostPlace.where({ id: hostPlace.id })
+ expect(await hostPlaceQuery.exists()).toBe(false)
+ expect(await hostPlaceQuery.removeAllDefaultScopes().exists()).toBe(true)
+
+ const roomQuery = Room.where({ id: room.id })
+ expect(await roomQuery.exists()).toBe(false)
+ expect(await roomQuery.removeAllDefaultScopes().exists()).toBe(true)
+
+ const placeTextQuery = LocalizedText.where({ id: placeText.id })
+ expect(await placeTextQuery.exists()).toBe(false)
+ expect(await placeTextQuery.removeAllDefaultScopes().exists()).toBe(true)
+
+ const roomTextQuery = LocalizedText.where({ id: roomText.id })
+ expect(await roomTextQuery.exists()).toBe(false)
+ expect(await roomTextQuery.removeAllDefaultScopes().exists()).toBe(true)
+ })
+ })
})