chore: improved validations

This commit is contained in:
nostrdev-com 2025-04-11 11:56:56 +03:00
parent 613c298410
commit 6526935620
5 changed files with 180 additions and 125 deletions

@ -12,6 +12,7 @@
"Cachaça",
"Caturra",
"Coren",
"EAN",
"espadín",
"Genever",
"Jägermeister",
@ -42,6 +43,7 @@
"Robusta",
"RRP",
"screwcap",
"SKU",
"Soju",
"Sokujō",
"Solera",
@ -50,6 +52,7 @@
"Tequilana",
"tobalá",
"Typica",
"UPC",
"Verte",
"VSOP",
"Yamahai",

@ -4,3 +4,4 @@ export * from './review'
export * from './wine'
export * from './spirit'
export * from './product'
export * from './validations'

@ -3,23 +3,37 @@ import {
Ingredient,
SpiritType,
SpiritVolume,
VintageOptions,
SpiritCharacteristics
} from '../../types'
import { isObject, spiritVariantMap, spiritCharacteristicsMap } from '../'
import {
vintageValidation,
productCodeEANvalidation,
productCodeUPCvalidation,
productCodeSKUvalidation,
countryValidation,
nameValidation,
typeValidation,
producerIdValidation,
volumeValidation,
alcoholValidation,
RRPamountValidation,
RRPcurrencyValidation,
descriptionValidation,
urlValidation,
imageValidation
} from './'
export const spiritValidation = (data: unknown): Joi.ValidationResult =>
Joi.object({
productCodeEAN: Joi.string().allow('').required(),
productCodeUPC: Joi.string().allow('').required(),
productCodeSKU: Joi.string().allow('').required(),
country: Joi.string().length(2),
productCodeEAN: productCodeEANvalidation,
productCodeUPC: productCodeUPCvalidation,
productCodeSKU: productCodeSKUvalidation,
country: countryValidation,
region: Joi.string(),
name: Joi.string().required(),
producerId: Joi.string().length(24).required(),
type: Joi.string()
.valid(...Object.values(SpiritType))
.required(),
name: nameValidation,
producerId: producerIdValidation,
type: typeValidation(SpiritType),
variant: Joi.alternatives().try(
Joi.string().custom((variant, helper) => {
// return if no value
@ -267,23 +281,15 @@ export const spiritValidation = (data: unknown): Joi.ValidationResult =>
})
)
.required(),
ingredients: Joi.array()
.items(Joi.string().valid(...Object.values(Ingredient)))
.required(),
volume: Joi.string()
.valid(...Object.values(SpiritVolume))
.required(),
alcohol: Joi.number().min(0).max(0.99).required(),
vintage: Joi.alternatives()
.try(
Joi.string().valid(...Object.values(VintageOptions)),
Joi.number().min(1700).max(new Date().getFullYear())
)
.required(),
RRPamount: Joi.number().required(),
RRPcurrency: Joi.string().length(3).required(),
description: Joi.string().required(),
url: Joi.string(),
image: Joi.string()
volume: volumeValidation(SpiritVolume),
alcohol: alcoholValidation,
vintage: vintageValidation,
RRPamount: RRPamountValidation,
RRPcurrency: RRPcurrencyValidation,
description: descriptionValidation,
url: urlValidation,
image: imageValidation
}).validate(data)

@ -0,0 +1,38 @@
import Joi from 'joi'
import { VintageOptions } from '../../types'
export const vintageValidation = Joi.alternatives()
.try(
Joi.string().valid(...Object.values(VintageOptions)),
Joi.number().min(1700).max(new Date().getFullYear())
)
.required()
export const productCodeEANvalidation = Joi.string().allow('').required()
export const productCodeUPCvalidation = Joi.string().allow('').required()
export const productCodeSKUvalidation = Joi.string().allow('').required()
export const countryValidation = Joi.string().length(2)
export const nameValidation = Joi.string().required()
export const typeValidation = (typeEnum: { [key: string]: string }) =>
Joi.string()
.valid(...Object.values(typeEnum))
.required()
export const producerIdValidation = Joi.string().length(24).required()
export const volumeValidation = (volumeEnum: { [key: string]: string }) =>
Joi.string()
.valid(...Object.values(volumeEnum))
.required()
export const alcoholValidation = Joi.number().min(0).max(0.99).required()
export const RRPamountValidation = Joi.number().required()
export const RRPcurrencyValidation = Joi.string().length(3).required()
export const descriptionValidation = Joi.string().required()
export const urlValidation = Joi.string()
export const imageValidation = Joi.string()

@ -1,7 +1,6 @@
import Joi from 'joi'
import {
WineType,
VintageOptions,
BottleClosure,
Viticulture,
WineRegion,
@ -13,90 +12,30 @@ import {
RedWineCharacteristic
} from '../../types'
import { wineRegionsMap, isObject } from '../'
import {
vintageValidation,
productCodeEANvalidation,
productCodeUPCvalidation,
productCodeSKUvalidation,
countryValidation,
nameValidation,
typeValidation,
producerIdValidation,
volumeValidation,
alcoholValidation,
RRPamountValidation,
RRPcurrencyValidation,
descriptionValidation,
urlValidation,
imageValidation
} from './'
export const wineValidation = (data: unknown): Joi.ValidationResult =>
Joi.object({
productCodeEAN: Joi.string().allow('').required(),
productCodeUPC: Joi.string().allow('').required(),
productCodeSKU: Joi.string().allow('').required(),
type: Joi.string()
.valid(...Object.values(WineType))
.required(),
style: Joi.string()
.valid(...Object.values(WineStyle))
.required(),
characteristics: Joi.array()
.items(Joi.string())
.custom((value: string[], helper) => {
// return if no value
if (!value) {
return value
}
// return if no state ancestors
if (!helper.state.ancestors) {
return value
}
const wineType: WineType = helper.state.ancestors[0].type
// return if no wineType
if (!wineType) {
return value
}
let options: string[] = []
switch (wineType) {
case WineType.White:
{
options = Object.values(WhiteWineCharacteristic)
}
break
case WineType.Amber:
{
options = Object.values(AmberWineCharacteristic)
}
break
case WineType.Rose:
{
options = Object.values(RoseWineCharacteristic)
}
break
case WineType.Red:
{
options = Object.values(RedWineCharacteristic)
}
break
default:
break
}
if (!options.length) {
return helper.message({
custom: Joi.expression(
`no characteristics found for provided type of wine`
)
})
}
for (const characteristic of value) {
if (!options.includes(characteristic)) {
return helper.message({
custom: Joi.expression(
`"${characteristic}" is not a valid characteristic for "${wineType}" wine. Valid options are [${options.map((option) => `"${option}"`).join(', ')}]`
)
})
}
}
return value
}),
country: Joi.string().length(2),
productCodeEAN: productCodeEANvalidation,
productCodeUPC: productCodeUPCvalidation,
productCodeSKU: productCodeSKUvalidation,
country: countryValidation,
// TODO: improve types
region: Joi.alternatives()
.try(
@ -356,19 +295,87 @@ export const wineValidation = (data: unknown): Joi.ValidationResult =>
)
.allow('')
.required(),
name: Joi.string().required(),
producerId: Joi.string().length(24).required(),
name: nameValidation,
producerId: producerIdValidation,
type: typeValidation(WineType),
style: Joi.string()
.valid(...Object.values(WineStyle))
.required(),
characteristics: Joi.array()
.items(Joi.string())
.custom((value: string[], helper) => {
// return if no value
if (!value) {
return value
}
// return if no state ancestors
if (!helper.state.ancestors) {
return value
}
const wineType: WineType = helper.state.ancestors[0].type
// return if no wineType
if (!wineType) {
return value
}
let options: string[] = []
switch (wineType) {
case WineType.White:
{
options = Object.values(WhiteWineCharacteristic)
}
break
case WineType.Amber:
{
options = Object.values(AmberWineCharacteristic)
}
break
case WineType.Rose:
{
options = Object.values(RoseWineCharacteristic)
}
break
case WineType.Red:
{
options = Object.values(RedWineCharacteristic)
}
break
default:
break
}
if (!options.length) {
return helper.message({
custom: Joi.expression(
`no characteristics found for provided type of wine`
)
})
}
for (const characteristic of value) {
if (!options.includes(characteristic)) {
return helper.message({
custom: Joi.expression(
`"${characteristic}" is not a valid characteristic for "${wineType}" wine. Valid options are [${options.map((option) => `"${option}"`).join(', ')}]`
)
})
}
}
return value
}),
varietal: Joi.string().required(),
vintage: Joi.alternatives()
.try(
Joi.string().valid(...Object.values(VintageOptions)),
Joi.number().min(1700).max(new Date().getFullYear())
)
.required(),
volume: Joi.string()
.valid(...Object.values(WineVolume))
.required(),
alcohol: Joi.number().min(0).max(0.99).required(),
volume: volumeValidation(WineVolume),
alcohol: alcoholValidation,
vintage: vintageValidation,
viticulture: Joi.string()
.valid(...Object.values(Viticulture))
.required(),
@ -379,9 +386,9 @@ export const wineValidation = (data: unknown): Joi.ValidationResult =>
closure: Joi.string()
.valid(...Object.values(BottleClosure))
.required(),
RRPamount: Joi.number().required(),
RRPcurrency: Joi.string().length(3).required(),
description: Joi.string().required(),
url: Joi.string(),
image: Joi.string()
RRPamount: RRPamountValidation,
RRPcurrency: RRPcurrencyValidation,
description: descriptionValidation,
url: urlValidation,
image: imageValidation
}).validate(data)