intangere / tea

vweb with data modeling and validation

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

tea

Fork of vweb with data modeling and validation.
This is a proof of concept.

Installation

mkdir modules
cd modules
git clone https://github.com/intangere/tea.git

Usage

Probably all of Vweb's normal usage applies.
This is just a thin wrapper on top of Vweb that exposes enough to allow for changing route signatures.

There is plenty of examples in example.v
A simple complete example is as follows:

Let's create a /login route which takes in a user model.
First import tea and declare your app variable as such:

import tea

struct App {
        tea.Context
pub mut:
        validators tea.Validators<App>
}

Define your user model:

struct User {
        username string
        password string
}

Define a function to check the user model is valid:

fn (user User) is_valid() bool {
        username := user.username.strip_margin()
        password := user.password.strip_margin()
        return username.len > 3 &&
                password.len > 8
}

Define your /login route:

['/login'; post]
fn (mut app App) login(user User) tea.Result {
        // posted user parameters will be available in user after being validated
        println('Username: ' + user.username + ' Password: ' + user.password)
        app.json('{"status":"testing"}')
}

Define your main function:

fn main() {
        mut app := App{}
        
}

Now in that main function you can create your user data validator.
(You could define this as a named function too)

login_validator := fn (mut app App) {
        model := tea.decode_model<User>(app.req.data)
        if !model.is_valid() {
                app.validation_error('username or password too short')
                return
        }
        app.login(model)
 }

The data validator should unpack some sort of data (in this case the request json body), validate it, and depending on the result, return a validation error or pass the validated model(s) to the route.
Now app needs to be aware of the validator and which route to run it on. So after declaring the validator you need to add it to app.validators:

app.validators.validators['/login'] = login_validator

Then just run the app like you would with Vweb:

tea.run(&app, 8080)

Known problems

  • More than one url parameter is not supported in Vweb

Other notes

  • If you combine url parameters like /user/:user_id with other parameters, the url parameter user_id must be after the other parameters. i.e
// this will not compile (with or without a validator defined. However it does require one)
['/user/:user_id']
fn (mut app App) user(user_id string, user User) {
}
// this will compile with or without a validator
['/user/:user_id']
fn (mut app App) user(user_id string) {
}
// this will compile with a validator
['/user/:user_id']
fn (mut app App) user(user_id int) {
}
// this is how it has to be defined and will compile (requires a validator defined for /user/:user_id)
['/user/:user_id']
fn (mut app App) user(user User, user_id string) {
}
  • tea.decode_model<T>(string) takes a json string as input and decodes it into a struct
  • app.url_params exposes the url path parameters when applicable as a map[string]string
    • Therefore you can have validated routes that don't take the url param i.e :user_id as a parameter but still access it
  • tea.from_map<T>(map[string]string) can be used to take app.query and turn it into a struct with string only fields

End goal

Whenever compile-time reflection is more complete in V something like this should be possible which would hide most of the internals:

import tea

struct App {
        tea.Context
pub mut:
        validators tea.Validators<App>
}

['/login'; post]
fn (mut app App) login(user User) tea.Result {
        // posted user parameters will be available in user after being validated
        println('Username: ' + user.username + ' Password: ' + user.password)
        app.json('{"status":"testing"}')
}

['/login'; validator]
fn (mut app App) login_validator() User {
        model := tea.decode_model<User>(app.req.data)
        if !model.is_valid() {
                app.validation_error('username or password too short')
                return
        }
        return model
}

fn main() {
        mut app := App{}
        tea.run(&app, 8080)
}

About

vweb with data modeling and validation


Languages

Language:V 99.4%Language:AMPL 0.6%