-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #19 from feathers-plus/validate-context
🎁 Allow validating anything in context
- Loading branch information
Showing
5 changed files
with
257 additions
and
78 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 |
---|---|---|
@@ -1,66 +1,79 @@ | ||
/* eslint comma-dangle: 0, object-shorthand: 0, prefer-arrow-callback: 0*/ /* ES5 code */ | ||
const errors = require('@feathersjs/errors'); | ||
const utils = require('feathers-hooks-common/lib/services'); | ||
const joiErrorsForForms = require('joi-errors-for-forms'); | ||
|
||
// We only directly need the convert option. The others are listed for convenience. | ||
// See defaults at https://hapi.dev/family/joi/api/?v=17.1.0#anyvalidatevalue-options | ||
const joiDefaults = { | ||
abortEarly: true, | ||
allowUnknown: false, | ||
cache: true, | ||
convert: true, | ||
debug: false, | ||
externals: true, | ||
noDefaults: false, | ||
nonEnumerables: false, | ||
presence: 'optional', | ||
skipFunctions: false, | ||
stripUnknown: false | ||
}; | ||
|
||
function setupValidateWithJoi(joiSchema, joiOptions, translator, ifTest) { | ||
if (!['undefined', 'object'].includes(typeof joiOptions)) { | ||
throw new errors.GeneralError('joiOptions must be a valid object.'); | ||
} | ||
|
||
const mergedOptions = Object.assign({}, joiDefaults, joiOptions); | ||
|
||
return async function validateWithJoi(context, next) { | ||
utils.checkContext(context, 'before', ['create', 'update', 'patch'], 'validate-joi'); | ||
const values = utils.getItems(context); | ||
|
||
|
||
try { | ||
const convertedValues = await joiSchema.validateAsync(values, mergedOptions); | ||
|
||
if (mergedOptions.convert === true) { | ||
utils.replaceItems(context, convertedValues); | ||
} | ||
|
||
if (typeof next === 'function') { | ||
return next(null, context); | ||
} | ||
return context; | ||
} catch (error) { | ||
const formErrors = translator(error); | ||
if (formErrors) { | ||
// Hacky, but how else without a custom assert? | ||
const msg = ifTest ? JSON.stringify(formErrors) : 'Invalid data'; | ||
throw new errors.BadRequest(msg, { errors: formErrors }); | ||
} | ||
return formErrors || error; | ||
} | ||
}; | ||
} | ||
|
||
module.exports = { | ||
form: function (joiSchema, joiOptions, translations, ifTest) { | ||
const translator = joiErrorsForForms.form(translations); | ||
return setupValidateWithJoi(joiSchema, joiOptions, translator, ifTest); | ||
}, | ||
mongoose: function (joiSchema, joiOptions, translations, ifTest) { | ||
const translator = joiErrorsForForms.mongoose(translations); | ||
return setupValidateWithJoi(joiSchema, joiOptions, translator, ifTest); | ||
} | ||
}; | ||
/* eslint comma-dangle: 0, object-shorthand: 0, prefer-arrow-callback: 0 */ | ||
const errors = require('@feathersjs/errors'); | ||
const utils = require('feathers-hooks-common/lib/services'); | ||
const joiErrorsForForms = require('joi-errors-for-forms'); | ||
|
||
// We only directly need the convert option. The others are listed for convenience. | ||
// See defaults at https://hapi.dev/family/joi/api/?v=17.1.0#anyvalidatevalue-options | ||
const joiDefaults = { | ||
abortEarly: true, | ||
allowUnknown: false, | ||
cache: true, | ||
convert: true, | ||
debug: false, | ||
externals: true, | ||
noDefaults: false, | ||
nonEnumerables: false, | ||
presence: 'optional', | ||
skipFunctions: false, | ||
stripUnknown: false, | ||
getContext: undefined, | ||
setContext: undefined | ||
}; | ||
|
||
function setupValidateWithJoi(joiSchema, joiOptions, translator, ifTest) { | ||
if (!['undefined', 'object'].includes(typeof joiOptions)) { | ||
throw new errors.GeneralError('joiOptions must be a valid object.'); | ||
} | ||
|
||
const { getContext, setContext, ...mergedOptions } = { ...joiDefaults, ...joiOptions }; | ||
|
||
if ((getContext || setContext) && (!getContext || !setContext)) { | ||
throw new errors.GeneralError('getContext and setContext must be used together'); | ||
} | ||
|
||
return async function validateWithJoi(context, next) { | ||
let values; | ||
if (typeof getContext === 'function') { | ||
values = getContext(context); | ||
} else { | ||
values = utils.getItems(context); | ||
} | ||
|
||
try { | ||
const convertedValues = await joiSchema.validateAsync(values, mergedOptions); | ||
|
||
if (mergedOptions.convert === true) { | ||
if (typeof setContext === 'function') { | ||
setContext(context, convertedValues); | ||
} else { | ||
utils.replaceItems(context, convertedValues); | ||
} | ||
} | ||
|
||
if (typeof next === 'function') { | ||
return next(null, context); | ||
} | ||
return context; | ||
} catch (error) { | ||
const formErrors = translator(error); | ||
if (formErrors) { | ||
// Hacky, but how else without a custom assert? | ||
const msg = ifTest ? JSON.stringify(formErrors) : 'Invalid data'; | ||
throw new errors.BadRequest(msg, { errors: formErrors }); | ||
} | ||
return formErrors || error; | ||
} | ||
}; | ||
} | ||
|
||
module.exports = { | ||
form: function (joiSchema, joiOptions, translations, ifTest) { | ||
const translator = joiErrorsForForms.form(translations); | ||
return setupValidateWithJoi(joiSchema, joiOptions, translator, ifTest); | ||
}, | ||
mongoose: function (joiSchema, joiOptions, translations, ifTest) { | ||
const translator = joiErrorsForForms.mongoose(translations); | ||
return setupValidateWithJoi(joiSchema, joiOptions, translator, ifTest); | ||
} | ||
}; |
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
Oops, something went wrong.