feat(wine): implemented characteristics validation

This commit is contained in:
nostrdev-com 2025-04-09 15:29:31 +03:00
parent f44aa5673e
commit 5e01078d7a
3 changed files with 136 additions and 16 deletions
src
models
types
utils/validation

@ -6,7 +6,12 @@ import {
VintageOptions,
StandardDrinks,
WineRegion,
WineVolume
WineVolume,
WineStyle,
WhiteWineCharacteristic,
AmberWineCharacteristic,
RoseWineCharacteristic,
RedWineCharacteristic
} from '../types'
import { Alpha2Code } from 'i18n-iso-countries'
import { CurrencyCode } from 'currency-codes-ts/dist/types'
@ -17,8 +22,13 @@ export class Wine {
public productCodeUPC: string, // Product Code (https://en.wikipedia.org/wiki/Universal_Product_Code)
public productCodeSKU: string, // Stock keeping unit (https://en.wikipedia.org/wiki/Stock_keeping_unit)
public type: WineType, // numerical rating, e.g., 1-100
public style: string, // bubbles+fizz, table, dessert, fortified, vermouth
public characteristic: string, // light aromatic, textural, fruit forward, structural & savoury, powerful
public style: WineStyle, // bubbles+fizz, table, dessert, fortified, vermouth
public characteristics: (
| WhiteWineCharacteristic
| AmberWineCharacteristic
| RoseWineCharacteristic
| RedWineCharacteristic
)[], // light aromatic, textural, fruit forward, structural & savoury, powerful
public country: Alpha2Code, // two-letter country codes defined in ISO 3166-1 (https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
public region: WineRegion, // appellation, village, sub-region, vineyard
public name: string, // label

@ -1,20 +1,53 @@
export enum WineType {
White = 'white',
Amber = 'amber',
Rose = 'rose',
Red = 'red'
White = 'White',
Amber = 'Amber',
Rose = 'Rose',
Red = 'Red'
}
export enum WhiteWineCharacteristic {
LightAromatic = 'Light Aromatic',
TexturalAndSavory = 'Textural and Savory',
RichAndFruitForward = 'Rich and Fruit Forward'
}
export enum AmberWineCharacteristic {
TexturalAndAromatic = 'Textural and Aromatic',
StructuralAndSavory = 'Structural and Savory',
PowerAndPresence = 'Power and Presence'
}
export enum RoseWineCharacteristic {
LightAndFruitForward = 'Light and Fruit Forward',
TexturalAndSavory = 'Textural and Savory',
StructuralAndAromatic = 'Structural and Aromatic'
}
export enum RedWineCharacteristic {
LightAndFruitForward = 'Light and Fruit Forward',
StructuralAndAromatic = 'Structural and Aromatic',
TexturalAndSavory = 'Textural and Savory',
PowerAndPresence = 'Power and Presence'
}
export enum WineStyle {
BubblesAndFizz = 'Bubbles + Fizz',
Table = 'Table',
Dessert = 'Dessert',
Fortified = 'Fortified',
Vermouth = 'Vermouth'
}
export enum Viticulture {
Biodynamic = 'biodynamic',
Organic = 'organic',
Conventional = 'conventional'
Biodynamic = 'Biodynamic',
Organic = 'Organic',
Conventional = 'Conventional'
}
export enum BottleClosure {
Cork = 'cork',
CrownSeal = 'crown-seal',
Screwcap = 'screwcap'
Cork = 'Cork',
CrownSeal = 'Crown-seal',
Screwcap = 'Screwcap'
}
export interface WineRegion {

@ -5,7 +5,12 @@ import {
BottleClosure,
Viticulture,
WineRegion,
WineVolume
WineVolume,
WineStyle,
WhiteWineCharacteristic,
AmberWineCharacteristic,
RoseWineCharacteristic,
RedWineCharacteristic
} from '../../types'
import { wineRegionsMap, isObject } from '../'
@ -17,8 +22,80 @@ export const wineValidation = (data: unknown): Joi.ValidationResult =>
type: Joi.string()
.valid(...Object.values(WineType))
.required(),
style: Joi.string().required(),
characteristic: Joi.string().required(),
style: Joi.string()
.valid(...Object.values(WineStyle))
.required(),
characteristic: 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),
region: Joi.alternatives()
.try(