-
Notifications
You must be signed in to change notification settings - Fork 0
Requirement Validators
Validators define rules for incoming data. They are used in HTTP request requirements and shell command arguments to validate and reject data before it reaches your handler code.
from napi.Require import (
AnyOf, AnyTypeOf, ExactMatch, RegExMatch, File, ListOf
)
# OR napi.http.Require
# Source: nautica.models.RequirementsValue must be one of the provided options:
AnyOf("list", "dict") # accepts "list" or "dict"
AnyOf("admin", "user", "mod") # accepts any of the three rolesValue must match any of the provided types:
AnyTypeOf(str, int) # accepts strings or integers
AnyTypeOf(str, None) # accepts strings or NoneValue must equal the provided value exactly:
ExactMatch("v3") # accepts only "v3"
ExactMatch(42) # accepts only 42Value must match the provided regex pattern:
RegExMatch(r"^[a-zA-Z0-9_]{3,20}$") # alphanumeric, 3-20 chars
RegExMatch(r"^\d{4}-\d{2}-\d{2}$") # date format YYYY-MM-DDFile upload validation, only usable in the files field of @HTTP.Require():
File(max_size=File.MB(2), mime=["image/png", "image/jpeg"])| Parameter | Type | Description |
|---|---|---|
max_size |
int |
Maximum file size in bytes |
mime |
list[str] |
Allowed MIME types |
Size helpers:
File.KB(128) # 128 kilobytes
File.MB(2) # 2 megabytes
File.GB(1) # 1 gigabyteAll items in a list has to match the type of object.
ListOf(str) # All items have to be a string
ListOf(int, max_length = 10) # Only integers, 10 items max
ListOf({"name": str, "age": int}, min_length = 5, max_length = 10) # Only dicts with 'name:str' and 'age:int'; 5-10 items
ListOf(AnyTypeOf(str, int)) # This also worksPass validators as values in @HTTP.Require() instead of plain types:
from napi.http import HTTP, Context, Reply, Require
@HTTP.GET()
@HTTP.Require(
query = {"format": Require.AnyOf("list", "dict")},
body = {"username": Require.RegExMatch(r"^[a-zA-Z0-9_]{3,20}$")},
files = {"avatar": Require.File(max_size=Require.File.MB(2), mime=["image/png"])}
)
async def example(ctx: Context):
...If validation fails, Nautica automatically returns a 422 Unprocessable Content with details about what was missing or invalid. Meaning your handler is
never reached.
Pass validators as argument types in CommandRequirements:
from nautica.services.builtins.shell.decorator import RegisterCommand, CommandRequirements
from nautica.models.Requirements import AnyOf
@RegisterCommand(
"setmode",
"Sets the service mode",
args = CommandRequirements(
args = {"mode": AnyOf("debug", "production")}
)
)
def setmode(mode: str):
return f"Mode set to {mode}"> setmode debug # valid
> setmode invalid # Argument <mode> does not match anyOf(debug, production)
You can define your own validators by extending Requirement:
from nautica.models.Requirements import Requirement
class MyValidator(Requirement):
def isValid(self, content) -> bool: # The validation logic
return content == "hello"
def toComponent(self) -> dict: # For OpenAPI Docs generation
return {
"type": "string",
"enum": ["hello"]
}
def __str__(self):
return "mustBeHello"| Method | Required | Description |
|---|---|---|
isValid(content) |
Yes | Return True if valid, False otherwise |
toComponent() Recommended |
Return dict, with a valid OpenAPI schema |
|
__str__() |
Recommended | Shown in error messages when validation fails |
Custom validators work anywhere a built-in validator would:
@HTTP.Require(query = {"greeting": MyValidator()})You can extend existing validators to customize their behavior. A useful
pattern is extending AnyOf with dynamic options, values that are only
known at runtime:
from nautica import Services
from nautica.models.Requirements import AnyOf, Requirement
class RunningServices(AnyOf):
def __init__(self):
Requirement.__init__(self) # skip AnyOf's __init__
@property
def options(self):
return [s._getName() for s in Services.instances]
def __str__(self):
return "service"Requirement.__init__() is called directly instead of super().__init__()
to skip AnyOf's init, which would require options upfront — since our
options are dynamic and only available at runtime.
Using it in a shell command:
@RegisterCommand(
"restart",
"Restarts a service",
args = CommandRequirements(
args = {"service": RunningServices()}
)
)
def restart(service: str):
return f"Restarting {service}..."> restart HTTPServer # valid
> restart Unknown # invalid, not a running service
Or in an HTTP route:
@HTTP.GET()
@HTTP.Require(
query = {"service": RunningServices()}
)
async def get_service_info(ctx: Context):
name = ctx.query["service"]
...| Validator | Usage | Works in |
|---|---|---|
AnyOf(*options) |
Value must be one of the options | HTTP, Shell |
AnyTypeOf(*types) |
Value must match one of the types | HTTP, Shell |
ExactMatch(match) |
Value must equal exactly | HTTP, Shell |
RegExMatch(pattern) |
Value must match regex | HTTP, Shell |
File(max_size, mime) |
File upload validation | HTTP only |
ListOf(obj, min_length, max_length) |
Items must match validator or type | HTTP, Shell |
Custom Requirement
|
Define your own rules | HTTP, Shell |
- Home
- CLI Reference
- Service Registry
- Built-in Services
- Managers
- Requirement Validators