Skip to content

Commit

Permalink
doc(fxvalidator): Provided module documentation (#308)
Browse files Browse the repository at this point in the history
  • Loading branch information
ekkinox authored Jan 22, 2025
1 parent a7b13df commit bb35e61
Show file tree
Hide file tree
Showing 3 changed files with 383 additions and 4 deletions.
382 changes: 382 additions & 0 deletions docs/modules/fxvalidator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,382 @@
---
title: Modules - Log
icon: material/cube-outline
---

# :material-cube-outline: Validator Module

[![ci](https://github.com/ankorstore/yokai/actions/workflows/fxvalidator-ci.yml/badge.svg)](https://github.com/ankorstore/yokai/actions/workflows/fxvalidator-ci.yml)
[![go report](https://goreportcard.com/badge/github.com/ankorstore/yokai/fxvalidator)](https://goreportcard.com/report/github.com/ankorstore/yokai/fxvalidator)
[![codecov](https://codecov.io/gh/ankorstore/yokai/graph/badge.svg?token=ghUBlFsjhR&flag=fxvalidator)](https://app.codecov.io/gh/ankorstore/yokai/tree/main/fxvalidator)
[![Deps](https://img.shields.io/badge/osi-deps-blue)](https://deps.dev/go/github.com%2Fankorstore%2Fyokai%2Ffxvalidator)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/ankorstore/yokai/fxvalidator)](https://pkg.go.dev/github.com/ankorstore/yokai/fxvalidator)
## Overview

Yokai provides a [fxvalidator](https://github.com/ankorstore/yokai/tree/main/fxvalidator) module, allowing you to inject a validator anywhere needed.

It wraps the [go-playground/validator](https://github.com/go-playground/validator) module.

## Installation

First install the module:

```shell
go get github.com/ankorstore/yokai/fxvalidator
```

Then activate it in your application bootstrapper:

```go title="internal/bootstrap.go"
package internal

import (
"github.com/ankorstore/yokai/fxcore"
"github.com/ankorstore/yokai/fxvalidator"
)

var Bootstrapper = fxcore.NewBootstrapper().WithOptions(
// modules registration
fxvalidator.FXValidatorModule,
// ...
)
```

## Configuration

Configuration reference:

```yaml title="configs/config.yaml"
modules:
validator:
tag_name: validate # struct tag to define validation rules, default = validate
private_fields: false # to enable validation on private fields, disabled by default
```
## Usage
This module makes available a `*validator.Validate` instance in
Yokai dependency injection system, that you can inject anywhere.

For example:

```go title="internal/service/example.go"
package service
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type ExampleStruct struct {
Name string `validate:"required"`
Email string `validate:"required,email"`
}

type ExampleService struct {
validate *validator.Validate
}

func NewExampleService(validate *validator.Validate) *ExampleService {
return &ExampleService{
validate: validate,
}
}

func (s *ExampleService) DoSomething() error {
es := ExampleStruct{
Name: "name",
Email: "[email protected]",
}
err := s.validate.Struct(es)
if valErrs, ok := err.(*validator.ValidationErrors); ok {
fmt.Println(valErrs)
}
return valErrs
}
```

See [go-playground/validator](https://github.com/go-playground/validator) documentation for more details about available validation features.

## Customization

This module provides the possibility to easily customize your validator.

### Custom aliases

You can register custom validation aliases with `AsAlias()`:

```go title="internal/register.go"
package internal

import (
"github.com/ankorstore/yokai/fxvalidator"
"go.uber.org/fx"
)

func Register() fx.Option {
return fx.Options(
// register the rla alias: provided value must be required, alpha only and lowercase
fxvalidator.AsAlias("rla", "required,alpha,lowercase"),
// ...
)
}
```

Then:

```go title="internal/service/example.go"
package service

import (
"context"

"github.com/go-playground/validator/v10"
)

type ExampleService struct {
validate *validator.Validate
}

func NewExampleService(validate *validator.Validate) *ExampleService {
return &ExampleService{
validate: validate,
}
}

func (s *ExampleService) DoSomething(ctx context.Context) error {
// valid
err := s.validate.VarCtx(ctx, "valid", "rla")

// invalid
err = s.validate.VarCtx(ctx, "1234", "rla")

return err
}
```

### Custom validations

You can register custom validations functions with `AsValidation()`:

```go title="internal/register.go"
package internal

import (
"github.com/ankorstore/yokai/fxvalidator"
"go.uber.org/fx"
)

func Register() fx.Option {
return fx.Options(
// register the foobar validation: provided lowercased value must be "foo" or "bar"
fxvalidator.AsValidation(
"foobar",
func(ctx context.Context, fl validator.FieldLevel) bool {
s := strings.ToLower(fl.Field().String())

return s == "foo" || s == "bar"
},
true,
),
// ...
)
}
```

Then:

```go title="internal/service/example.go"
package service

import (
"context"

"github.com/go-playground/validator/v10"
)

type ExampleService struct {
validate *validator.Validate
}

func NewExampleService(validate *validator.Validate) *ExampleService {
return &ExampleService{
validate: validate,
}
}

func (s *ExampleService) DoSomething(ctx context.Context) error {
// valid
err := s.validate.VarCtx(ctx, "FoO", "foobar")

// invalid
err = s.validate.VarCtx(ctx, "invalid", "foobar")

return err
}
```

### Custom struct validations

You can register custom struct validations functions with `AsStructValidation()`:

```go title="internal/register.go"
package internal

import (
"github.com/ankorstore/yokai/fxvalidator"
"github.com/foo/bar/internal/service"
"go.uber.org/fx"
)

func Register() fx.Option {
return fx.Options(
// register a struct validation for the struct service.ExampleStruct{}
fxvalidator.AsStructValidation(
func(ctx context.Context, sl validator.StructLevel) {
fb, ok := sl.Current().Interface().(service.ExampleStruct)
if ok {
if fb.Foo != "foo" {
sl.ReportError(fb.Foo, "Foo", "Foo", "invalid-foo", "invalid foo")
}
if fb.Bar != "bar" {
sl.ReportError(fb.Bar, "Bar", "Bar", "invalid-bar", "invalid bar")
}
}
},
service.ExampleStruct{},
),
// ...
)
}
```

Then:

```go title="internal/service/example.go"
package service

import (
"context"

"github.com/go-playground/validator/v10"
)

type ExampleStruct struct {
Foo string
Bar string
}

type ExampleService struct {
validate *validator.Validate
}

func NewExampleService(validate *validator.Validate) *ExampleService {
return &ExampleService{
validate: validate,
}
}

func (s *ExampleService) DoSomething(ctx context.Context) error {
// valid
err := s.validate.StructCtx(ctx, ExampleStruct{Foo: "foo", Bar: "bar"})

// invalid
err = s.validate.StructCtx(ctx, ExampleStruct{Foo: "invalid", Bar: ""})

return err
}
```

### Custom types

You can register custom types functions with `AsCustomType()`:

```go title="internal/register.go"
package internal

import (
"reflect"

"github.com/ankorstore/yokai/fxvalidator"
"github.com/foo/bar/internal/service"
"go.uber.org/fx"
)

func Register() fx.Option {
return fx.Options(
// register a custom type function for service.Foo{} and service.Bar{}
fxvalidator.AsCustomType(
func(field reflect.Value) interface{} {
if f, ok := field.Interface().(service.Foo); ok {
if f.Value != "foo" {
return ""
}

return f.Value
}

if b, ok := field.Interface().(service.Bar); ok {
if b.Value != "bar" {
return ""
}

return b.Value
}

return ""
},
service.Foo{},
service.Bar{},
),
// ...
)
}
```

Then:

```go title="internal/service/example.go"
package service

import (
"context"

"github.com/go-playground/validator/v10"
)

type Foo struct {
Value string `validate:"required"`
}

type Bar struct {
Value string `validate:"required"`
}

type ExampleStruct struct {
Foo Foo
Bar Bar
}

type ExampleService struct {
validate *validator.Validate
}

func NewExampleService(validate *validator.Validate) *ExampleService {
return &ExampleService{
validate: validate,
}
}

func (s *ExampleService) DoSomething(ctx context.Context) error {
// valid
err := s.validate.StructCtx(ctx, ExampleStruct{Foo: Foo{Value: "foo"}, Bar: Bar{Value: "bar"}})

// invalid
err = s.validate.StructCtx(ctx, ExampleStruct{Foo: Foo{Value: "invalid"}, Bar: Bar{Value: ""}})

return err
}
```
Loading

0 comments on commit bb35e61

Please sign in to comment.