1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-05-02 22:17:10 +08:00
Dušan Borovčanin 3d3aa525a6
NOISSUE - Switch to Google Zanzibar Access control approach (#1919)
* Return Auth service

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Update Compose to run with SpiceDB and Auth svc

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Update auth gRPC API

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Remove Users' policies

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Move Groups to internal

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Use shared groups in Users

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Remove unused code

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Use pkg Groups in Things

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Remove Things groups

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Make imports consistent

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Update Groups networking

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Remove things groups-specific API

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Move Things Clients to the root

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Move Clients to Users root

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Temporarily remove tracing

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Fix imports

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Add buffer config for gRPC

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Update auth type for Things

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Use Auth for login

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Add temporary solution for refresh token

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Update Tokenizer interface

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Updade tokens issuing

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Fix token issuing

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Update JWT validator and refactor Tokenizer

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Rename access timeout

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Rename login to authenticate

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Update Identify to use SubjectID

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Add Auth to Groups

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Use the Auth service for Groups

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Update auth schema

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Fix Auth for Groups

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Add auth for addons (#14)

Signed-off-by: Arvindh <arvindh91@gmail.com>

Speparate Login and Refresh tokens

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Merge authN and authZ requests for things

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Add connect and disconnect

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Update sharing

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Fix policies addition and removal

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Update relation with roels

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Add gRPC to Things

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Assign and Unassign members to group and Listing of Group members (#15)

* add auth for addons

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add assign and unassign to group

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add group incomplete repo implementation

Signed-off-by: Arvindh <arvindh91@gmail.com>

* groups for users

Signed-off-by: Arvindh <arvindh91@gmail.com>

* groups for users

Signed-off-by: Arvindh <arvindh91@gmail.com>

* groups for users

Signed-off-by: Arvindh <arvindh91@gmail.com>

* groups for users

Signed-off-by: Arvindh <arvindh91@gmail.com>

---------

Signed-off-by: Arvindh <arvindh91@gmail.com>

Move coap mqtt and ws policies to spicedb (#16)

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

Remove old policies

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

NOISSUE - Things authorize to return thingID (#18)

This commit modifies the authorize endpoint to the grpc endpoint to return thingID. The authorize endpoint allows adapters to get the publisher of the message.

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

Add Groups to users service (#17)

* add assign and unassign to group

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add group incomplete repo implementation

Signed-off-by: Arvindh <arvindh91@gmail.com>

* groups for users

Signed-off-by: Arvindh <arvindh91@gmail.com>

* groups for users

Signed-off-by: Arvindh <arvindh91@gmail.com>

* groups for users

Signed-off-by: Arvindh <arvindh91@gmail.com>

* groups for users

Signed-off-by: Arvindh <arvindh91@gmail.com>

* groups for users stable 1

Signed-off-by: Arvindh <arvindh91@gmail.com>

* groups for users stable 2

Signed-off-by: Arvindh <arvindh91@gmail.com>

* groups for users & things

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Amend signature

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix merge error

Signed-off-by: Arvindh <arvindh91@gmail.com>

---------

Signed-off-by: Arvindh <arvindh91@gmail.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* NOISSUE - Fix es code (#21)

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* NOISSUE - Fix Bugs (#20)

* fix bugs

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix bugs

Signed-off-by: Arvindh <arvindh91@gmail.com>

---------

Signed-off-by: Arvindh <arvindh91@gmail.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* NOISSUE - Test e2e (#19)

* fix: connect method

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* fix: e2e

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* fix changes in sdk and e2e

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* feat(docker): remove unnecessary port mapping

Remove the port mapping for MQTT broker in the docker-compose.yml file.

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* Enable group listing

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* feat(responses): update ChannelsPage struct

The ChannelsPage struct in the responses.go file has been updated. The "Channels" field has been renamed to "Groups" to provide more accurate naming. This change ensures consistency and clarity in the codebase.

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* feat(things): add UpdateClientSecret method

Add the UpdateClientSecret method to the things service. This method allows updating the client secret for a specific client identified by the provided token, id, and key parameters.

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

---------

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Use smaller buffers for gRPC

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Clean up tests (#22)

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Add Connect Disconnect endpoints (#23)

* fix bugs

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix bugs

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix list of things in a channel and Add connect disconnect endpoint

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix list of things in a channel and Add connect disconnect endpoint

Signed-off-by: Arvindh <arvindh91@gmail.com>

---------

Signed-off-by: Arvindh <arvindh91@gmail.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Add: Things share with users (#25)

* fix list of things in a channel and Add connect disconnect endpoint

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: things share with other users

Signed-off-by: Arvindh <arvindh91@gmail.com>

---------

Signed-off-by: Arvindh <arvindh91@gmail.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* NOISSUE - Rename gRPC Services (#24)

* Rename things and users auth service

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* docs: add authorization docs for gRPC services

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* Rename things and users grpc services

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* Remove mainflux.env package

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

---------

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Add: Listing of things, channels, groups, users  (#26)

* add: listing of channels, users, groups, things

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: listing of channels, users, groups, things

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: listing of channels, users, groups, things

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: listing of channels, users, groups, things

Signed-off-by: Arvindh <arvindh91@gmail.com>

---------

Signed-off-by: Arvindh <arvindh91@gmail.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* NOISSUE - Clean Up Users (#27)

* feat(groups): rename redis package to events

- Renamed the `redis` package to `events` in the `internal/groups` directory.
- Updated the file paths and names accordingly.
- This change reflects the more accurate purpose of the package and improves code organization.

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* feat(auth): Modify identity method

Change request and response of identity method

Add accessToken and refreshToken to Token response

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* clean up users, remove dead code

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* feat(users): add unit tests for user service

This commit adds unit tests for the user service in the `users` package. The tests cover various scenarios and ensure the correct behavior of the service.

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

---------

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Add: List of user groups & removed repeating code in groups (#29)

* removed repeating code in list groups

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: list of user group

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: otel handler operator name for endpoints

Signed-off-by: Arvindh <arvindh91@gmail.com>

---------

Signed-off-by: Arvindh <arvindh91@gmail.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* NOISSUE - Clean Up Things Service (#28)

* Rework things service

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* add tests

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

---------

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* NOISSUE - Clean Up Auth Service (#30)

* clean up auth service

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* feat(auth): remove unused import

Remove the unused import of `emptypb` in `auth.pb.go`. This import is not being used in the codebase and can be safely removed.

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

---------

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* NOISSUE - Update API docs (#31)

Signed-off-by: rodneyosodo <blackd0t@protonmail.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Remove TODO comments and cleanup the code

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Update dependenices

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

---------

Signed-off-by: Arvindh <arvindh91@gmail.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>
Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>
Signed-off-by: rodneyosodo <blackd0t@protonmail.com>
Co-authored-by: b1ackd0t <28790446+rodneyosodo@users.noreply.github.com>
Co-authored-by: Arvindh <30824765+arvindh123@users.noreply.github.com>
2023-10-15 22:02:13 +02:00

1332 lines
43 KiB
Go

package echo
import (
"encoding"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"time"
)
/**
Following functions provide handful of methods for binding to Go native types from request query or path parameters.
* QueryParamsBinder(c) - binds query parameters (source URL)
* PathParamsBinder(c) - binds path parameters (source URL)
* FormFieldBinder(c) - binds form fields (source URL + body)
Example:
```go
var length int64
err := echo.QueryParamsBinder(c).Int64("length", &length).BindError()
```
For every supported type there are following methods:
* <Type>("param", &destination) - if parameter value exists then binds it to given destination of that type i.e Int64(...).
* Must<Type>("param", &destination) - parameter value is required to exist, binds it to given destination of that type i.e MustInt64(...).
* <Type>s("param", &destination) - (for slices) if parameter values exists then binds it to given destination of that type i.e Int64s(...).
* Must<Type>s("param", &destination) - (for slices) parameter value is required to exist, binds it to given destination of that type i.e MustInt64s(...).
for some slice types `BindWithDelimiter("param", &dest, ",")` supports splitting parameter values before type conversion is done
i.e. URL `/api/search?id=1,2,3&id=1` can be bind to `[]int64{1,2,3,1}`
`FailFast` flags binder to stop binding after first bind error during binder call chain. Enabled by default.
`BindError()` returns first bind error from binder and resets errors in binder. Useful along with `FailFast()` method
to do binding and returns on first problem
`BindErrors()` returns all bind errors from binder and resets errors in binder.
Types that are supported:
* bool
* float32
* float64
* int
* int8
* int16
* int32
* int64
* uint
* uint8/byte (does not support `bytes()`. Use BindUnmarshaler/CustomFunc to convert value from base64 etc to []byte{})
* uint16
* uint32
* uint64
* string
* time
* duration
* BindUnmarshaler() interface
* TextUnmarshaler() interface
* JSONUnmarshaler() interface
* UnixTime() - converts unix time (integer) to time.Time
* UnixTimeMilli() - converts unix time with millisecond precision (integer) to time.Time
* UnixTimeNano() - converts unix time with nanosecond precision (integer) to time.Time
* CustomFunc() - callback function for your custom conversion logic. Signature `func(values []string) []error`
*/
// BindingError represents an error that occurred while binding request data.
type BindingError struct {
// Field is the field name where value binding failed
Field string `json:"field"`
// Values of parameter that failed to bind.
Values []string `json:"-"`
*HTTPError
}
// NewBindingError creates new instance of binding error
func NewBindingError(sourceParam string, values []string, message interface{}, internalError error) error {
return &BindingError{
Field: sourceParam,
Values: values,
HTTPError: &HTTPError{
Code: http.StatusBadRequest,
Message: message,
Internal: internalError,
},
}
}
// Error returns error message
func (be *BindingError) Error() string {
return fmt.Sprintf("%s, field=%s", be.HTTPError.Error(), be.Field)
}
// ValueBinder provides utility methods for binding query or path parameter to various Go built-in types
type ValueBinder struct {
// failFast is flag for binding methods to return without attempting to bind when previous binding already failed
failFast bool
errors []error
// ValueFunc is used to get single parameter (first) value from request
ValueFunc func(sourceParam string) string
// ValuesFunc is used to get all values for parameter from request. i.e. `/api/search?ids=1&ids=2`
ValuesFunc func(sourceParam string) []string
// ErrorFunc is used to create errors. Allows you to use your own error type, that for example marshals to your specific json response
ErrorFunc func(sourceParam string, values []string, message interface{}, internalError error) error
}
// QueryParamsBinder creates query parameter value binder
func QueryParamsBinder(c Context) *ValueBinder {
return &ValueBinder{
failFast: true,
ValueFunc: c.QueryParam,
ValuesFunc: func(sourceParam string) []string {
values, ok := c.QueryParams()[sourceParam]
if !ok {
return nil
}
return values
},
ErrorFunc: NewBindingError,
}
}
// PathParamsBinder creates path parameter value binder
func PathParamsBinder(c Context) *ValueBinder {
return &ValueBinder{
failFast: true,
ValueFunc: c.Param,
ValuesFunc: func(sourceParam string) []string {
// path parameter should not have multiple values so getting values does not make sense but lets not error out here
value := c.Param(sourceParam)
if value == "" {
return nil
}
return []string{value}
},
ErrorFunc: NewBindingError,
}
}
// FormFieldBinder creates form field value binder
// For all requests, FormFieldBinder parses the raw query from the URL and uses query params as form fields
//
// For POST, PUT, and PATCH requests, it also reads the request body, parses it
// as a form and uses query params as form fields. Request body parameters take precedence over URL query
// string values in r.Form.
//
// NB: when binding forms take note that this implementation uses standard library form parsing
// which parses form data from BOTH URL and BODY if content type is not MIMEMultipartForm
// See https://golang.org/pkg/net/http/#Request.ParseForm
func FormFieldBinder(c Context) *ValueBinder {
vb := &ValueBinder{
failFast: true,
ValueFunc: func(sourceParam string) string {
return c.Request().FormValue(sourceParam)
},
ErrorFunc: NewBindingError,
}
vb.ValuesFunc = func(sourceParam string) []string {
if c.Request().Form == nil {
// this is same as `Request().FormValue()` does internally
_ = c.Request().ParseMultipartForm(32 << 20)
}
values, ok := c.Request().Form[sourceParam]
if !ok {
return nil
}
return values
}
return vb
}
// FailFast set internal flag to indicate if binding methods will return early (without binding) when previous bind failed
// NB: call this method before any other binding methods as it modifies binding methods behaviour
func (b *ValueBinder) FailFast(value bool) *ValueBinder {
b.failFast = value
return b
}
func (b *ValueBinder) setError(err error) {
if b.errors == nil {
b.errors = []error{err}
return
}
b.errors = append(b.errors, err)
}
// BindError returns first seen bind error and resets/empties binder errors for further calls
func (b *ValueBinder) BindError() error {
if b.errors == nil {
return nil
}
err := b.errors[0]
b.errors = nil // reset errors so next chain will start from zero
return err
}
// BindErrors returns all bind errors and resets/empties binder errors for further calls
func (b *ValueBinder) BindErrors() []error {
if b.errors == nil {
return nil
}
errors := b.errors
b.errors = nil // reset errors so next chain will start from zero
return errors
}
// CustomFunc binds parameter values with Func. Func is called only when parameter values exist.
func (b *ValueBinder) CustomFunc(sourceParam string, customFunc func(values []string) []error) *ValueBinder {
return b.customFunc(sourceParam, customFunc, false)
}
// MustCustomFunc requires parameter values to exist to bind with Func. Returns error when value does not exist.
func (b *ValueBinder) MustCustomFunc(sourceParam string, customFunc func(values []string) []error) *ValueBinder {
return b.customFunc(sourceParam, customFunc, true)
}
func (b *ValueBinder) customFunc(sourceParam string, customFunc func(values []string) []error, valueMustExist bool) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
values := b.ValuesFunc(sourceParam)
if len(values) == 0 {
if valueMustExist {
b.setError(b.ErrorFunc(sourceParam, []string{}, "required field value is empty", nil))
}
return b
}
if errs := customFunc(values); errs != nil {
b.errors = append(b.errors, errs...)
}
return b
}
// String binds parameter to string variable
func (b *ValueBinder) String(sourceParam string, dest *string) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
value := b.ValueFunc(sourceParam)
if value == "" {
return b
}
*dest = value
return b
}
// MustString requires parameter value to exist to bind to string variable. Returns error when value does not exist
func (b *ValueBinder) MustString(sourceParam string, dest *string) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
value := b.ValueFunc(sourceParam)
if value == "" {
b.setError(b.ErrorFunc(sourceParam, []string{value}, "required field value is empty", nil))
return b
}
*dest = value
return b
}
// Strings binds parameter values to slice of string
func (b *ValueBinder) Strings(sourceParam string, dest *[]string) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
value := b.ValuesFunc(sourceParam)
if value == nil {
return b
}
*dest = value
return b
}
// MustStrings requires parameter values to exist to bind to slice of string variables. Returns error when value does not exist
func (b *ValueBinder) MustStrings(sourceParam string, dest *[]string) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
value := b.ValuesFunc(sourceParam)
if value == nil {
b.setError(b.ErrorFunc(sourceParam, []string{}, "required field value is empty", nil))
return b
}
*dest = value
return b
}
// BindUnmarshaler binds parameter to destination implementing BindUnmarshaler interface
func (b *ValueBinder) BindUnmarshaler(sourceParam string, dest BindUnmarshaler) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
tmp := b.ValueFunc(sourceParam)
if tmp == "" {
return b
}
if err := dest.UnmarshalParam(tmp); err != nil {
b.setError(b.ErrorFunc(sourceParam, []string{tmp}, "failed to bind field value to BindUnmarshaler interface", err))
}
return b
}
// MustBindUnmarshaler requires parameter value to exist to bind to destination implementing BindUnmarshaler interface.
// Returns error when value does not exist
func (b *ValueBinder) MustBindUnmarshaler(sourceParam string, dest BindUnmarshaler) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
value := b.ValueFunc(sourceParam)
if value == "" {
b.setError(b.ErrorFunc(sourceParam, []string{value}, "required field value is empty", nil))
return b
}
if err := dest.UnmarshalParam(value); err != nil {
b.setError(b.ErrorFunc(sourceParam, []string{value}, "failed to bind field value to BindUnmarshaler interface", err))
}
return b
}
// JSONUnmarshaler binds parameter to destination implementing json.Unmarshaler interface
func (b *ValueBinder) JSONUnmarshaler(sourceParam string, dest json.Unmarshaler) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
tmp := b.ValueFunc(sourceParam)
if tmp == "" {
return b
}
if err := dest.UnmarshalJSON([]byte(tmp)); err != nil {
b.setError(b.ErrorFunc(sourceParam, []string{tmp}, "failed to bind field value to json.Unmarshaler interface", err))
}
return b
}
// MustJSONUnmarshaler requires parameter value to exist to bind to destination implementing json.Unmarshaler interface.
// Returns error when value does not exist
func (b *ValueBinder) MustJSONUnmarshaler(sourceParam string, dest json.Unmarshaler) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
tmp := b.ValueFunc(sourceParam)
if tmp == "" {
b.setError(b.ErrorFunc(sourceParam, []string{tmp}, "required field value is empty", nil))
return b
}
if err := dest.UnmarshalJSON([]byte(tmp)); err != nil {
b.setError(b.ErrorFunc(sourceParam, []string{tmp}, "failed to bind field value to json.Unmarshaler interface", err))
}
return b
}
// TextUnmarshaler binds parameter to destination implementing encoding.TextUnmarshaler interface
func (b *ValueBinder) TextUnmarshaler(sourceParam string, dest encoding.TextUnmarshaler) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
tmp := b.ValueFunc(sourceParam)
if tmp == "" {
return b
}
if err := dest.UnmarshalText([]byte(tmp)); err != nil {
b.setError(b.ErrorFunc(sourceParam, []string{tmp}, "failed to bind field value to encoding.TextUnmarshaler interface", err))
}
return b
}
// MustTextUnmarshaler requires parameter value to exist to bind to destination implementing encoding.TextUnmarshaler interface.
// Returns error when value does not exist
func (b *ValueBinder) MustTextUnmarshaler(sourceParam string, dest encoding.TextUnmarshaler) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
tmp := b.ValueFunc(sourceParam)
if tmp == "" {
b.setError(b.ErrorFunc(sourceParam, []string{tmp}, "required field value is empty", nil))
return b
}
if err := dest.UnmarshalText([]byte(tmp)); err != nil {
b.setError(b.ErrorFunc(sourceParam, []string{tmp}, "failed to bind field value to encoding.TextUnmarshaler interface", err))
}
return b
}
// BindWithDelimiter binds parameter to destination by suitable conversion function.
// Delimiter is used before conversion to split parameter value to separate values
func (b *ValueBinder) BindWithDelimiter(sourceParam string, dest interface{}, delimiter string) *ValueBinder {
return b.bindWithDelimiter(sourceParam, dest, delimiter, false)
}
// MustBindWithDelimiter requires parameter value to exist to bind destination by suitable conversion function.
// Delimiter is used before conversion to split parameter value to separate values
func (b *ValueBinder) MustBindWithDelimiter(sourceParam string, dest interface{}, delimiter string) *ValueBinder {
return b.bindWithDelimiter(sourceParam, dest, delimiter, true)
}
func (b *ValueBinder) bindWithDelimiter(sourceParam string, dest interface{}, delimiter string, valueMustExist bool) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
values := b.ValuesFunc(sourceParam)
if len(values) == 0 {
if valueMustExist {
b.setError(b.ErrorFunc(sourceParam, []string{}, "required field value is empty", nil))
}
return b
}
tmpValues := make([]string, 0, len(values))
for _, v := range values {
tmpValues = append(tmpValues, strings.Split(v, delimiter)...)
}
switch d := dest.(type) {
case *[]string:
*d = tmpValues
return b
case *[]bool:
return b.bools(sourceParam, tmpValues, d)
case *[]int64, *[]int32, *[]int16, *[]int8, *[]int:
return b.ints(sourceParam, tmpValues, d)
case *[]uint64, *[]uint32, *[]uint16, *[]uint8, *[]uint: // *[]byte is same as *[]uint8
return b.uints(sourceParam, tmpValues, d)
case *[]float64, *[]float32:
return b.floats(sourceParam, tmpValues, d)
case *[]time.Duration:
return b.durations(sourceParam, tmpValues, d)
default:
// support only cases when destination is slice
// does not support time.Time as it needs argument (layout) for parsing or BindUnmarshaler
b.setError(b.ErrorFunc(sourceParam, []string{}, "unsupported bind type", nil))
return b
}
}
// Int64 binds parameter to int64 variable
func (b *ValueBinder) Int64(sourceParam string, dest *int64) *ValueBinder {
return b.intValue(sourceParam, dest, 64, false)
}
// MustInt64 requires parameter value to exist to bind to int64 variable. Returns error when value does not exist
func (b *ValueBinder) MustInt64(sourceParam string, dest *int64) *ValueBinder {
return b.intValue(sourceParam, dest, 64, true)
}
// Int32 binds parameter to int32 variable
func (b *ValueBinder) Int32(sourceParam string, dest *int32) *ValueBinder {
return b.intValue(sourceParam, dest, 32, false)
}
// MustInt32 requires parameter value to exist to bind to int32 variable. Returns error when value does not exist
func (b *ValueBinder) MustInt32(sourceParam string, dest *int32) *ValueBinder {
return b.intValue(sourceParam, dest, 32, true)
}
// Int16 binds parameter to int16 variable
func (b *ValueBinder) Int16(sourceParam string, dest *int16) *ValueBinder {
return b.intValue(sourceParam, dest, 16, false)
}
// MustInt16 requires parameter value to exist to bind to int16 variable. Returns error when value does not exist
func (b *ValueBinder) MustInt16(sourceParam string, dest *int16) *ValueBinder {
return b.intValue(sourceParam, dest, 16, true)
}
// Int8 binds parameter to int8 variable
func (b *ValueBinder) Int8(sourceParam string, dest *int8) *ValueBinder {
return b.intValue(sourceParam, dest, 8, false)
}
// MustInt8 requires parameter value to exist to bind to int8 variable. Returns error when value does not exist
func (b *ValueBinder) MustInt8(sourceParam string, dest *int8) *ValueBinder {
return b.intValue(sourceParam, dest, 8, true)
}
// Int binds parameter to int variable
func (b *ValueBinder) Int(sourceParam string, dest *int) *ValueBinder {
return b.intValue(sourceParam, dest, 0, false)
}
// MustInt requires parameter value to exist to bind to int variable. Returns error when value does not exist
func (b *ValueBinder) MustInt(sourceParam string, dest *int) *ValueBinder {
return b.intValue(sourceParam, dest, 0, true)
}
func (b *ValueBinder) intValue(sourceParam string, dest interface{}, bitSize int, valueMustExist bool) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
value := b.ValueFunc(sourceParam)
if value == "" {
if valueMustExist {
b.setError(b.ErrorFunc(sourceParam, []string{}, "required field value is empty", nil))
}
return b
}
return b.int(sourceParam, value, dest, bitSize)
}
func (b *ValueBinder) int(sourceParam string, value string, dest interface{}, bitSize int) *ValueBinder {
n, err := strconv.ParseInt(value, 10, bitSize)
if err != nil {
if bitSize == 0 {
b.setError(b.ErrorFunc(sourceParam, []string{value}, "failed to bind field value to int", err))
} else {
b.setError(b.ErrorFunc(sourceParam, []string{value}, fmt.Sprintf("failed to bind field value to int%v", bitSize), err))
}
return b
}
switch d := dest.(type) {
case *int64:
*d = n
case *int32:
*d = int32(n)
case *int16:
*d = int16(n)
case *int8:
*d = int8(n)
case *int:
*d = int(n)
}
return b
}
func (b *ValueBinder) intsValue(sourceParam string, dest interface{}, valueMustExist bool) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
values := b.ValuesFunc(sourceParam)
if len(values) == 0 {
if valueMustExist {
b.setError(b.ErrorFunc(sourceParam, values, "required field value is empty", nil))
}
return b
}
return b.ints(sourceParam, values, dest)
}
func (b *ValueBinder) ints(sourceParam string, values []string, dest interface{}) *ValueBinder {
switch d := dest.(type) {
case *[]int64:
tmp := make([]int64, len(values))
for i, v := range values {
b.int(sourceParam, v, &tmp[i], 64)
if b.failFast && b.errors != nil {
return b
}
}
if b.errors == nil {
*d = tmp
}
case *[]int32:
tmp := make([]int32, len(values))
for i, v := range values {
b.int(sourceParam, v, &tmp[i], 32)
if b.failFast && b.errors != nil {
return b
}
}
if b.errors == nil {
*d = tmp
}
case *[]int16:
tmp := make([]int16, len(values))
for i, v := range values {
b.int(sourceParam, v, &tmp[i], 16)
if b.failFast && b.errors != nil {
return b
}
}
if b.errors == nil {
*d = tmp
}
case *[]int8:
tmp := make([]int8, len(values))
for i, v := range values {
b.int(sourceParam, v, &tmp[i], 8)
if b.failFast && b.errors != nil {
return b
}
}
if b.errors == nil {
*d = tmp
}
case *[]int:
tmp := make([]int, len(values))
for i, v := range values {
b.int(sourceParam, v, &tmp[i], 0)
if b.failFast && b.errors != nil {
return b
}
}
if b.errors == nil {
*d = tmp
}
}
return b
}
// Int64s binds parameter to slice of int64
func (b *ValueBinder) Int64s(sourceParam string, dest *[]int64) *ValueBinder {
return b.intsValue(sourceParam, dest, false)
}
// MustInt64s requires parameter value to exist to bind to int64 slice variable. Returns error when value does not exist
func (b *ValueBinder) MustInt64s(sourceParam string, dest *[]int64) *ValueBinder {
return b.intsValue(sourceParam, dest, true)
}
// Int32s binds parameter to slice of int32
func (b *ValueBinder) Int32s(sourceParam string, dest *[]int32) *ValueBinder {
return b.intsValue(sourceParam, dest, false)
}
// MustInt32s requires parameter value to exist to bind to int32 slice variable. Returns error when value does not exist
func (b *ValueBinder) MustInt32s(sourceParam string, dest *[]int32) *ValueBinder {
return b.intsValue(sourceParam, dest, true)
}
// Int16s binds parameter to slice of int16
func (b *ValueBinder) Int16s(sourceParam string, dest *[]int16) *ValueBinder {
return b.intsValue(sourceParam, dest, false)
}
// MustInt16s requires parameter value to exist to bind to int16 slice variable. Returns error when value does not exist
func (b *ValueBinder) MustInt16s(sourceParam string, dest *[]int16) *ValueBinder {
return b.intsValue(sourceParam, dest, true)
}
// Int8s binds parameter to slice of int8
func (b *ValueBinder) Int8s(sourceParam string, dest *[]int8) *ValueBinder {
return b.intsValue(sourceParam, dest, false)
}
// MustInt8s requires parameter value to exist to bind to int8 slice variable. Returns error when value does not exist
func (b *ValueBinder) MustInt8s(sourceParam string, dest *[]int8) *ValueBinder {
return b.intsValue(sourceParam, dest, true)
}
// Ints binds parameter to slice of int
func (b *ValueBinder) Ints(sourceParam string, dest *[]int) *ValueBinder {
return b.intsValue(sourceParam, dest, false)
}
// MustInts requires parameter value to exist to bind to int slice variable. Returns error when value does not exist
func (b *ValueBinder) MustInts(sourceParam string, dest *[]int) *ValueBinder {
return b.intsValue(sourceParam, dest, true)
}
// Uint64 binds parameter to uint64 variable
func (b *ValueBinder) Uint64(sourceParam string, dest *uint64) *ValueBinder {
return b.uintValue(sourceParam, dest, 64, false)
}
// MustUint64 requires parameter value to exist to bind to uint64 variable. Returns error when value does not exist
func (b *ValueBinder) MustUint64(sourceParam string, dest *uint64) *ValueBinder {
return b.uintValue(sourceParam, dest, 64, true)
}
// Uint32 binds parameter to uint32 variable
func (b *ValueBinder) Uint32(sourceParam string, dest *uint32) *ValueBinder {
return b.uintValue(sourceParam, dest, 32, false)
}
// MustUint32 requires parameter value to exist to bind to uint32 variable. Returns error when value does not exist
func (b *ValueBinder) MustUint32(sourceParam string, dest *uint32) *ValueBinder {
return b.uintValue(sourceParam, dest, 32, true)
}
// Uint16 binds parameter to uint16 variable
func (b *ValueBinder) Uint16(sourceParam string, dest *uint16) *ValueBinder {
return b.uintValue(sourceParam, dest, 16, false)
}
// MustUint16 requires parameter value to exist to bind to uint16 variable. Returns error when value does not exist
func (b *ValueBinder) MustUint16(sourceParam string, dest *uint16) *ValueBinder {
return b.uintValue(sourceParam, dest, 16, true)
}
// Uint8 binds parameter to uint8 variable
func (b *ValueBinder) Uint8(sourceParam string, dest *uint8) *ValueBinder {
return b.uintValue(sourceParam, dest, 8, false)
}
// MustUint8 requires parameter value to exist to bind to uint8 variable. Returns error when value does not exist
func (b *ValueBinder) MustUint8(sourceParam string, dest *uint8) *ValueBinder {
return b.uintValue(sourceParam, dest, 8, true)
}
// Byte binds parameter to byte variable
func (b *ValueBinder) Byte(sourceParam string, dest *byte) *ValueBinder {
return b.uintValue(sourceParam, dest, 8, false)
}
// MustByte requires parameter value to exist to bind to byte variable. Returns error when value does not exist
func (b *ValueBinder) MustByte(sourceParam string, dest *byte) *ValueBinder {
return b.uintValue(sourceParam, dest, 8, true)
}
// Uint binds parameter to uint variable
func (b *ValueBinder) Uint(sourceParam string, dest *uint) *ValueBinder {
return b.uintValue(sourceParam, dest, 0, false)
}
// MustUint requires parameter value to exist to bind to uint variable. Returns error when value does not exist
func (b *ValueBinder) MustUint(sourceParam string, dest *uint) *ValueBinder {
return b.uintValue(sourceParam, dest, 0, true)
}
func (b *ValueBinder) uintValue(sourceParam string, dest interface{}, bitSize int, valueMustExist bool) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
value := b.ValueFunc(sourceParam)
if value == "" {
if valueMustExist {
b.setError(b.ErrorFunc(sourceParam, []string{}, "required field value is empty", nil))
}
return b
}
return b.uint(sourceParam, value, dest, bitSize)
}
func (b *ValueBinder) uint(sourceParam string, value string, dest interface{}, bitSize int) *ValueBinder {
n, err := strconv.ParseUint(value, 10, bitSize)
if err != nil {
if bitSize == 0 {
b.setError(b.ErrorFunc(sourceParam, []string{value}, "failed to bind field value to uint", err))
} else {
b.setError(b.ErrorFunc(sourceParam, []string{value}, fmt.Sprintf("failed to bind field value to uint%v", bitSize), err))
}
return b
}
switch d := dest.(type) {
case *uint64:
*d = n
case *uint32:
*d = uint32(n)
case *uint16:
*d = uint16(n)
case *uint8: // byte is alias to uint8
*d = uint8(n)
case *uint:
*d = uint(n)
}
return b
}
func (b *ValueBinder) uintsValue(sourceParam string, dest interface{}, valueMustExist bool) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
values := b.ValuesFunc(sourceParam)
if len(values) == 0 {
if valueMustExist {
b.setError(b.ErrorFunc(sourceParam, values, "required field value is empty", nil))
}
return b
}
return b.uints(sourceParam, values, dest)
}
func (b *ValueBinder) uints(sourceParam string, values []string, dest interface{}) *ValueBinder {
switch d := dest.(type) {
case *[]uint64:
tmp := make([]uint64, len(values))
for i, v := range values {
b.uint(sourceParam, v, &tmp[i], 64)
if b.failFast && b.errors != nil {
return b
}
}
if b.errors == nil {
*d = tmp
}
case *[]uint32:
tmp := make([]uint32, len(values))
for i, v := range values {
b.uint(sourceParam, v, &tmp[i], 32)
if b.failFast && b.errors != nil {
return b
}
}
if b.errors == nil {
*d = tmp
}
case *[]uint16:
tmp := make([]uint16, len(values))
for i, v := range values {
b.uint(sourceParam, v, &tmp[i], 16)
if b.failFast && b.errors != nil {
return b
}
}
if b.errors == nil {
*d = tmp
}
case *[]uint8: // byte is alias to uint8
tmp := make([]uint8, len(values))
for i, v := range values {
b.uint(sourceParam, v, &tmp[i], 8)
if b.failFast && b.errors != nil {
return b
}
}
if b.errors == nil {
*d = tmp
}
case *[]uint:
tmp := make([]uint, len(values))
for i, v := range values {
b.uint(sourceParam, v, &tmp[i], 0)
if b.failFast && b.errors != nil {
return b
}
}
if b.errors == nil {
*d = tmp
}
}
return b
}
// Uint64s binds parameter to slice of uint64
func (b *ValueBinder) Uint64s(sourceParam string, dest *[]uint64) *ValueBinder {
return b.uintsValue(sourceParam, dest, false)
}
// MustUint64s requires parameter value to exist to bind to uint64 slice variable. Returns error when value does not exist
func (b *ValueBinder) MustUint64s(sourceParam string, dest *[]uint64) *ValueBinder {
return b.uintsValue(sourceParam, dest, true)
}
// Uint32s binds parameter to slice of uint32
func (b *ValueBinder) Uint32s(sourceParam string, dest *[]uint32) *ValueBinder {
return b.uintsValue(sourceParam, dest, false)
}
// MustUint32s requires parameter value to exist to bind to uint32 slice variable. Returns error when value does not exist
func (b *ValueBinder) MustUint32s(sourceParam string, dest *[]uint32) *ValueBinder {
return b.uintsValue(sourceParam, dest, true)
}
// Uint16s binds parameter to slice of uint16
func (b *ValueBinder) Uint16s(sourceParam string, dest *[]uint16) *ValueBinder {
return b.uintsValue(sourceParam, dest, false)
}
// MustUint16s requires parameter value to exist to bind to uint16 slice variable. Returns error when value does not exist
func (b *ValueBinder) MustUint16s(sourceParam string, dest *[]uint16) *ValueBinder {
return b.uintsValue(sourceParam, dest, true)
}
// Uint8s binds parameter to slice of uint8
func (b *ValueBinder) Uint8s(sourceParam string, dest *[]uint8) *ValueBinder {
return b.uintsValue(sourceParam, dest, false)
}
// MustUint8s requires parameter value to exist to bind to uint8 slice variable. Returns error when value does not exist
func (b *ValueBinder) MustUint8s(sourceParam string, dest *[]uint8) *ValueBinder {
return b.uintsValue(sourceParam, dest, true)
}
// Uints binds parameter to slice of uint
func (b *ValueBinder) Uints(sourceParam string, dest *[]uint) *ValueBinder {
return b.uintsValue(sourceParam, dest, false)
}
// MustUints requires parameter value to exist to bind to uint slice variable. Returns error when value does not exist
func (b *ValueBinder) MustUints(sourceParam string, dest *[]uint) *ValueBinder {
return b.uintsValue(sourceParam, dest, true)
}
// Bool binds parameter to bool variable
func (b *ValueBinder) Bool(sourceParam string, dest *bool) *ValueBinder {
return b.boolValue(sourceParam, dest, false)
}
// MustBool requires parameter value to exist to bind to bool variable. Returns error when value does not exist
func (b *ValueBinder) MustBool(sourceParam string, dest *bool) *ValueBinder {
return b.boolValue(sourceParam, dest, true)
}
func (b *ValueBinder) boolValue(sourceParam string, dest *bool, valueMustExist bool) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
value := b.ValueFunc(sourceParam)
if value == "" {
if valueMustExist {
b.setError(b.ErrorFunc(sourceParam, []string{}, "required field value is empty", nil))
}
return b
}
return b.bool(sourceParam, value, dest)
}
func (b *ValueBinder) bool(sourceParam string, value string, dest *bool) *ValueBinder {
n, err := strconv.ParseBool(value)
if err != nil {
b.setError(b.ErrorFunc(sourceParam, []string{value}, "failed to bind field value to bool", err))
return b
}
*dest = n
return b
}
func (b *ValueBinder) boolsValue(sourceParam string, dest *[]bool, valueMustExist bool) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
values := b.ValuesFunc(sourceParam)
if len(values) == 0 {
if valueMustExist {
b.setError(b.ErrorFunc(sourceParam, []string{}, "required field value is empty", nil))
}
return b
}
return b.bools(sourceParam, values, dest)
}
func (b *ValueBinder) bools(sourceParam string, values []string, dest *[]bool) *ValueBinder {
tmp := make([]bool, len(values))
for i, v := range values {
b.bool(sourceParam, v, &tmp[i])
if b.failFast && b.errors != nil {
return b
}
}
if b.errors == nil {
*dest = tmp
}
return b
}
// Bools binds parameter values to slice of bool variables
func (b *ValueBinder) Bools(sourceParam string, dest *[]bool) *ValueBinder {
return b.boolsValue(sourceParam, dest, false)
}
// MustBools requires parameter values to exist to bind to slice of bool variables. Returns error when values does not exist
func (b *ValueBinder) MustBools(sourceParam string, dest *[]bool) *ValueBinder {
return b.boolsValue(sourceParam, dest, true)
}
// Float64 binds parameter to float64 variable
func (b *ValueBinder) Float64(sourceParam string, dest *float64) *ValueBinder {
return b.floatValue(sourceParam, dest, 64, false)
}
// MustFloat64 requires parameter value to exist to bind to float64 variable. Returns error when value does not exist
func (b *ValueBinder) MustFloat64(sourceParam string, dest *float64) *ValueBinder {
return b.floatValue(sourceParam, dest, 64, true)
}
// Float32 binds parameter to float32 variable
func (b *ValueBinder) Float32(sourceParam string, dest *float32) *ValueBinder {
return b.floatValue(sourceParam, dest, 32, false)
}
// MustFloat32 requires parameter value to exist to bind to float32 variable. Returns error when value does not exist
func (b *ValueBinder) MustFloat32(sourceParam string, dest *float32) *ValueBinder {
return b.floatValue(sourceParam, dest, 32, true)
}
func (b *ValueBinder) floatValue(sourceParam string, dest interface{}, bitSize int, valueMustExist bool) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
value := b.ValueFunc(sourceParam)
if value == "" {
if valueMustExist {
b.setError(b.ErrorFunc(sourceParam, []string{}, "required field value is empty", nil))
}
return b
}
return b.float(sourceParam, value, dest, bitSize)
}
func (b *ValueBinder) float(sourceParam string, value string, dest interface{}, bitSize int) *ValueBinder {
n, err := strconv.ParseFloat(value, bitSize)
if err != nil {
b.setError(b.ErrorFunc(sourceParam, []string{value}, fmt.Sprintf("failed to bind field value to float%v", bitSize), err))
return b
}
switch d := dest.(type) {
case *float64:
*d = n
case *float32:
*d = float32(n)
}
return b
}
func (b *ValueBinder) floatsValue(sourceParam string, dest interface{}, valueMustExist bool) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
values := b.ValuesFunc(sourceParam)
if len(values) == 0 {
if valueMustExist {
b.setError(b.ErrorFunc(sourceParam, []string{}, "required field value is empty", nil))
}
return b
}
return b.floats(sourceParam, values, dest)
}
func (b *ValueBinder) floats(sourceParam string, values []string, dest interface{}) *ValueBinder {
switch d := dest.(type) {
case *[]float64:
tmp := make([]float64, len(values))
for i, v := range values {
b.float(sourceParam, v, &tmp[i], 64)
if b.failFast && b.errors != nil {
return b
}
}
if b.errors == nil {
*d = tmp
}
case *[]float32:
tmp := make([]float32, len(values))
for i, v := range values {
b.float(sourceParam, v, &tmp[i], 32)
if b.failFast && b.errors != nil {
return b
}
}
if b.errors == nil {
*d = tmp
}
}
return b
}
// Float64s binds parameter values to slice of float64 variables
func (b *ValueBinder) Float64s(sourceParam string, dest *[]float64) *ValueBinder {
return b.floatsValue(sourceParam, dest, false)
}
// MustFloat64s requires parameter values to exist to bind to slice of float64 variables. Returns error when values does not exist
func (b *ValueBinder) MustFloat64s(sourceParam string, dest *[]float64) *ValueBinder {
return b.floatsValue(sourceParam, dest, true)
}
// Float32s binds parameter values to slice of float32 variables
func (b *ValueBinder) Float32s(sourceParam string, dest *[]float32) *ValueBinder {
return b.floatsValue(sourceParam, dest, false)
}
// MustFloat32s requires parameter values to exist to bind to slice of float32 variables. Returns error when values does not exist
func (b *ValueBinder) MustFloat32s(sourceParam string, dest *[]float32) *ValueBinder {
return b.floatsValue(sourceParam, dest, true)
}
// Time binds parameter to time.Time variable
func (b *ValueBinder) Time(sourceParam string, dest *time.Time, layout string) *ValueBinder {
return b.time(sourceParam, dest, layout, false)
}
// MustTime requires parameter value to exist to bind to time.Time variable. Returns error when value does not exist
func (b *ValueBinder) MustTime(sourceParam string, dest *time.Time, layout string) *ValueBinder {
return b.time(sourceParam, dest, layout, true)
}
func (b *ValueBinder) time(sourceParam string, dest *time.Time, layout string, valueMustExist bool) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
value := b.ValueFunc(sourceParam)
if value == "" {
if valueMustExist {
b.setError(b.ErrorFunc(sourceParam, []string{value}, "required field value is empty", nil))
}
return b
}
t, err := time.Parse(layout, value)
if err != nil {
b.setError(b.ErrorFunc(sourceParam, []string{value}, "failed to bind field value to Time", err))
return b
}
*dest = t
return b
}
// Times binds parameter values to slice of time.Time variables
func (b *ValueBinder) Times(sourceParam string, dest *[]time.Time, layout string) *ValueBinder {
return b.times(sourceParam, dest, layout, false)
}
// MustTimes requires parameter values to exist to bind to slice of time.Time variables. Returns error when values does not exist
func (b *ValueBinder) MustTimes(sourceParam string, dest *[]time.Time, layout string) *ValueBinder {
return b.times(sourceParam, dest, layout, true)
}
func (b *ValueBinder) times(sourceParam string, dest *[]time.Time, layout string, valueMustExist bool) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
values := b.ValuesFunc(sourceParam)
if len(values) == 0 {
if valueMustExist {
b.setError(b.ErrorFunc(sourceParam, []string{}, "required field value is empty", nil))
}
return b
}
tmp := make([]time.Time, len(values))
for i, v := range values {
t, err := time.Parse(layout, v)
if err != nil {
b.setError(b.ErrorFunc(sourceParam, []string{v}, "failed to bind field value to Time", err))
if b.failFast {
return b
}
continue
}
tmp[i] = t
}
if b.errors == nil {
*dest = tmp
}
return b
}
// Duration binds parameter to time.Duration variable
func (b *ValueBinder) Duration(sourceParam string, dest *time.Duration) *ValueBinder {
return b.duration(sourceParam, dest, false)
}
// MustDuration requires parameter value to exist to bind to time.Duration variable. Returns error when value does not exist
func (b *ValueBinder) MustDuration(sourceParam string, dest *time.Duration) *ValueBinder {
return b.duration(sourceParam, dest, true)
}
func (b *ValueBinder) duration(sourceParam string, dest *time.Duration, valueMustExist bool) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
value := b.ValueFunc(sourceParam)
if value == "" {
if valueMustExist {
b.setError(b.ErrorFunc(sourceParam, []string{value}, "required field value is empty", nil))
}
return b
}
t, err := time.ParseDuration(value)
if err != nil {
b.setError(b.ErrorFunc(sourceParam, []string{value}, "failed to bind field value to Duration", err))
return b
}
*dest = t
return b
}
// Durations binds parameter values to slice of time.Duration variables
func (b *ValueBinder) Durations(sourceParam string, dest *[]time.Duration) *ValueBinder {
return b.durationsValue(sourceParam, dest, false)
}
// MustDurations requires parameter values to exist to bind to slice of time.Duration variables. Returns error when values does not exist
func (b *ValueBinder) MustDurations(sourceParam string, dest *[]time.Duration) *ValueBinder {
return b.durationsValue(sourceParam, dest, true)
}
func (b *ValueBinder) durationsValue(sourceParam string, dest *[]time.Duration, valueMustExist bool) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
values := b.ValuesFunc(sourceParam)
if len(values) == 0 {
if valueMustExist {
b.setError(b.ErrorFunc(sourceParam, []string{}, "required field value is empty", nil))
}
return b
}
return b.durations(sourceParam, values, dest)
}
func (b *ValueBinder) durations(sourceParam string, values []string, dest *[]time.Duration) *ValueBinder {
tmp := make([]time.Duration, len(values))
for i, v := range values {
t, err := time.ParseDuration(v)
if err != nil {
b.setError(b.ErrorFunc(sourceParam, []string{v}, "failed to bind field value to Duration", err))
if b.failFast {
return b
}
continue
}
tmp[i] = t
}
if b.errors == nil {
*dest = tmp
}
return b
}
// UnixTime binds parameter to time.Time variable (in local Time corresponding to the given Unix time).
//
// Example: 1609180603 bind to 2020-12-28T18:36:43.000000000+00:00
//
// Note:
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
func (b *ValueBinder) UnixTime(sourceParam string, dest *time.Time) *ValueBinder {
return b.unixTime(sourceParam, dest, false, time.Second)
}
// MustUnixTime requires parameter value to exist to bind to time.Duration variable (in local time corresponding
// to the given Unix time). Returns error when value does not exist.
//
// Example: 1609180603 bind to 2020-12-28T18:36:43.000000000+00:00
//
// Note:
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
func (b *ValueBinder) MustUnixTime(sourceParam string, dest *time.Time) *ValueBinder {
return b.unixTime(sourceParam, dest, true, time.Second)
}
// UnixTimeMilli binds parameter to time.Time variable (in local time corresponding to the given Unix time in millisecond precision).
//
// Example: 1647184410140 bind to 2022-03-13T15:13:30.140000000+00:00
//
// Note:
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
func (b *ValueBinder) UnixTimeMilli(sourceParam string, dest *time.Time) *ValueBinder {
return b.unixTime(sourceParam, dest, false, time.Millisecond)
}
// MustUnixTimeMilli requires parameter value to exist to bind to time.Duration variable (in local time corresponding
// to the given Unix time in millisecond precision). Returns error when value does not exist.
//
// Example: 1647184410140 bind to 2022-03-13T15:13:30.140000000+00:00
//
// Note:
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
func (b *ValueBinder) MustUnixTimeMilli(sourceParam string, dest *time.Time) *ValueBinder {
return b.unixTime(sourceParam, dest, true, time.Millisecond)
}
// UnixTimeNano binds parameter to time.Time variable (in local time corresponding to the given Unix time in nanosecond precision).
//
// Example: 1609180603123456789 binds to 2020-12-28T18:36:43.123456789+00:00
// Example: 1000000000 binds to 1970-01-01T00:00:01.000000000+00:00
// Example: 999999999 binds to 1970-01-01T00:00:00.999999999+00:00
//
// Note:
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
// - Javascript's Number type only has about 53 bits of precision (Number.MAX_SAFE_INTEGER = 9007199254740991). Compare it to 1609180603123456789 in example.
func (b *ValueBinder) UnixTimeNano(sourceParam string, dest *time.Time) *ValueBinder {
return b.unixTime(sourceParam, dest, false, time.Nanosecond)
}
// MustUnixTimeNano requires parameter value to exist to bind to time.Duration variable (in local Time corresponding
// to the given Unix time value in nano second precision). Returns error when value does not exist.
//
// Example: 1609180603123456789 binds to 2020-12-28T18:36:43.123456789+00:00
// Example: 1000000000 binds to 1970-01-01T00:00:01.000000000+00:00
// Example: 999999999 binds to 1970-01-01T00:00:00.999999999+00:00
//
// Note:
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
// - Javascript's Number type only has about 53 bits of precision (Number.MAX_SAFE_INTEGER = 9007199254740991). Compare it to 1609180603123456789 in example.
func (b *ValueBinder) MustUnixTimeNano(sourceParam string, dest *time.Time) *ValueBinder {
return b.unixTime(sourceParam, dest, true, time.Nanosecond)
}
func (b *ValueBinder) unixTime(sourceParam string, dest *time.Time, valueMustExist bool, precision time.Duration) *ValueBinder {
if b.failFast && b.errors != nil {
return b
}
value := b.ValueFunc(sourceParam)
if value == "" {
if valueMustExist {
b.setError(b.ErrorFunc(sourceParam, []string{value}, "required field value is empty", nil))
}
return b
}
n, err := strconv.ParseInt(value, 10, 64)
if err != nil {
b.setError(b.ErrorFunc(sourceParam, []string{value}, "failed to bind field value to Time", err))
return b
}
switch precision {
case time.Second:
*dest = time.Unix(n, 0)
case time.Millisecond:
*dest = time.Unix(n/1e3, (n%1e3)*1e6) // TODO: time.UnixMilli(n) exists since Go1.17 switch to that when min version allows
case time.Nanosecond:
*dest = time.Unix(0, n)
}
return b
}