Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
26 changes: 26 additions & 0 deletions packages/express-cargo/src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,32 @@ 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): TypedPropertyDecorator<any[]> {
return (target: Object, propertyKey: string | symbol): void => {
addValidator(
target,
propertyKey,
new ValidatorRule(
propertyKey,
'arrayContains',
(value: unknown) => {
if (!Array.isArray(value)) {
return false
}
const valueSet = new Set(value)
return values.every(v => valueSet.has(v))
Comment on lines +461 to +462
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 array element 가 object 일때도 동작하나요???

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 부분 놓치고 있었던 것 같아요. object이면 원하는 방식으로 동작하지 않겠네요. 수정하도록 하겠습니다.

},
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()
})
})