Last active
March 22, 2022 20:10
-
-
Save DiesIrae/67182f7c6547500a99e5982b1b3c4eba to your computer and use it in GitHub Desktop.
Abstract Cascade Delete Hook for Keystone 6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// "@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