-
-
Save christopherliu/903bdc2175497f5aa1b465e205ae506c 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-6/core": "^1.0.1", | |
// ******************************************************************* | |
// Adding support for the General Availability release of Keystone 6. | |
// Only updated cascadeDelete.ts. Haven't updated tests. | |
// ******************************************************************* | |
// The actual lib | |
// utils/keystone/cascadeDelete.ts | |
type Id = NonNullable<any>; | |
type IdObject = { id: Id }; | |
type HookConfig = { | |
ref: string | |
}; | |
// TODO this will probably need further revising to work in Keystone 6. | |
// Only deleteMany has been tested in a live environment | |
export const deleteOneBeforeDeleteHook = | |
({ ref }: HookConfig) => | |
async ({ | |
fieldKey, | |
item, | |
context, | |
}) => { | |
const idPath = fieldKey + "Id"; | |
const id = (item as { [idPath: string]: Id })[idPath]; | |
if (id) await context.lists[ref].deleteOne({ id }); | |
}; | |
export const deleteManyBeforeDeleteHook = | |
({ ref }: HookConfig) => | |
async ({ | |
listKey, | |
fieldKey, | |
operation, | |
item, | |
context, | |
}) => { | |
if (operation !== "delete") { | |
return; | |
} | |
const { id } = item as IdObject; | |
const parentWithChildren = await context.query[listKey].findOne({ | |
where: { id }, | |
query: `${fieldKey} { id }`, | |
}); | |
const children = parentWithChildren[fieldKey] as [IdObject] | undefined; | |
await context.query[ref.split(".")[0]].deleteMany({ where: children }); | |
}; | |
// 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: { | |
beforeOperation: 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: { | |
beforeOperation: 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