mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 02:31:04 +01:00
commit
d8d170b775
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
go_version: [1.18.x]
|
||||
go_version: [1.19.x]
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
|
|
|
@ -28,6 +28,9 @@ The codebase for Dependency Injection, Internationalization and localization and
|
|||
|
||||
## Fixes and Improvements
|
||||
|
||||
- Enable setting a custom "go-redis" client through `SetClient` go redis driver method or `Client` struct field on sessions/database/redis driver as requested at [chat](https://chat.iris-go.com).
|
||||
- Ignore `"csrf.token"` form data key when missing on `ctx.ReadForm` by default as requested at [#1941](https://github.com/kataras/iris/issues/1941).
|
||||
|
||||
- Fix [CVE-2020-5398](https://github.com/advisories/GHSA-8wx2-9q48-vm9r).
|
||||
|
||||
- New `{x:weekday}` path parameter type, example code:
|
||||
|
|
|
@ -246,6 +246,7 @@ With your help, we can improve Open Source web development for everyone!
|
|||
<a href="https://github.com/lexrus"><img src="https://avatars1.githubusercontent.com/u/219689?v=4" alt="lexrus" title="lexrus" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/li3p"><img src="https://avatars1.githubusercontent.com/u/55519?v=4" alt="li3p" title="li3p" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/se77en"><img src="https://avatars1.githubusercontent.com/u/1468284?v=4" alt="se77en" title="se77en" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/simpleittools"><img src="https://avatars1.githubusercontent.com/u/42871067?v=4" alt="simpleittools" title="simpleittools" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/sumjoe"><img src="https://avatars1.githubusercontent.com/u/32655210?v=4" alt="sumjoe" title="sumjoe" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/vincent-li"><img src="https://avatars1.githubusercontent.com/u/765470?v=4" alt="vincent-li" title="vincent-li" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/sascha11110"><img src="https://avatars1.githubusercontent.com/u/15168372?v=4" alt="sascha11110" title="sascha11110" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
|
@ -337,6 +338,7 @@ With your help, we can improve Open Source web development for everyone!
|
|||
<a href="https://github.com/edwindna2"><img src="https://avatars1.githubusercontent.com/u/5441354?v=4" alt="edwindna2" title="edwindna2" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/fenriz07"><img src="https://avatars1.githubusercontent.com/u/9199380?v=4" alt="fenriz07" title="fenriz07" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/ffelipelimao"><img src="https://avatars1.githubusercontent.com/u/28612817?v=4" alt="ffelipelimao" title="ffelipelimao" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/geGao123"><img src="https://avatars1.githubusercontent.com/u/6398228?v=4" alt="geGao123" title="geGao123" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/gnosthi"><img src="https://avatars1.githubusercontent.com/u/17650528?v=4" alt="gnosthi" title="gnosthi" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/goten002"><img src="https://avatars1.githubusercontent.com/u/5025060?v=4" alt="goten002" title="goten002" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/guanzi008"><img src="https://avatars1.githubusercontent.com/u/20619190?v=4" alt="guanzi008" title="guanzi008" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
|
@ -368,6 +370,7 @@ With your help, we can improve Open Source web development for everyone!
|
|||
<a href="https://github.com/ozfive"><img src="https://avatars1.githubusercontent.com/u/4494266?v=4" alt="ozfive" title="ozfive" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/paulxu21"><img src="https://avatars1.githubusercontent.com/u/6261758?v=4" alt="paulxu21" title="paulxu21" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/pitt134"><img src="https://avatars1.githubusercontent.com/u/13091629?v=4" alt="pitt134" title="pitt134" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/qiepeipei"><img src="https://avatars1.githubusercontent.com/u/16110628?v=4" alt="qiepeipei" title="qiepeipei" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/qiuzhanghua"><img src="https://avatars1.githubusercontent.com/u/478393?v=4" alt="qiuzhanghua" title="qiuzhanghua" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/rapita"><img src="https://avatars1.githubusercontent.com/u/22305375?v=4" alt="rapita" title="rapita" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/relaera"><img src="https://avatars1.githubusercontent.com/u/26012106?v=4" alt="relaera" title="relaera" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
|
@ -380,6 +383,7 @@ With your help, we can improve Open Source web development for everyone!
|
|||
<a href="https://github.com/sbenimeli"><img src="https://avatars1.githubusercontent.com/u/46652122?v=4" alt="sbenimeli" title="sbenimeli" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/sebyno"><img src="https://avatars1.githubusercontent.com/u/15988169?v=4" alt="sebyno" title="sebyno" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/seun-otosho"><img src="https://avatars1.githubusercontent.com/u/74518370?v=4" alt="seun-otosho" title="seun-otosho" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/su1gen"><img src="https://avatars1.githubusercontent.com/u/86298730?v=4" alt="su1gen" title="su1gen" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/svirmi"><img src="https://avatars1.githubusercontent.com/u/52601346?v=4" alt="svirmi" title="svirmi" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/unixedia"><img src="https://avatars1.githubusercontent.com/u/70646128?v=4" alt="unixedia" title="unixedia" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/vguhesan"><img src="https://avatars1.githubusercontent.com/u/193960?v=4" alt="vguhesan" title="vguhesan" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
|
@ -393,6 +397,7 @@ With your help, we can improve Open Source web development for everyone!
|
|||
<a href="https://github.com/mdamschen"><img src="https://avatars1.githubusercontent.com/u/40914728?v=4" alt="mdamschen" title="mdamschen" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/mtrense"><img src="https://avatars1.githubusercontent.com/u/1008285?v=4" alt="mtrense" title="mtrense" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/netbaalzovf"><img src="https://avatars1.githubusercontent.com/u/98529711?v=4" alt="netbaalzovf" title="netbaalzovf" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/oliverjosefzimmer"><img src="https://avatars1.githubusercontent.com/u/24566297?v=4" alt="oliverjosefzimmer" title="oliverjosefzimmer" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/lfaynman"><img src="https://avatars1.githubusercontent.com/u/16815068?v=4" alt="lfaynman" title="lfaynman" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/ArturWierzbicki"><img src="https://avatars1.githubusercontent.com/u/23451458?v=4" alt="ArturWierzbicki" title="ArturWierzbicki" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
<a href="https://github.com/NA"><img src="https://avatars1.githubusercontent.com/u/1600?v=4" alt="NA" title="NA" with="75" style="width:75px;max-width:75px;height:75px" height="75" /></a>
|
||||
|
@ -470,7 +475,7 @@ $ go get github.com/kataras/iris/v12@master
|
|||
**Run**
|
||||
|
||||
```sh
|
||||
$ go mod tidy -compat=1.18
|
||||
$ go mod tidy -compat=1.19
|
||||
$ go run .
|
||||
```
|
||||
|
||||
|
|
|
@ -261,7 +261,7 @@ $ go get github.com/kataras/iris/v12@master
|
|||
```txt
|
||||
module myapp
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
||||
require github.com/kataras/iris/v12 master
|
||||
```
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
* [Not Found - Intelligence](routing/intelligence/main.go)
|
||||
* [Not Found - Suggest Closest Paths](routing/intelligence/manual/main.go)
|
||||
* [Dynamic Path](routing/dynamic-path/main.go)
|
||||
* [At-username](routing/dynamic-path/at-username/main.go)
|
||||
* [Root Wildcard](routing/dynamic-path/root-wildcard/main.go)
|
||||
* [Implement a Parameter Type](routing/macros/main.go)
|
||||
* [Same Path Pattern but Func](routing/dynamic-path/same-pattern-different-func/main.go)
|
||||
|
|
30
_examples/routing/dynamic-path/at-username/main.go
Normal file
30
_examples/routing/dynamic-path/at-username/main.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
package main
|
||||
|
||||
import "github.com/kataras/iris/v12"
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
ctx.Writef("Hello %s", "world")
|
||||
})
|
||||
|
||||
// This is an Iris-only feature across all web frameworks
|
||||
// in every programming language for years.
|
||||
// Dynamic Route Path Parameters Functions.
|
||||
// Set min length characters to 2.
|
||||
// Prefix of the username is '@'
|
||||
// Otherwise 404.
|
||||
//
|
||||
// You can also use the regexp(...) function for more advanced expressions.
|
||||
app.Get("/{username:string min(2) prefix(@)}", func(ctx iris.Context) {
|
||||
username := ctx.Params().Get("username")[1:]
|
||||
ctx.Writef("Username is %s", username)
|
||||
})
|
||||
|
||||
// http://localhost:8080 -> FOUND (Hello world)
|
||||
// http://localhost:8080/other -> NOT FOUND
|
||||
// http://localhost:8080/@ -> NOT FOUND
|
||||
// http://localhost:8080/@kataras -> FOUND (username is kataras)
|
||||
app.Listen(":8080")
|
||||
}
|
|
@ -30,11 +30,16 @@ func main() {
|
|||
Password: "",
|
||||
Database: "",
|
||||
Prefix: "myapp-",
|
||||
Driver: redis.GoRedis(), // defaults.
|
||||
Driver: redis.GoRedis(), // defaults to this driver.
|
||||
// To set a custom, existing go-redis client, use the "SetClient" method:
|
||||
// Driver: redis.GoRedis().SetClient(customGoRedisClient)
|
||||
})
|
||||
|
||||
// Optionally configure the underline driver:
|
||||
// driver := redis.GoRedis()
|
||||
// // To set a custom client:
|
||||
// driver.SetClient(customGoRedisClient)
|
||||
// OR:
|
||||
// driver.ClientOptions = redis.Options{...}
|
||||
// driver.ClusterOptions = redis.ClusterOptions{...}
|
||||
// redis.New(redis.Config{Driver: driver, ...})
|
||||
|
|
|
@ -1012,6 +1012,10 @@ var GetDomain = func(hostport string) string {
|
|||
// loopback.
|
||||
return "localhost"
|
||||
default:
|
||||
if net.ParseIP(host) != nil { // if it's an IP, see #1945.
|
||||
return host
|
||||
}
|
||||
|
||||
if domain, err := publicsuffix.EffectiveTLDPlusOne(host); err == nil {
|
||||
host = domain
|
||||
}
|
||||
|
@ -2575,8 +2579,12 @@ func (ctx *Context) RecordRequestBody(b bool) {
|
|||
|
||||
// IsRecordingBody reports whether the request body can be readen multiple times.
|
||||
func (ctx *Context) IsRecordingBody() bool {
|
||||
return ctx.values.GetBoolDefault(disableRequestBodyConsumptionContextKey,
|
||||
ctx.app.ConfigurationReadOnly().GetDisableBodyConsumptionOnUnmarshal())
|
||||
if ctx.app.ConfigurationReadOnly().GetDisableBodyConsumptionOnUnmarshal() {
|
||||
return true
|
||||
}
|
||||
|
||||
value, _ := ctx.values.GetBool(disableRequestBodyConsumptionContextKey)
|
||||
return value
|
||||
}
|
||||
|
||||
// GetBody reads and returns the request body.
|
||||
|
@ -2672,7 +2680,20 @@ type JSONReader struct { // Note(@kataras): struct instead of optional funcs to
|
|||
}
|
||||
|
||||
var ReadJSON = func(ctx *Context, outPtr interface{}, opts ...JSONReader) error {
|
||||
decoder := json.NewDecoder(ctx.request.Body)
|
||||
var body io.Reader
|
||||
|
||||
if ctx.IsRecordingBody() {
|
||||
data, err := io.ReadAll(ctx.request.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
setBody(ctx.request, data)
|
||||
body = bytes.NewReader(data)
|
||||
} else {
|
||||
body = ctx.request.Body
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(body)
|
||||
// decoder := gojson.NewDecoder(ctx.Request().Body)
|
||||
if len(opts) > 0 {
|
||||
options := opts[0]
|
||||
|
@ -2778,6 +2799,24 @@ var (
|
|||
// A shortcut for the `schema#IsErrPath`.
|
||||
IsErrPath = schema.IsErrPath
|
||||
|
||||
// IsErrPathCRSFToken reports whether the given "err" is caused
|
||||
// by unknown key error on "csrf.token". See `context#ReadForm` for more.
|
||||
IsErrPathCRSFToken = func(err error) bool {
|
||||
if err == nil || CSRFTokenFormKey == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if m, ok := err.(schema.MultiError); ok {
|
||||
if csrfErr, hasCSRFToken := m[CSRFTokenFormKey]; hasCSRFToken {
|
||||
_, is := csrfErr.(schema.UnknownKeyError)
|
||||
return is
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ErrEmptyForm is returned by
|
||||
// - `context#ReadForm`
|
||||
// - `context#ReadQuery`
|
||||
|
@ -2820,6 +2859,11 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
// CSRFTokenFormKey the CSRF token key of the form data.
|
||||
//
|
||||
// See ReadForm method for more.
|
||||
const CSRFTokenFormKey = "csrf.token"
|
||||
|
||||
// ReadForm binds the request body of a form to the "formObject".
|
||||
// It supports any kind of type, including custom structs.
|
||||
// It will return nothing if request data are empty.
|
||||
|
@ -2831,6 +2875,9 @@ var (
|
|||
// If a client sent an unknown field, this method will return an error,
|
||||
// in order to ignore that error use the `err != nil && !iris.IsErrPath(err)`.
|
||||
//
|
||||
// As of 15 Aug 2022, ReadForm does not return an error over unknown CSRF token form key,
|
||||
// to change this behavior globally, set the `context.CSRFTokenFormKey` to an empty value.
|
||||
//
|
||||
// Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-form/main.go
|
||||
func (ctx *Context) ReadForm(formObject interface{}) error {
|
||||
values := ctx.FormValues()
|
||||
|
@ -2842,7 +2889,7 @@ func (ctx *Context) ReadForm(formObject interface{}) error {
|
|||
}
|
||||
|
||||
err := schema.DecodeForm(values, formObject)
|
||||
if err != nil {
|
||||
if err != nil && !IsErrPathCRSFToken(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ func (e *Error) As(target interface{}) bool {
|
|||
}
|
||||
}
|
||||
|
||||
return errors.As(e.Err, &te.Err)
|
||||
return errors.As(te.Err, &e)
|
||||
}
|
||||
|
||||
return ok
|
||||
|
|
|
@ -32,13 +32,22 @@ func TestErrorIs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// errorString is a trivial implementation of error.
|
||||
type errorString struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func (e *errorString) Error() string {
|
||||
return e.s
|
||||
}
|
||||
|
||||
func TestErrorAs(t *testing.T) {
|
||||
testErr := errors.New("as")
|
||||
testErr := &errorString{"as"}
|
||||
err := &Error{Err: testErr}
|
||||
if expected, got := true, errors.As(err, &testErr); expected != got {
|
||||
t.Fatalf("[testErr as err] expected %v but got %v", expected, got)
|
||||
}
|
||||
if expected, got := true, errors.As(testErr, &err); expected != got {
|
||||
if expected, got := false, errors.As(testErr, &err); expected != got /* errorString does not implemeny As, so the std/default functionality will be applied */ {
|
||||
t.Fatalf("[err as testErr] expected %v but got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
|
14
go.mod
14
go.mod
|
@ -1,13 +1,13 @@
|
|||
module github.com/kataras/iris/v12
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
||||
// retract v12.1.8 // please update to @master
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.2.0
|
||||
github.com/CloudyKit/jet/v6 v6.1.0
|
||||
github.com/Shopify/goreferrer v0.0.0-20210630161223-536fa16abd6f
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06
|
||||
github.com/andybalholm/brotli v1.0.4
|
||||
github.com/blang/semver/v4 v4.0.0
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4
|
||||
|
@ -40,11 +40,11 @@ require (
|
|||
github.com/vmihailenco/msgpack/v5 v5.3.5
|
||||
github.com/yosssi/ace v0.0.5
|
||||
go.etcd.io/bbolt v1.3.6
|
||||
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab
|
||||
golang.org/x/text v0.3.7
|
||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
|
||||
google.golang.org/protobuf v1.28.1
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
|
@ -81,7 +81,7 @@ require (
|
|||
github.com/minio/highwayhash v1.0.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nats-io/jwt/v2 v2.2.0 // indirect
|
||||
github.com/nats-io/jwt/v2 v2.3.0 // indirect
|
||||
github.com/nats-io/nats.go v1.16.0 // indirect
|
||||
github.com/nats-io/nkeys v0.3.0 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
|
|
23
go.sum
generated
23
go.sum
generated
|
@ -9,8 +9,8 @@ github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=
|
|||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Shopify/goreferrer v0.0.0-20210630161223-536fa16abd6f h1:XeOBnoBP7K19tMBEKeUo1NOxOO+h5FFi2HGzQvvkb44=
|
||||
github.com/Shopify/goreferrer v0.0.0-20210630161223-536fa16abd6f/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0=
|
||||
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
|
@ -150,8 +150,8 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OH
|
|||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/nats-io/jwt/v2 v2.2.0 h1:Yg/4WFK6vsqMudRg91eBb7Dh6XeVcDMPHycDE8CfltE=
|
||||
github.com/nats-io/jwt/v2 v2.2.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=
|
||||
github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI=
|
||||
github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k=
|
||||
github.com/nats-io/nats-server/v2 v2.8.4 h1:0jQzze1T9mECg8YZEl8+WYUXb9JKluJfCBriPUtluB4=
|
||||
github.com/nats-io/nats.go v1.16.0 h1:zvLE7fGBQYW6MWaFaRdsgm9qT39PJDQoju+DS8KsO1g=
|
||||
github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
|
||||
|
@ -245,13 +245,13 @@ go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
|||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8=
|
||||
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -266,15 +266,16 @@ golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w=
|
||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
|
@ -47,7 +47,14 @@ func (g GRPC) Apply(c *ControllerActivator) {
|
|||
return
|
||||
}
|
||||
|
||||
// If strict was false, allow common HTTP clients, consumes and produces JSON.
|
||||
// If strict was true fires 404 on common HTTP clients.
|
||||
if g.Strict {
|
||||
ctx.NotFound()
|
||||
ctx.StopExecution()
|
||||
return
|
||||
}
|
||||
|
||||
// Allow common HTTP clients, consumes and produces JSON.
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ type Config struct {
|
|||
Addr string
|
||||
// Clusters a list of network addresses for clusters.
|
||||
// If not empty "Addr" is ignored and Redis clusters feature is used instead.
|
||||
// Note that this field is ignored when setgging a custom `GoRedisClient`.
|
||||
Clusters []string
|
||||
// Use the specified Username to authenticate the current connection
|
||||
// with one of the connections defined in the ACL list when connecting
|
||||
|
@ -267,7 +268,8 @@ func (db *Database) Delete(sid string, key string) (deleted bool) {
|
|||
|
||||
// Clear removes all session key values but it keeps the session entry.
|
||||
func (db *Database) Clear(sid string) error {
|
||||
keys := db.keys(db.makeSID(sid))
|
||||
sid = db.makeSID(sid)
|
||||
keys := db.keys(sid)
|
||||
for _, key := range keys {
|
||||
if key == SessionIDKey {
|
||||
continue
|
||||
|
|
|
@ -27,7 +27,9 @@ type GoRedisClient interface {
|
|||
// for the go-redis redis driver. See driver.go file.
|
||||
type GoRedisDriver struct {
|
||||
// Both Client and ClusterClient implements this interface.
|
||||
client GoRedisClient
|
||||
// Custom one can be directly passed but if so, the
|
||||
// Connect method does nothing (so all connection and client settings are ignored).
|
||||
Client GoRedisClient
|
||||
// Customize any go-redis fields manually
|
||||
// before Connect.
|
||||
ClientOptions Options
|
||||
|
@ -111,12 +113,24 @@ func (r *GoRedisDriver) mergeClusterOptions(c Config) *ClusterOptions {
|
|||
return &opts
|
||||
}
|
||||
|
||||
// SetClient sets an existing go redis client to the sessions redis driver.
|
||||
//
|
||||
// Returns itself.
|
||||
func (r *GoRedisDriver) SetClient(goRedisClient GoRedisClient) *GoRedisDriver {
|
||||
r.Client = goRedisClient
|
||||
return r
|
||||
}
|
||||
|
||||
// Connect initializes the redis client.
|
||||
func (r *GoRedisDriver) Connect(c Config) error {
|
||||
if r.Client != nil { // if a custom one was given through SetClient.
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(c.Clusters) > 0 {
|
||||
r.client = redis.NewClusterClient(r.mergeClusterOptions(c))
|
||||
r.Client = redis.NewClusterClient(r.mergeClusterOptions(c))
|
||||
} else {
|
||||
r.client = redis.NewClient(r.mergeClientOptions(c))
|
||||
r.Client = redis.NewClient(r.mergeClientOptions(c))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -125,29 +139,29 @@ func (r *GoRedisDriver) Connect(c Config) error {
|
|||
// PingPong sends a ping message and reports whether
|
||||
// the PONG message received successfully.
|
||||
func (r *GoRedisDriver) PingPong() (bool, error) {
|
||||
pong, err := r.client.Ping(defaultContext).Result()
|
||||
pong, err := r.Client.Ping(defaultContext).Result()
|
||||
return pong == "PONG", err
|
||||
}
|
||||
|
||||
// CloseConnection terminates the underline redis connection.
|
||||
func (r *GoRedisDriver) CloseConnection() error {
|
||||
return r.client.Close()
|
||||
return r.Client.Close()
|
||||
}
|
||||
|
||||
// Set stores a "value" based on the session's "key".
|
||||
// The value should be type of []byte, so unmarshal can happen.
|
||||
func (r *GoRedisDriver) Set(sid, key string, value interface{}) error {
|
||||
return r.client.HSet(defaultContext, sid, key, value).Err()
|
||||
return r.Client.HSet(defaultContext, sid, key, value).Err()
|
||||
}
|
||||
|
||||
// Get returns the associated value of the session's given "key".
|
||||
func (r *GoRedisDriver) Get(sid, key string) (interface{}, error) {
|
||||
return r.client.HGet(defaultContext, sid, key).Bytes()
|
||||
return r.Client.HGet(defaultContext, sid, key).Bytes()
|
||||
}
|
||||
|
||||
// Exists reports whether a session exists or not.
|
||||
func (r *GoRedisDriver) Exists(sid string) bool {
|
||||
n, err := r.client.Exists(defaultContext, sid).Result()
|
||||
n, err := r.Client.Exists(defaultContext, sid).Result()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
@ -157,7 +171,7 @@ func (r *GoRedisDriver) Exists(sid string) bool {
|
|||
|
||||
// TTL returns any TTL value of the session.
|
||||
func (r *GoRedisDriver) TTL(sid string) time.Duration {
|
||||
dur, err := r.client.TTL(defaultContext, sid).Result()
|
||||
dur, err := r.Client.TTL(defaultContext, sid).Result()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
@ -167,29 +181,29 @@ func (r *GoRedisDriver) TTL(sid string) time.Duration {
|
|||
|
||||
// UpdateTTL sets expiration duration of the session.
|
||||
func (r *GoRedisDriver) UpdateTTL(sid string, newLifetime time.Duration) error {
|
||||
_, err := r.client.Expire(defaultContext, sid, newLifetime).Result()
|
||||
_, err := r.Client.Expire(defaultContext, sid, newLifetime).Result()
|
||||
return err
|
||||
}
|
||||
|
||||
// GetAll returns all the key values under the session.
|
||||
func (r *GoRedisDriver) GetAll(sid string) (map[string]string, error) {
|
||||
return r.client.HGetAll(defaultContext, sid).Result()
|
||||
return r.Client.HGetAll(defaultContext, sid).Result()
|
||||
}
|
||||
|
||||
// GetKeys returns all keys under the session.
|
||||
func (r *GoRedisDriver) GetKeys(sid string) ([]string, error) {
|
||||
return r.client.HKeys(defaultContext, sid).Result()
|
||||
return r.Client.HKeys(defaultContext, sid).Result()
|
||||
}
|
||||
|
||||
// Len returns the total length of key-values of the session.
|
||||
func (r *GoRedisDriver) Len(sid string) int {
|
||||
return int(r.client.HLen(defaultContext, sid).Val())
|
||||
return int(r.Client.HLen(defaultContext, sid).Val())
|
||||
}
|
||||
|
||||
// Delete removes a value from the redis store.
|
||||
func (r *GoRedisDriver) Delete(sid, key string) error {
|
||||
if key == "" {
|
||||
return r.client.Del(defaultContext, sid).Err()
|
||||
return r.Client.Del(defaultContext, sid).Err()
|
||||
}
|
||||
return r.client.HDel(defaultContext, sid, key).Err()
|
||||
return r.Client.HDel(defaultContext, sid, key).Err()
|
||||
}
|
||||
|
|
|
@ -1,18 +1,34 @@
|
|||
package jsonx
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var fixedEastUTCLocations = make(map[int]*time.Location)
|
||||
|
||||
func registerFixedEastUTCLocation(name string, secondsFromUTC int) {
|
||||
loc := time.FixedZone(name, secondsFromUTC)
|
||||
fixedEastUTCLocations[secondsFromUTC] = loc
|
||||
}
|
||||
|
||||
func init() {
|
||||
registerFixedEastUTCLocation("EEST", 3*60*60) // + 3 hours.
|
||||
}
|
||||
|
||||
const (
|
||||
// ISO8601Layout holds the time layout for the the javascript iso time.
|
||||
// Read more at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString.
|
||||
ISO8601Layout = "2006-01-02T15:04:05"
|
||||
// ISO8601ZLayout same as ISO8601Layout but with the timezone suffix.
|
||||
ISO8601ZLayout = "2006-01-02T15:04:05Z"
|
||||
// ISO8601ZUTCOffsetLayout ISO 8601 format, with full time and zone with UTC offset.
|
||||
// Example: 2022-08-10T03:21:00.000000+03:00, 2022-08-09T00:00:00.000000.
|
||||
ISO8601ZUTCOffsetLayout = "2006-01-02T15:04:05.999999Z07:00"
|
||||
)
|
||||
|
||||
// ISO8601 describes a time compatible with javascript time format.
|
||||
|
@ -29,7 +45,28 @@ func ParseISO8601(s string) (ISO8601, error) {
|
|||
err error
|
||||
)
|
||||
|
||||
if s[len(s)-1] == 'Z' {
|
||||
if idx := strings.LastIndexFunc(s, startUTCOffsetIndexFunc); idx > 20 /* should have some distance, e.g. 26 */ {
|
||||
length := parseSignedOffset(s[idx:])
|
||||
|
||||
if idx+1 > idx+length || len(s) <= idx+length+1 {
|
||||
return ISO8601{}, fmt.Errorf("ISO8601: invalid timezone format: %s", s[idx:])
|
||||
}
|
||||
|
||||
offsetText := s[idx+1 : idx+length]
|
||||
offset, parseErr := strconv.Atoi(offsetText)
|
||||
if parseErr != nil {
|
||||
return ISO8601{}, err
|
||||
}
|
||||
|
||||
// E.g. offset of +0300 is returned as 10800 which is - (3 * 60 * 60).
|
||||
secondsEastUTC := offset * 60 * 60
|
||||
|
||||
if loc, ok := fixedEastUTCLocations[secondsEastUTC]; ok { // Specific (fixed) zone.
|
||||
tt, err = time.ParseInLocation(ISO8601ZUTCOffsetLayout, s, loc)
|
||||
} else { // Local or UTC.
|
||||
tt, err = time.Parse(ISO8601ZUTCOffsetLayout, s)
|
||||
}
|
||||
} else if s[len(s)-1] == 'Z' {
|
||||
tt, err = time.Parse(ISO8601ZLayout, s)
|
||||
} else {
|
||||
tt, err = time.Parse(ISO8601Layout, s)
|
||||
|
@ -39,7 +76,7 @@ func ParseISO8601(s string) (ISO8601, error) {
|
|||
return ISO8601{}, err
|
||||
}
|
||||
|
||||
return ISO8601(tt.UTC()), nil
|
||||
return ISO8601(tt), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON parses the "b" into ISO8601 time.
|
||||
|
@ -90,6 +127,11 @@ func (t ISO8601) String() string {
|
|||
return tt.Format(ISO8601Layout)
|
||||
}
|
||||
|
||||
// Value returns the database value of time.Time.
|
||||
func (t ISO8601) Value() (driver.Value, error) {
|
||||
return time.Time(t), nil
|
||||
}
|
||||
|
||||
// Scan completes the sql driver.Scanner interface.
|
||||
func (t *ISO8601) Scan(src interface{}) error {
|
||||
switch v := src.(type) {
|
||||
|
@ -104,6 +146,8 @@ func (t *ISO8601) Scan(src interface{}) error {
|
|||
return err
|
||||
}
|
||||
*t = tt
|
||||
case []byte:
|
||||
return t.Scan(string(v))
|
||||
case nil:
|
||||
*t = ISO8601(time.Time{})
|
||||
default:
|
||||
|
@ -112,3 +156,54 @@ func (t *ISO8601) Scan(src interface{}) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseSignedOffset parses a signed timezone offset (e.g. "+03" or "-04").
|
||||
// The function checks for a signed number in the range -23 through +23 excluding zero.
|
||||
// Returns length of the found offset string or 0 otherwise.
|
||||
//
|
||||
// Language internal function.
|
||||
func parseSignedOffset(value string) int {
|
||||
sign := value[0]
|
||||
if sign != '-' && sign != '+' {
|
||||
return 0
|
||||
}
|
||||
x, rem, err := leadingInt(value[1:])
|
||||
|
||||
// fail if nothing consumed by leadingInt
|
||||
if err != nil || value[1:] == rem {
|
||||
return 0
|
||||
}
|
||||
if x > 23 {
|
||||
return 0
|
||||
}
|
||||
return len(value) - len(rem)
|
||||
}
|
||||
|
||||
var errLeadingInt = errors.New("ISO8601: time: bad [0-9]*") // never printed.
|
||||
|
||||
// leadingInt consumes the leading [0-9]* from s.
|
||||
//
|
||||
// Language internal function.
|
||||
func leadingInt(s string) (x uint64, rem string, err error) {
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if c < '0' || c > '9' {
|
||||
break
|
||||
}
|
||||
if x > 1<<63/10 {
|
||||
// overflow
|
||||
return 0, "", errLeadingInt
|
||||
}
|
||||
x = x*10 + uint64(c) - '0'
|
||||
if x > 1<<63 {
|
||||
// overflow
|
||||
return 0, "", errLeadingInt
|
||||
}
|
||||
}
|
||||
return x, s[i:], nil
|
||||
}
|
||||
|
||||
func startUTCOffsetIndexFunc(char rune) bool {
|
||||
return char == '+' || char == '-'
|
||||
}
|
||||
|
|
81
x/jsonx/iso8601_test.go
Normal file
81
x/jsonx/iso8601_test.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
package jsonx
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestISO8601(t *testing.T) {
|
||||
data := `{"start": "2021-08-20T10:05:01", "end": "2021-12-01T17:05:06", "nothing": null, "empty": ""}`
|
||||
v := struct {
|
||||
Start ISO8601 `json:"start"`
|
||||
End ISO8601 `json:"end"`
|
||||
Nothing ISO8601 `json:"nothing"`
|
||||
Empty ISO8601 `json:"empty"`
|
||||
}{}
|
||||
err := json.Unmarshal([]byte(data), &v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !v.Nothing.IsZero() {
|
||||
t.Fatalf("expected 'nothing' to be zero but got: %v", v.Nothing)
|
||||
}
|
||||
|
||||
if !v.Empty.IsZero() {
|
||||
t.Fatalf("expected 'empty' to be zero but got: %v", v.Empty)
|
||||
}
|
||||
|
||||
loc := time.UTC
|
||||
|
||||
if expected, got := time.Date(2021, time.August, 20, 10, 5, 1, 0, loc), v.Start.ToTime(); expected != got {
|
||||
t.Fatalf("expected 'start' to be: %v but got: %v", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := time.Date(2021, time.December, 1, 17, 5, 6, 0, loc), v.End.ToTime(); expected != got {
|
||||
t.Fatalf("expected 'start' to be: %v but got: %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestISO8601WithZoneUTCOffset(t *testing.T) {
|
||||
data := `{"start": "2022-08-10T03:21:00.000000+03:00", "end": "2022-08-10T09:49:00.000000+03:00", "nothing": null, "empty": ""}`
|
||||
v := struct {
|
||||
Start ISO8601 `json:"start"`
|
||||
End ISO8601 `json:"end"`
|
||||
Nothing ISO8601 `json:"nothing"`
|
||||
Empty ISO8601 `json:"empty"`
|
||||
}{}
|
||||
err := json.Unmarshal([]byte(data), &v)
|
||||
if err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
|
||||
// t.Logf("Start: %s, location: %s\n", v.Start.String(), v.Start.ToTime().Location().String())
|
||||
|
||||
if !v.Nothing.IsZero() {
|
||||
t.Fatalf("expected 'nothing' to be zero but got: %v", v.Nothing)
|
||||
}
|
||||
|
||||
if !v.Empty.IsZero() {
|
||||
t.Fatalf("expected 'empty' to be zero but got: %v", v.Empty)
|
||||
}
|
||||
|
||||
loc := time.FixedZone("EEST", 10800)
|
||||
|
||||
if expected, got := time.Date(2022, time.August, 10, 3, 21, 0, 0, loc).String(), v.Start.ToTime().String(); expected != got {
|
||||
t.Fatalf("expected 'start' string to be: %v but got: %v", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := time.Date(2022, time.August, 10, 9, 49, 0, 0, loc).String(), v.End.ToTime().String(); expected != got {
|
||||
t.Fatalf("expected 'start' string to be: %v but got: %v", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := time.Date(2022, time.August, 10, 3, 21, 0, 0, loc), v.Start.ToTime().In(loc); expected != got {
|
||||
t.Fatalf("expected 'start' to be: %v but got: %v", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := time.Date(2022, time.August, 10, 9, 49, 0, 0, loc), v.End.ToTime().In(loc); expected != got {
|
||||
t.Fatalf("expected 'start' to be: %v but got: %v", expected, got)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user