import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# API Validation

In the [API Schema](./configuration/api-schema), we talked about how to define the API Schema, and also mentioned the `validators` field feature could assist each request parameter to check the format. In the chapter we will talk more about the validator's usage and currently how many validators VulcanSQL supports.

There are two ways to apply validators: define them in [API schema](./configuration/api-schema) and use validation filters.

### Define validators in data api schema

The most common way to apply validators is by `validators` property for [dynamic parameters](./sql-syntax#dynamic-parameter), this field in the API Schema is an array type, so you could add the multiple validators on a single parameter like the example below:

```yaml
urlPath: /orders/:order_id
request:
  - fieldName: orders_id
    fieldIn: path
    validators:
      - required
      - uuid
```

In the above example, we restricted the parameter `order_id` to be in `UUID` format and be required. All the requests that aren't fit these requirements will be rejected.

:::info
VulcanSQL sometimes applys some validators automatically, e.g. when a parameter is used in url path, we'd apply `required` validator on it.

:::

#### Set the arguments for the validators

Some validators accept some extra arguments. We could set them by adding `args` field, e.g: to add arguments for `number` and `date` validators:

```yaml
urlPath: /orders
request:
  - fieldName: create_date
    fieldIn: query
    validators:
      - name: date
        args:
          format: 'YYYY-MM-DD'
  - fieldName: price
    fieldIn: query
    validators:
      - name: number
        args:
          min: 0
          max: 1000000
```

As you saw, to add argument for a validator, we need to use the `name` field to indicate the validator name, and use `args` filed to specify arguments.

### Validation filters

Another way to apply validators is using validation filters, it allows you to define validators with [SQL Syntax](./sql-syntax#filters). Every validator has a unique filter named with prefix `is_`, e.g. `integer` validator has the `is_integer` filter.

To use these filter, chain them after your parameters:

```sql
-- orders.sql
SELECT * FROM
orders
WHERE order_id = {{ context.params.order_id | is_required | is_uuid }}
```

#### Set the arguments for the validators

To set arguments for validators, use Python's keyword arguments:

```sql
-- orders.sql
SELECT * FROM
orders
WHERE create_date = {{ context.params.create_date | is_date(format='YYYY-MM-DD') }}
AND price = {{ context.params.price | is_integer(min=0, max=1000000) }}
```

For more information and further use cases, please check the document [Validation Filter](./api-validation/validation-filter).

## Error Response when sending invalid requests

When a request doesn't fit the requirements of validators, we'd reject the request and return an error response, below is an example:

```yaml
# order.yaml
urlPath: /orders
request:
  - fieldName: price
    fieldIn: query
    validators:
      - name: number
        args:
          min: 0
          max: 1000000
```

Then when you sent the request with GET`<endpoint>/api/orders?price=1000001`, it will return an HTTP status code with **400** and the below message:

```yaml
{
  code: 'vulcan.userError',
  message: 'The input parameter is invalid, it should be integer type',
}
```

## Current supported validators

### `required` validator

Make the request parameter field the **required** parameter, use the [**Joi to check required**](https://joi.dev/api/?v=17.6.1#anyrequired---aliases-exist). The `args` field:

- `disallow` - Array type. Specifies what input values are also not as required and if the parameter value appeared in the `disallow` value, send the response with the error message.

<Tabs groupId="validator-method">
<TabItem value="schema" label="API Schema">

```yaml
# dep_users.yaml
urlPath: /dep_users
request:
  - fieldName: department
    fieldIn: query
    validators:
      - name: required
        args:
          disallow:
            - ''
            - ' '
            - 'null'
```

</TabItem>
<TabItem value="validation-filter" label="Validation Filter">

```sql
SELECT * FROM users
WHERE department = {{ context.params.department | is_required(disallow=['', ' ', 'null']) }}
```

</TabItem>
</Tabs>

Then when you sent the request with GET`<endpoint>/api/dep_users?department=null`, it will return an HTTP status code with **400** and the below message:

```yaml
{
  code: 'vulcan.userError',
  message: 'The input parameter is invalid, it should be required',
}
```

### `uuid` validator

Validate the UUID format for the request parameter, use the [Joi to check UUID](https://joi.dev/api/?v=17.6.1#stringguid---aliases-uuid). The `args` field:

- `version` - String type. Specifies the UUID version. the option could be `uuidv1`, `uuidv4` or `uuidv5`

<Tabs groupId="validator-method">
<TabItem value="schema" label="API Schema">

```yaml
# order.yaml
urlPath: /orders/:id
request:
  - fieldName: id
    fieldIn: path
    validators:
      - name: uuid
        args:
          version: uuidv5
```

</TabItem>
<TabItem value="validation-filter" label="Validation Filter">

```sql
SELECT * FROM order
WHERE id = {{ context.params.id | is_uuid(version='uuidv5') }}
```

</TabItem>
</Tabs>

### `date` validator

Validate whether the request parameter is Date Format type or not, use the [**dayjs package**](https://day.js.org/docs/en/parse/string) to be the format source. The `args` field:

- `format` - String type. Specifies the date needed format, supported [ISO_8601](https://www.w3.org/TR/NOTE-datetime) token, e.g: `'YYYYMMDD'`, `'YYYY-MM-DD'`, `'YYYY-MM-DD HH:mm'`.

<Tabs groupId="validator-method">
<TabItem value="schema" label="API Schema">

```yaml
# order.yaml
urlPath: /orders
request:
  - fieldName: create_date
    fieldIn: query
    validators:
      - name: date
        args:
          format: 'YYYY-MM-DD' # make the date only support 'YYYY-MM-DD' format
```

</TabItem>
<TabItem value="validation-filter" label="Validation Filter">

```sql
SELECT * FROM order
WHERE create_date = {{ context.params.date | is_date(format='YYYY-MM-DD') }} -- make the date only support 'YYYY-MM-DD' format
```

</TabItem>
</Tabs>

### `string` validator

Validate whether the request parameter is a String type, use the [Joi to check String](https://joi.dev/api/?v=17.6.1#string) ( including the `args` field ). The `args` field:

- `format` - String type. Specifies the string format, and supports regular expression. Use the [Joi pattern](https://joi.dev/api/?v=17.6.1#objectpatternpattern-schema-options) to check.
- `length` - Number type. Specifies the String's exact length.
- `max` - Number type. Specifies the minimum number of string characters.
- `min` - Number type. Specifies the maximum number of string characters.

<Tabs groupId="validator-method">
<TabItem value="schema" label="API Schema">

```yaml
# bank_account.yaml
urlPath: /bank_account/:id
request:
  - fieldName: id
    fieldIn: path
    validators:
      - name: string
        args:
          format: '^09[0-9]{8}$'
          length: 10
```

</TabItem>
<TabItem value="validation-filter" label="Validation Filter">

```sql
SELECT * FROM bank_account
WHERE id = {{ context.params.id | is_string(format='^09[0-9]{8}$', length=10) }}
```

</TabItem>
</Tabs>

### `integer` validator

Validate whether the request parameter is an Integer type, use the [Joi to check Integer](https://joi.dev/api/?v=17.6.1#numberinteger). ( including the `args` field ). The `args` field:

- `max` - Number type. Specifies the maximum integer value.
- `min` - Number type. Specifies the minimum integer value.
- `greater` - Number type. Specifies that the integer value must be greater than the limit.
- `less` - Number type. Specifies that the value must be less than the limit.

<Tabs groupId="validator-method">
<TabItem value="schema" label="API Schema">

```yaml
# bank_account.yaml
urlPath: /youth_users
request:
  - fieldName: age
    fieldIn: query
    validators:
      - name: integer
        args:
          - greater: 10
          - less: 19
```

</TabItem>
<TabItem value="validation-filter" label="Validation Filter">

```sql
SELECT * FROM bank_account
WHERE age = {{ context.params.age | is_integer(greater=10, less=19) }}
```

</TabItem>
</Tabs>

### `enum` validator

Constraint the input values with a whitelist. The `args` field:

- `items` - Array type (**Required**). Specifies the whitelist, you must add at least one element to this array.

:::tip
Enum validator won't care about the data type, so the both `[1]` amd `["1"]` allow the value `1` no mater it is string or number.
:::

<Tabs groupId="validator-method">
<TabItem value="schema" label="API Schema">

```yaml
# customers.yaml
urlPath: /customers
request:
  - fieldName: marital_status
    fieldIn: query
    validators:
      - name: enum
        args:
          items:
            - Married
            - Single
            - Unknown
```

</TabItem>
<TabItem value="validation-filter" label="Validation Filter">

```sql
SELECT * FROM customers
WHERE marital_status = {{ context.params.marital_status | is_enum(items=['Married', 'Single', 'Unknown']) }}
```

</TabItem>
</Tabs>
