Skip to content

Instantly share code, notes, and snippets.

@DiesIrae
Last active March 22, 2022 20:10
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save DiesIrae/67182f7c6547500a99e5982b1b3c4eba to your computer and use it in GitHub Desktop.
Save DiesIrae/67182f7c6547500a99e5982b1b3c4eba to your computer and use it in GitHub Desktop.
Abstract Cascade Delete Hook for Keystone 6
// "@keystone-next/types": "^22.0.0",
// "@keystone-next/keystone": "^22.0.0"
// The actual lib
// utils/keystone/cascadeDelete.ts
import { ListHooks } from "@keystone-next/types"
import { BaseGeneratedListTypes } from "@keystone-next/types/src/utils"
type BeforeDeleteType = Exclude<
ListHooks<BaseGeneratedListTypes>["beforeDelete"],
undefined
>
type BeforeDeleteParameter = Parameters<BeforeDeleteType>[0] & {
fieldPath: string
}
type HookConfig = {
ref: string
}
type Id = NonNullable<any>
type IdObject = { id: Id }
export const deleteOneBeforeDeleteHook =
({ ref }: HookConfig) =>
async ({ existingItem, context, fieldPath }: BeforeDeleteParameter) => {
const idPath = fieldPath + "Id"
const id = (existingItem as { [idPath: string]: Id })[idPath]
if (id) await context.lists[ref].deleteOne({ id })
}
export const deleteManyBeforeDeleteHook =
({ ref }: HookConfig) =>
async ({
existingItem,
context,
fieldPath,
listKey,
}: BeforeDeleteParameter) => {
const { id } = existingItem as IdObject
const parentWithChildren = await context.lists[listKey].findOne({
where: { id },
query: `${fieldPath} { id }`,
})
const children = parentWithChildren[fieldPath] as [IdObject] | undefined
const ids = (children || []).map(({ id }) => id)
await context.lists[ref].deleteMany({ ids })
}
// Usage examples
// lists/test/oneToMany.ts
import { list } from "@keystone-next/keystone/schema"
import { relationship, text } from "@keystone-next/fields"
import { deleteManyBeforeDeleteHook } from "../../utils/keystone/cascadeDelete"
const TestHorseOTM = list({
fields: {
name: text(),
},
})
const TestRiderOTM = list({
fields: {
name: text(),
horses: relationship({
ref: "TestHorseOTM",
many: true,
hooks: {
beforeDelete: deleteManyBeforeDeleteHook({ ref: "TestHorseOTM" }),
},
}),
},
})
export const testOneToManyList = { TestRiderOTM, TestHorseOTM }
// lists/test/oneToOne.ts
import { list } from "@keystone-next/keystone/schema"
import { relationship, text } from "@keystone-next/fields"
import { deleteOneBeforeDeleteHook } from "../../utils/keystone/cascadeDelete"
const TestRiderOTO = list({
fields: {
name: text(),
},
})
const TestHorseOTO = list({
fields: {
name: text(),
rider: relationship({
ref: "TestRiderOTO",
hooks: {
beforeDelete: deleteOneBeforeDeleteHook({ ref: "TestRiderOTO" }),
},
}),
},
})
export const testOneToOneList = { TestRiderOTO, TestHorseOTO }
// Tests
// cascadeDeleteOneToOne.test.ts
import { setupTestRunner } from "@keystone-next/testing"
import config from "./../keystone"
const runner = setupTestRunner({ config })
describe("One to One", () => {
it(
"Delete",
runner(async ({ context }) => {
const createRider = { name: "Roger" }
const createHorse = {
name: "Jumpy",
rider: { create: createRider },
}
const horse = await context.lists.TestHorseOTO.createOne({
data: createHorse,
query: "id rider { name }",
})
expect(horse).toMatchInlineSnapshot(
{
id: expect.any(String),
},
`
Object {
"id": Any<String>,
"rider": Object {
"name": "Roger",
},
}
`,
)
await context.lists.TestHorseOTO.deleteOne({ id: horse.id })
expect(await context.lists.TestRiderOTO.findMany()).toEqual([])
}),
)
})
// cascadeDeleteOneToMany.test.ts
import { setupTestRunner } from "@keystone-next/testing"
import config from "./../keystone"
const runner = setupTestRunner({ config })
describe("One to Many", () => {
it(
"Delete",
runner(async ({ context }) => {
const createHorses = [{ name: "Jumpy" }]
const createRider = {
name: "Roger",
horses: { create: createHorses },
}
const rider = await context.lists.TestRiderOTM.createOne({
data: createRider,
query: "id horses { name }",
})
expect(rider).toMatchInlineSnapshot(
{
id: expect.any(String),
},
`
Object {
"horses": Array [
Object {
"name": "Jumpy",
},
],
"id": Any<String>,
}
`,
)
await context.lists.TestRiderOTM.deleteOne({ id: rider.id })
expect(await context.lists.TestHorseOTM.findMany()).toEqual([])
}),
)
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment