Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ app.listen(3000)
| `@IsTrue()` | 값이 `true` 여야 함 | `@IsTrue() acceptedTerms!: boolean` |
| `@IsFalse()` | 값이 `false` 여야 함 | `@IsFalse() blocked!: boolean` |
| `@OneOf(options: readonly any[])` | 값이 `options` 중 하나여야 함 | `@OneOf(['credit','debit'] as const) method!: 'credit' \| 'debit'` |
| `@ArrayContains(values: any[])` | 배열이 지정된 모든 값을 포함해야 함 | `@ArrayContains([1, 2]) nums!: number[]` |
| `@Enum(enumObj: object, message?)` | 값이 `enumObj`의 멤버여야 함 | `@Enum(UserRole) role!: UserRole` |
| `@Validate(validateFn, message?)` | 커스텀 검증 함수를 사용 | `@Validate(v => typeof v === 'string' && v.includes('@'), 'invalid email') email!: string` |
| `@Regexp(pattern: RegExp, message?)` | 문자열이 주어진 정규식을 만족해야 함 | `@Regexp(/^[0-9]+$/, 'digits only') phone!: string` |
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ Full guide and API reference:
| `@IsTrue()` | Value must be `true`. | `@IsTrue() acceptedTerms!: boolean` |
| `@IsFalse()` | Value must be `false`. | `@IsFalse() blocked!: boolean` |
| `@OneOf(options: readonly any[])` | Value must be one of `options`. | `@OneOf(['credit','debit'] as const) method!: 'credit' \| 'debit'` |
| `@ArrayContains(values: any[])` | Array must contain all specified values. | `@ArrayContains([1, 2]) nums!: number[]` |
| `@Enum(enumObj: object, message?)` | Value must be a member of `enumObj`. | `@Enum(UserRole) role!: UserRole` |
| `@Validate(validateFn, message?)` | Custom validation function. | `@Validate(v => typeof v === 'string' && v.includes('@'), 'invalid email') email!: string` |
| `@Regexp(pattern: RegExp, message?)` | String must match the given regular expression. | `@Regexp(/^[0-9]+$/, 'digits only') phone!: string` |
Expand Down
7 changes: 7 additions & 0 deletions apps/docs/docs/decorators/validators.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ Validates that the input value is one of the specified values.

- **`values`**: The array of allowed values.

### `@ArrayContains(values: any[], message?: string)`

Validates that the array contains all the specified values.

- **`values`**: The values that must be present in the array.
- **`message`** (optional): The error message to display when validation fails. If omitted, a default message will be used.

### `@Enum(enumObj: object, message?: string)`

Validates that the input value matches one of the values in the specified enum object.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ Validiert, dass der Eingabewert einer der angegebenen Werte ist.

- **`values`**: Das Array der zulässigen Werte.

### `@ArrayContains(values: any[], message?: string)`

Validiert, dass das Array alle angegebenen Werte enthält.

- **`values`**: Die Werte, die im Array vorhanden sein müssen.
- **`message`** (optional): Die Fehlermeldung, die angezeigt wird, wenn die Validierung fehlschlägt. Wenn weggelassen, wird eine Standardmeldung verwendet.

### `@Enum(enumObj: object, message?: string)`

Validiert, dass der Eingabewert mit einem der Werte im angegebenen Enum-Objekt übereinstimmt.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ Valide que la valeur d'entrée est l'une des valeurs spécifiées.

- **`values`** : Le tableau des valeurs autorisées.

### `@ArrayContains(values: any[], message?: string)`

Valide que le tableau contient toutes les valeurs spécifiées.

- **`values`**: Les valeurs qui doivent être présentes dans le tableau.
- **`message`** (optionnel) : Le message d'erreur à afficher lorsque la validation échoue. S'il est omis, un message par défaut sera utilisé.

### `@Enum(enumObj: object, message?: string)`

Valide que la valeur d'entrée correspond à l'une des valeurs de l'objet enum spécifié.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ title: 유효성 검사 데코레이터

---

### `@ArrayContains(values: any[], message?: string)`

배열이 지정된 모든 값을 포함하고 있는지 검증합니다.

- **`values`**: 배열에 반드시 포함되어야 하는 값들입니다.
- **`message`** (선택 사항): 검증 실패 시 표시할 메시지. 생략하면 기본 메시지가 사용됩니다.

### `@Enum(enumObj: object, message?: string)`

입력 값이 지정된 열거형 객체의 값 중 하나와 일치하는지 검증합니다. 또한 입력 값을 해당하는 열거형 값으로 자동으로 변환합니다.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ Express-Cargo использует декораторы для валидаци

- **`values`**: Массив допустимых значений.

### `@ArrayContains(values: any[], message?: string)`

Проверяет, что массив содержит все указанные значения.

- **`values`**: Значения, которые должны присутствовать в массиве.
- **`message`** (необязательно): Сообщение об ошибке, которое будет отображаться при сбое валидации. Если опущено, будет использоваться сообщение по умолчанию.

### `@Enum(enumObj: object, message?: string)`

Проверяет, что входное значение соответствует одному из значений в указанном объекте перечисления.
Expand Down
23 changes: 23 additions & 0 deletions apps/example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,29 @@ curl -X POST 'http://localhost:3000/enum' \

---

### @ArrayContains

```typescript
class ArrayContainsExample {
@Body()
@ArrayContains([1, 2])
numbers!: number[]
}

router.post('/array-contains', bindingCargo(ArrayContainsExample), (req, res) => {
const cargo = getCargo<ArrayContainsExample>(req)
res.json(cargo)
})
```

```shell
curl -X POST 'http://localhost:3000/array-contains' \
-H 'Content-Type: application/json' \
-d '{"numbers": [1, 2, 3]}'
```

---

### @Validate

```typescript
Expand Down
12 changes: 12 additions & 0 deletions apps/example/src/routers/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
With,
Without,
Enum,
ArrayContains,
} from 'express-cargo'

const router: Router = express.Router()
Expand Down Expand Up @@ -315,4 +316,15 @@ router.post('/enum', bindingCargo(EnumExample), (req, res) => {
res.json(cargo)
})

class ArrayContainsExample {
@Body()
@ArrayContains([1, 2])
numbers!: number[]
}

router.post('/array-contains', bindingCargo(ArrayContainsExample), (req, res) => {
const cargo = getCargo<ArrayContainsExample>(req)
res.json(cargo)
})

export default router
20 changes: 20 additions & 0 deletions packages/express-cargo/src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,26 @@ export function Without(fieldName: string, message?: cargoErrorMessage): Propert
}
}

/**
* Checks if the array contains all the specified values.
* @param values - The values that must be present in the array.
* @param message - Optional custom error message.
*/
export function ArrayContains(values: any[], message?: cargoErrorMessage): PropertyDecorator {
return (target: Object, propertyKey: string | symbol): void => {
addValidator(
target,
propertyKey,
new ValidatorRule(
propertyKey,
'arrayContains',
(val: any) => Array.isArray(val) && values.every(v => val.includes(v)),
message || `${String(propertyKey)} must contain ${values.join(', ')}`,
),
)
}
}

/**
* Applies validation rules to each element of an array.
* @param args - Validation decorators or functions to apply to each element.
Expand Down
58 changes: 58 additions & 0 deletions packages/express-cargo/tests/validator/arrayContains.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { ArrayContains, Body, CargoFieldError } from '../../src'
import { CargoClassMetadata } from '../../src/metadata'

describe('ArrayContains Validator', () => {
class TestClass {
@Body()
@ArrayContains([1, 2])
numbers!: number[]

@Body()
noValidation!: number[]
}

const classMeta = new CargoClassMetadata(TestClass.prototype)

it('should have arrayContains validator metadata', () => {
const meta = classMeta.getFieldMetadata('numbers')
const rule = meta.getValidators()?.find(v => v.type === 'arrayContains')

expect(rule).toBeDefined()
})

it('should pass validation when array contains all required values', () => {
const meta = classMeta.getFieldMetadata('numbers')
const rule = meta.getValidators()?.find(v => v.type === 'arrayContains')

expect(rule?.validate([1, 2, 3])).toBeNull()
expect(rule?.validate([1, 2])).toBeNull()
expect(rule?.validate([2, 1])).toBeNull()
})

it('should fail validation when array is missing required values', () => {
const meta = classMeta.getFieldMetadata('numbers')
const rule = meta.getValidators()?.find(v => v.type === 'arrayContains')

expect(rule?.validate([1])).toBeInstanceOf(CargoFieldError)
expect(rule?.validate([2])).toBeInstanceOf(CargoFieldError)
expect(rule?.validate([3, 4])).toBeInstanceOf(CargoFieldError)
expect(rule?.validate([])).toBeInstanceOf(CargoFieldError)
})

it('should fail validation when value is not an array', () => {
const meta = classMeta.getFieldMetadata('numbers')
const rule = meta.getValidators()?.find(v => v.type === 'arrayContains')

expect(rule?.validate(1)).toBeInstanceOf(CargoFieldError)
expect(rule?.validate('string')).toBeInstanceOf(CargoFieldError)
expect(rule?.validate(null)).toBeInstanceOf(CargoFieldError)
expect(rule?.validate(undefined)).toBeInstanceOf(CargoFieldError)
})

it('should not have arrayContains validator metadata on undecorated field', () => {
const meta = classMeta.getFieldMetadata('noValidation')
const rule = meta.getValidators()?.find(v => v.type === 'arrayContains')

expect(rule).toBeUndefined()
})
})