-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Consolidate validation code into a single location (#6139)
- Loading branch information
Showing
5 changed files
with
127 additions
and
97 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@keystone-next/keystone': patch | ||
--- | ||
|
||
Refactored mutation validation handling into a single location. |
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import { ValidationFailureError } from '../graphql-errors'; | ||
import { InitialisedList } from '../types-for-lists'; | ||
import { promiseAllRejectWithAllErrors } from '../utils'; | ||
|
||
type ValidationError = { msg: string; data: {}; internalData: {} }; | ||
|
||
type AddValidationError = (msg: string, data?: {}, internalData?: {}) => void; | ||
|
||
export async function validationHook( | ||
listKey: string, | ||
operation: 'create' | 'update' | 'delete', | ||
originalInput: Record<string, string> | undefined, | ||
validationHook: (addValidationError: AddValidationError) => void | Promise<void> | ||
) { | ||
const errors: ValidationError[] = []; | ||
|
||
await validationHook((msg, data = {}, internalData = {}) => { | ||
errors.push({ msg, data, internalData }); | ||
}); | ||
|
||
if (errors.length) { | ||
throw new ValidationFailureError({ | ||
data: { | ||
messages: errors.map(e => e.msg), | ||
errors: errors.map(e => e.data), | ||
listKey, | ||
operation, | ||
}, | ||
internalData: { errors: errors.map(e => e.internalData), data: originalInput }, | ||
}); | ||
} | ||
} | ||
|
||
type UpdateCreateHookArgs = Parameters< | ||
Exclude<InitialisedList['hooks']['validateInput'], undefined> | ||
>[0]; | ||
export async function validateUpdateCreate({ | ||
list, | ||
hookArgs, | ||
}: { | ||
list: InitialisedList; | ||
hookArgs: Omit<UpdateCreateHookArgs, 'addValidationError'>; | ||
}) { | ||
const { operation, resolvedData, originalInput } = hookArgs; | ||
// Check isRequired | ||
await validationHook(list.listKey, operation, originalInput, addValidationError => { | ||
for (const [fieldKey, field] of Object.entries(list.fields)) { | ||
// yes, this is a massive hack, it's just to make image and file fields work well enough | ||
let val = resolvedData[fieldKey]; | ||
if (field.dbField.kind === 'multi') { | ||
if (Object.values(resolvedData[fieldKey]).every(x => x === null)) { | ||
val = null; | ||
} | ||
if (Object.values(resolvedData[fieldKey]).every(x => x === undefined)) { | ||
val = undefined; | ||
} | ||
} | ||
if ( | ||
field.__legacy?.isRequired && | ||
((operation === 'create' && val == null) || (operation === 'update' && val === null)) | ||
) { | ||
addValidationError( | ||
`Required field "${fieldKey}" is null or undefined.`, | ||
{ resolvedData, operation, originalInput }, | ||
{} | ||
); | ||
} | ||
} | ||
}); | ||
|
||
// Field validation hooks | ||
await validationHook(list.listKey, operation, originalInput, async addValidationError => { | ||
await promiseAllRejectWithAllErrors( | ||
Object.entries(list.fields).map(async ([fieldPath, field]) => { | ||
// @ts-ignore | ||
await field.hooks.validateInput?.({ ...hookArgs, addValidationError, fieldPath }); | ||
}) | ||
); | ||
}); | ||
|
||
// List validation hooks | ||
await validationHook(list.listKey, operation, originalInput, async addValidationError => { | ||
// @ts-ignore | ||
await list.hooks.validateInput?.({ ...hookArgs, addValidationError }); | ||
}); | ||
} | ||
|
||
type DeleteHookArgs = Parameters<Exclude<InitialisedList['hooks']['validateDelete'], undefined>>[0]; | ||
export async function validateDelete({ | ||
list, | ||
hookArgs, | ||
}: { | ||
list: InitialisedList; | ||
hookArgs: Omit<DeleteHookArgs, 'addValidationError'>; | ||
}) { | ||
// Field validation | ||
await validationHook(list.listKey, 'delete', undefined, async addValidationError => { | ||
await promiseAllRejectWithAllErrors( | ||
Object.entries(list.fields).map(async ([fieldPath, field]) => { | ||
await field.hooks.validateDelete?.({ ...hookArgs, addValidationError, fieldPath }); | ||
}) | ||
); | ||
}); | ||
|
||
// List validation | ||
await validationHook(list.listKey, 'delete', undefined, async addValidationError => { | ||
await list.hooks.validateDelete?.({ ...hookArgs, addValidationError }); | ||
}); | ||
} |
587a8d0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs: