Merge master and develop
This commit is contained in:
commit
3acc858909
12
.deepsource.toml
Normal file
12
.deepsource.toml
Normal file
@ -0,0 +1,12 @@
|
||||
version = 1
|
||||
|
||||
test_patterns = ["**/*_test.go"]
|
||||
|
||||
exclude_patterns = ["scripts/**"]
|
||||
|
||||
[[analyzers]]
|
||||
name = "go"
|
||||
enabled = true
|
||||
|
||||
[analyzers.meta]
|
||||
import_paths = ["github.com/beego/beego"]
|
||||
34
.github/workflows/changelog.yml
vendored
Normal file
34
.github/workflows/changelog.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
# This action requires that any PR targeting the master branch should touch at
|
||||
# least one CHANGELOG file. If a CHANGELOG entry is not required, add the "Skip
|
||||
# Changelog" label to disable this action.
|
||||
|
||||
name: changelog
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||
branches:
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
changelog:
|
||||
runs-on: ubuntu-latest
|
||||
if: "!contains(github.event.pull_request.labels.*.name, 'Skip Changelog')"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Check for CHANGELOG changes
|
||||
run: |
|
||||
# Only the latest commit of the feature branch is available
|
||||
# automatically. To diff with the base branch, we need to
|
||||
# fetch that too (and we only need its latest commit).
|
||||
git fetch origin ${{ github.base_ref }} --depth=1
|
||||
if [[ $(git diff --name-only FETCH_HEAD | grep CHANGELOG) ]]
|
||||
then
|
||||
echo "A CHANGELOG was modified. Looks good!"
|
||||
else
|
||||
echo "No CHANGELOG was modified."
|
||||
echo "Please add a CHANGELOG entry, or add the \"Skip Changelog\" label if not required."
|
||||
false
|
||||
fi
|
||||
32
.github/workflows/golangci-lint.yml
vendored
Normal file
32
.github/workflows/golangci-lint.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: golangci-lint
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
pull_request:
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||
version: v1.29
|
||||
|
||||
# Optional: working directory, useful for monorepos
|
||||
# working-directory: ./
|
||||
|
||||
# Optional: golangci-lint command line arguments.
|
||||
args: --timeout=5m --print-issued-lines=true --print-linter-name=true --uniq-by-line=true
|
||||
|
||||
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
||||
only-new-issues: true
|
||||
|
||||
# Optional: if set to true then the action will use pre-installed Go
|
||||
# skip-go-installation: true
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -10,3 +10,5 @@ _beeTmp2/
|
||||
pkg/_beeTmp/
|
||||
pkg/_beeTmp2/
|
||||
test/tmp/
|
||||
|
||||
profile.out
|
||||
|
||||
28
.travis.yml
28
.travis.yml
@ -16,6 +16,7 @@ env:
|
||||
- ORM_DRIVER=postgres ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable"
|
||||
- ORM_DRIVER=mysql export ORM_SOURCE="root:@/orm_test?charset=utf8"
|
||||
before_install:
|
||||
- export CODECOV_TOKEN="4f4bc484-32a8-43b7-9f48-20966bd48ceb"
|
||||
# link the local repo with ${GOPATH}/src/<namespace>/<repo>
|
||||
- GO_REPO_NAMESPACE=${GO_REPO_FULLNAME%/*}
|
||||
# relies on GOPATH to contain only one directory...
|
||||
@ -55,30 +56,9 @@ before_install:
|
||||
- docker exec etcd-gcr-v3.3.25 /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl put current.serialize.name test"
|
||||
- docker exec etcd-gcr-v3.3.25 /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl put sub.sub.key1 sub.sub.key"
|
||||
install:
|
||||
- go get github.com/lib/pq
|
||||
- go get github.com/go-sql-driver/mysql
|
||||
- go get github.com/mattn/go-sqlite3
|
||||
- go get github.com/bradfitz/gomemcache/memcache
|
||||
- go get github.com/gomodule/redigo/redis
|
||||
- go get github.com/beego/x2j
|
||||
- go get github.com/couchbase/go-couchbase
|
||||
- go get github.com/beego/goyaml2
|
||||
- go get gopkg.in/yaml.v2
|
||||
- go get github.com/belogik/goes
|
||||
- go get github.com/ledisdb/ledisdb
|
||||
- go get github.com/ssdb/gossdb/ssdb
|
||||
- go get github.com/cloudflare/golz4
|
||||
- go get github.com/gogo/protobuf/proto
|
||||
- go get github.com/Knetic/govaluate
|
||||
- go get github.com/casbin/casbin
|
||||
- go get github.com/elazarl/go-bindata-assetfs
|
||||
- go get github.com/OwnLocal/goes
|
||||
- go get github.com/shiena/ansicolor
|
||||
- go get -u honnef.co/go/tools/cmd/staticcheck
|
||||
- go get -u github.com/mdempsky/unconvert
|
||||
- go get -u github.com/gordonklaus/ineffassign
|
||||
- go get -u golang.org/x/lint/golint
|
||||
- go get -u github.com/go-redis/redis
|
||||
before_script:
|
||||
|
||||
# -
|
||||
@ -87,19 +67,19 @@ before_script:
|
||||
- sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi"
|
||||
- sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi"
|
||||
- sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi"
|
||||
- sh -c "go get github.com/golang/lint/golint; golint ./...;"
|
||||
- sh -c "go list ./... | grep -v vendor | xargs go vet -v"
|
||||
- mkdir -p res/var
|
||||
- ./ssdb/ssdb-server ./ssdb/ssdb.conf -d
|
||||
after_script:
|
||||
- killall -w ssdb-server
|
||||
- rm -rf ./res/var/*
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
script:
|
||||
- go test ./...
|
||||
- GO111MODULE=on go test -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
- staticcheck -show-ignored -checks "-ST1017,-U1000,-ST1005,-S1034,-S1012,-SA4006,-SA6005,-SA1019,-SA1024" ./
|
||||
- unconvert $(go list ./... | grep -v /vendor/)
|
||||
- ineffassign .
|
||||
- find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s
|
||||
- golint ./...
|
||||
addons:
|
||||
postgresql: "9.6"
|
||||
|
||||
33
CHANGELOG.md
33
CHANGELOG.md
@ -1,9 +1,40 @@
|
||||
# developing
|
||||
- Error codes definition of cache module. [4493](https://github.com/beego/beego/pull/4493)
|
||||
- Remove generateCommentRoute http hook. Using `bee generate routers` commands instead.[4486](https://github.com/beego/beego/pull/4486) [bee PR 762](https://github.com/beego/bee/pull/762)
|
||||
- Fix: /abc.html/aaa match /abc/aaa. [4459](https://github.com/beego/beego/pull/4459)
|
||||
- ORM mock. [4407](https://github.com/beego/beego/pull/4407)
|
||||
- Add sonar check and ignore test. [4432](https://github.com/beego/beego/pull/4432) [4433](https://github.com/beego/beego/pull/4433)
|
||||
- Update changlog.yml to check every PR to develop branch.[4427](https://github.com/beego/beego/pull/4427)
|
||||
- Fix 4396: Add context.param module into adapter. [4398](https://github.com/beego/beego/pull/4398)
|
||||
- Support `RollbackUnlessCommit` API. [4542](https://github.com/beego/beego/pull/4542)
|
||||
- Fix 4503 and 4504: Add `when` to `Write([]byte)` method and add `prefix` to `writeMsg`. [4507](https://github.com/beego/beego/pull/4507)
|
||||
- Fix 4480: log format incorrect. [4482](https://github.com/beego/beego/pull/4482)
|
||||
- Remove `duration` from prometheus labels. [4391](https://github.com/beego/beego/pull/4391)
|
||||
- Fix `unknown escape sequence` in generated code. [4385](https://github.com/beego/beego/pull/4385)
|
||||
- Using fixed name `commentRouter.go` as generated file name. [4385](https://github.com/beego/beego/pull/4385)
|
||||
- Fix 4383: ORM Adapter produces panic when using orm.RegisterModelWithPrefix. [4386](https://github.com/beego/beego/pull/4386)
|
||||
- Support 4144: Add new api for order by for supporting multiple way to query [4294](https://github.com/beego/beego/pull/4294)
|
||||
- Support session Filter chain. [4404](https://github.com/beego/beego/pull/4404)
|
||||
- Feature issue #4402 finish router get example. [4416](https://github.com/beego/beego/pull/4416)
|
||||
- Implement context.Context support and deprecate `QueryM2MWithCtx` and `QueryTableWithCtx` [4424](https://github.com/beego/beego/pull/4424)
|
||||
- Finish timeout option for tasks #4441 [4441](https://github.com/beego/beego/pull/4441)
|
||||
- Error Module brief design & using httplib module to validate this design. [4453](https://github.com/beego/beego/pull/4453)
|
||||
- Fix 4444: panic when 404 not found. [4446](https://github.com/beego/beego/pull/4446)
|
||||
- Fix 4435: fix panic when controller dir not found. [4452](https://github.com/beego/beego/pull/4452)
|
||||
- Fix 4435: fix panic when controller dir not found. [4452](https://github.com/beego/beego/pull/4452)
|
||||
- Fix 4456: Fix router method expression [4456](https://github.com/beego/beego/pull/4456)
|
||||
- Remove some `go get` lines in `.travis.yml` file [4469](https://github.com/beego/beego/pull/4469)
|
||||
- Fix 4451: support QueryExecutor interface. [4461](https://github.com/beego/beego/pull/4461)
|
||||
- Add some testing scripts [4461](https://github.com/beego/beego/pull/4461)
|
||||
- Refactor httplib: Move debug code to a filter [4440](https://github.com/beego/beego/issues/4440)
|
||||
- fix: code quality issues [4513](https://github.com/beego/beego/pull/4513)
|
||||
- Optimize maligned structs to reduce memory foot-print [4525](https://github.com/beego/beego/pull/4525)
|
||||
- Feat: add token bucket ratelimit filter [4508](https://github.com/beego/beego/pull/4508)
|
||||
- Improve: Avoid ignoring mistakes that need attention [4548](https://github.com/beego/beego/pull/4548)
|
||||
- Integration: DeepSource [4560](https://github.com/beego/beego/pull/4560)
|
||||
|
||||
|
||||
|
||||
## Fix Sonar
|
||||
- [4473](https://github.com/beego/beego/pull/4473)
|
||||
- [4474](https://github.com/beego/beego/pull/4474)
|
||||
- [4479](https://github.com/beego/beego/pull/4479)
|
||||
|
||||
@ -36,7 +36,7 @@ We provide docker compose file to start all middlewares.
|
||||
You can run:
|
||||
|
||||
```shell script
|
||||
docker-compose -f scripts/test_docker_compose.yml up -d
|
||||
docker-compose -f scripts/test_docker_compose.yaml up -d
|
||||
```
|
||||
|
||||
Unit tests read addresses from environment, here is an example:
|
||||
@ -53,7 +53,7 @@ export SSDB_ADDR="192.168.0.105:8888"
|
||||
|
||||
### Pull requests
|
||||
|
||||
First of all. beego follow the gitflow. So please send you pull request to **develop-2** branch. We will close the pull
|
||||
First of all. beego follow the gitflow. So please send you pull request to **develop** branch. We will close the pull
|
||||
request to master branch.
|
||||
|
||||
We are always happy to receive pull requests, and do our best to review them as fast as possible. Not sure if that typo
|
||||
|
||||
5
ERROR_SPECIFICATION.md
Normal file
5
ERROR_SPECIFICATION.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Error Module
|
||||
|
||||
## Module code
|
||||
- httplib 1
|
||||
- cache 2
|
||||
@ -1,4 +1,4 @@
|
||||
# Beego [](https://travis-ci.org/astaxie/beego) [](http://godoc.org/github.com/beego/beego/v2) [](http://golangfoundation.org) [](https://goreportcard.com/report/github.com/beego/beego/v2)
|
||||
# Beego [](https://travis-ci.org/beego/beego) [](http://godoc.org/github.com/beego/beego) [](http://golangfoundation.org) [](https://goreportcard.com/report/github.com/beego/beego)
|
||||
|
||||
Beego is used for rapid development of enterprise application in Go, including RESTful APIs, web apps and backend
|
||||
services.
|
||||
@ -19,7 +19,7 @@ Beego is compos of four parts:
|
||||
|
||||
## Quick Start
|
||||
|
||||
[Officail website](http://beego.me)
|
||||
[Official website](http://beego.me)
|
||||
|
||||
[Example](https://github.com/beego/beego-example)
|
||||
|
||||
@ -40,7 +40,7 @@ Beego is compos of four parts:
|
||||
|
||||
#### Download and install
|
||||
|
||||
go get github.com/beego/beego/v2@v2.0.0
|
||||
go get github.com/beego/beego/v2@latest
|
||||
|
||||
#### Create file `hello.go`
|
||||
|
||||
@ -90,8 +90,7 @@ Congratulations! You've just built your first **beego** app.
|
||||
## Community
|
||||
|
||||
* [http://beego.me/community](http://beego.me/community)
|
||||
* Welcome to join us in Slack: [https://beego.slack.com](https://beego.slack.com), you can get invited
|
||||
from [here](https://github.com/beego/beedoc/issues/232)
|
||||
* Welcome to join us in Slack: [https://beego.slack.com invite](https://join.slack.com/t/beego/shared_invite/zt-fqlfjaxs-_CRmiITCSbEqQG9NeBqXKA),
|
||||
* QQ Group Group ID:523992905
|
||||
* [Contribution Guide](https://github.com/beego/beedoc/blob/master/en-US/intro/contributing.md).
|
||||
|
||||
|
||||
2
adapter/cache/cache.go
vendored
2
adapter/cache/cache.go
vendored
@ -16,7 +16,7 @@
|
||||
// Usage:
|
||||
//
|
||||
// import(
|
||||
// "github.com/beego/beego/v2/cache"
|
||||
// "github.com/beego/beego/v2/client/cache"
|
||||
// )
|
||||
//
|
||||
// bm, err := cache.NewCache("memory", `{"interval":60}`)
|
||||
|
||||
194
adapter/cache/cache_test.go
vendored
194
adapter/cache/cache_test.go
vendored
@ -19,12 +19,22 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
initError = "init err"
|
||||
setError = "set Error"
|
||||
checkError = "check err"
|
||||
getError = "get err"
|
||||
getMultiError = "GetMulti Error"
|
||||
)
|
||||
|
||||
func TestCacheIncr(t *testing.T) {
|
||||
bm, err := NewCache("memory", `{"interval":20}`)
|
||||
if err != nil {
|
||||
t.Error("init err")
|
||||
t.Error(initError)
|
||||
}
|
||||
// timeoutDuration := 10 * time.Second
|
||||
|
||||
@ -45,147 +55,95 @@ func TestCacheIncr(t *testing.T) {
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
bm, err := NewCache("memory", `{"interval":20}`)
|
||||
if err != nil {
|
||||
t.Error("init err")
|
||||
}
|
||||
timeoutDuration := 10 * time.Second
|
||||
if err = bm.Put("astaxie", 1, timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie") {
|
||||
t.Error("check err")
|
||||
}
|
||||
|
||||
if v := bm.Get("astaxie"); v.(int) != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
|
||||
time.Sleep(30 * time.Second)
|
||||
timeoutDuration := 5 * time.Second
|
||||
err = bm.Put("astaxie", 1, timeoutDuration)
|
||||
assert.Nil(t, err)
|
||||
|
||||
if bm.IsExist("astaxie") {
|
||||
t.Error("check err")
|
||||
}
|
||||
assert.True(t, bm.IsExist("astaxie"))
|
||||
|
||||
if err = bm.Put("astaxie", 1, timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
assert.Equal(t, 1, bm.Get("astaxie"))
|
||||
|
||||
if err = bm.Incr("astaxie"); err != nil {
|
||||
t.Error("Incr Error", err)
|
||||
}
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
if v := bm.Get("astaxie"); v.(int) != 2 {
|
||||
t.Error("get err")
|
||||
}
|
||||
assert.False(t, bm.IsExist("astaxie"))
|
||||
|
||||
if err = bm.Decr("astaxie"); err != nil {
|
||||
t.Error("Decr Error", err)
|
||||
}
|
||||
err = bm.Put("astaxie", 1, timeoutDuration)
|
||||
assert.Nil(t, err)
|
||||
|
||||
if v := bm.Get("astaxie"); v.(int) != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
bm.Delete("astaxie")
|
||||
if bm.IsExist("astaxie") {
|
||||
t.Error("delete err")
|
||||
}
|
||||
err = bm.Incr("astaxie")
|
||||
assert.Nil(t, err)
|
||||
|
||||
// test GetMulti
|
||||
if err = bm.Put("astaxie", "author", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie") {
|
||||
t.Error("check err")
|
||||
}
|
||||
if v := bm.Get("astaxie"); v.(string) != "author" {
|
||||
t.Error("get err")
|
||||
}
|
||||
assert.Equal(t, 2, bm.Get("astaxie"))
|
||||
|
||||
if err = bm.Put("astaxie1", "author1", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie1") {
|
||||
t.Error("check err")
|
||||
}
|
||||
assert.Nil(t, bm.Decr("astaxie"))
|
||||
|
||||
assert.Equal(t, 1, bm.Get("astaxie"))
|
||||
|
||||
assert.Nil(t, bm.Delete("astaxie"))
|
||||
|
||||
assert.False(t, bm.IsExist("astaxie"))
|
||||
|
||||
assert.Nil(t, bm.Put("astaxie", "author", timeoutDuration))
|
||||
|
||||
assert.True(t, bm.IsExist("astaxie"))
|
||||
|
||||
assert.Equal(t, "author", bm.Get("astaxie"))
|
||||
|
||||
assert.Nil(t, bm.Put("astaxie1", "author1", timeoutDuration))
|
||||
|
||||
assert.True(t, bm.IsExist("astaxie1"))
|
||||
|
||||
vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[0].(string) != "author" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[1].(string) != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
|
||||
assert.Equal(t, 2, len(vv))
|
||||
|
||||
|
||||
assert.Equal(t, "author", vv[0])
|
||||
|
||||
assert.Equal(t, "author1", vv[1])
|
||||
}
|
||||
|
||||
func TestFileCache(t *testing.T) {
|
||||
bm, err := NewCache("file", `{"CachePath":"cache","FileSuffix":".bin","DirectoryLevel":"2","EmbedExpiry":"0"}`)
|
||||
if err != nil {
|
||||
t.Error("init err")
|
||||
}
|
||||
timeoutDuration := 10 * time.Second
|
||||
if err = bm.Put("astaxie", 1, timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie") {
|
||||
t.Error("check err")
|
||||
}
|
||||
|
||||
if v := bm.Get("astaxie"); v.(int) != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
timeoutDuration := 5 * time.Second
|
||||
|
||||
if err = bm.Incr("astaxie"); err != nil {
|
||||
t.Error("Incr Error", err)
|
||||
}
|
||||
assert.Nil(t, bm.Put("astaxie", 1, timeoutDuration))
|
||||
|
||||
if v := bm.Get("astaxie"); v.(int) != 2 {
|
||||
t.Error("get err")
|
||||
}
|
||||
assert.True(t, bm.IsExist("astaxie"))
|
||||
|
||||
if err = bm.Decr("astaxie"); err != nil {
|
||||
t.Error("Decr Error", err)
|
||||
}
|
||||
assert.Equal(t, 1, bm.Get("astaxie"))
|
||||
|
||||
if v := bm.Get("astaxie"); v.(int) != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
bm.Delete("astaxie")
|
||||
if bm.IsExist("astaxie") {
|
||||
t.Error("delete err")
|
||||
}
|
||||
assert.Nil(t, bm.Incr("astaxie"))
|
||||
|
||||
// test string
|
||||
if err = bm.Put("astaxie", "author", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie") {
|
||||
t.Error("check err")
|
||||
}
|
||||
if v := bm.Get("astaxie"); v.(string) != "author" {
|
||||
t.Error("get err")
|
||||
}
|
||||
assert.Equal(t, 2, bm.Get("astaxie"))
|
||||
|
||||
// test GetMulti
|
||||
if err = bm.Put("astaxie1", "author1", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie1") {
|
||||
t.Error("check err")
|
||||
}
|
||||
assert.Nil(t, bm.Decr("astaxie"))
|
||||
|
||||
assert.Equal(t, 1, bm.Get("astaxie"))
|
||||
assert.Nil(t, bm.Delete("astaxie"))
|
||||
|
||||
assert.False(t, bm.IsExist("astaxie"))
|
||||
|
||||
assert.Nil(t, bm.Put("astaxie", "author", timeoutDuration))
|
||||
|
||||
assert.True(t, bm.IsExist("astaxie"))
|
||||
|
||||
assert.Equal(t, "author", bm.Get("astaxie"))
|
||||
|
||||
assert.Nil(t, bm.Put("astaxie1", "author1", timeoutDuration))
|
||||
|
||||
assert.True(t, bm.IsExist("astaxie1"))
|
||||
|
||||
vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[0].(string) != "author" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[1].(string) != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
|
||||
os.RemoveAll("cache")
|
||||
assert.Equal(t, 2, len(vv))
|
||||
|
||||
assert.Equal(t, "author", vv[0])
|
||||
assert.Equal(t, "author1", vv[1])
|
||||
assert.Nil(t, os.RemoveAll("cache"))
|
||||
}
|
||||
|
||||
4
adapter/cache/memcache/memcache.go
vendored
4
adapter/cache/memcache/memcache.go
vendored
@ -20,8 +20,8 @@
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/beego/beego/v2/cache/memcache"
|
||||
// "github.com/beego/beego/v2/cache"
|
||||
// _ "github.com/beego/beego/v2/client/cache/memcache"
|
||||
// "github.com/beego/beego/v2/client/cache"
|
||||
// )
|
||||
//
|
||||
// bm, err := cache.NewCache("memcache", `{"conn":"127.0.0.1:11211"}`)
|
||||
|
||||
109
adapter/cache/memcache/memcache_test.go
vendored
109
adapter/cache/memcache/memcache_test.go
vendored
@ -21,9 +21,19 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/beego/beego/v2/adapter/cache"
|
||||
)
|
||||
|
||||
const (
|
||||
initError = "init err"
|
||||
setError = "set Error"
|
||||
checkError = "check err"
|
||||
getError = "get err"
|
||||
getMultiError = "GetMulti Error"
|
||||
)
|
||||
|
||||
func TestMemcacheCache(t *testing.T) {
|
||||
|
||||
addr := os.Getenv("MEMCACHE_ADDR")
|
||||
@ -32,83 +42,52 @@ func TestMemcacheCache(t *testing.T) {
|
||||
}
|
||||
|
||||
bm, err := cache.NewCache("memcache", fmt.Sprintf(`{"conn": "%s"}`, addr))
|
||||
if err != nil {
|
||||
t.Error("init err")
|
||||
}
|
||||
timeoutDuration := 10 * time.Second
|
||||
if err = bm.Put("astaxie", "1", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie") {
|
||||
t.Error("check err")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
timeoutDuration := 5 * time.Second
|
||||
|
||||
assert.Nil(t, bm.Put("astaxie", "1", timeoutDuration))
|
||||
|
||||
assert.True(t, bm.IsExist("astaxie"))
|
||||
|
||||
time.Sleep(11 * time.Second)
|
||||
|
||||
if bm.IsExist("astaxie") {
|
||||
t.Error("check err")
|
||||
}
|
||||
if err = bm.Put("astaxie", "1", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
assert.False(t, bm.IsExist("astaxie"))
|
||||
|
||||
if v, err := strconv.Atoi(string(bm.Get("astaxie").([]byte))); err != nil || v != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
assert.Nil(t, bm.Put("astaxie", "1", timeoutDuration))
|
||||
v, err := strconv.Atoi(string(bm.Get("astaxie").([]byte)))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, v)
|
||||
|
||||
if err = bm.Incr("astaxie"); err != nil {
|
||||
t.Error("Incr Error", err)
|
||||
}
|
||||
assert.Nil(t, bm.Incr("astaxie"))
|
||||
|
||||
if v, err := strconv.Atoi(string(bm.Get("astaxie").([]byte))); err != nil || v != 2 {
|
||||
t.Error("get err")
|
||||
}
|
||||
v, err = strconv.Atoi(string(bm.Get("astaxie").([]byte)))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, v)
|
||||
|
||||
if err = bm.Decr("astaxie"); err != nil {
|
||||
t.Error("Decr Error", err)
|
||||
}
|
||||
assert.Nil(t, bm.Decr("astaxie"))
|
||||
|
||||
if v, err := strconv.Atoi(string(bm.Get("astaxie").([]byte))); err != nil || v != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
bm.Delete("astaxie")
|
||||
if bm.IsExist("astaxie") {
|
||||
t.Error("delete err")
|
||||
}
|
||||
v, err = strconv.Atoi(string(bm.Get("astaxie").([]byte)))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, v)
|
||||
|
||||
// test string
|
||||
if err = bm.Put("astaxie", "author", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie") {
|
||||
t.Error("check err")
|
||||
}
|
||||
assert.Nil(t, bm.Delete("astaxie"))
|
||||
|
||||
if v := bm.Get("astaxie").([]byte); string(v) != "author" {
|
||||
t.Error("get err")
|
||||
}
|
||||
assert.False(t, bm.IsExist("astaxie"))
|
||||
|
||||
// test GetMulti
|
||||
if err = bm.Put("astaxie1", "author1", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie1") {
|
||||
t.Error("check err")
|
||||
}
|
||||
assert.Nil(t, bm.Put("astaxie", "author", timeoutDuration))
|
||||
|
||||
assert.True(t, bm.IsExist("astaxie"))
|
||||
|
||||
assert.Equal(t, []byte("author"), bm.Get("astaxie"))
|
||||
|
||||
assert.Nil(t, bm.Put("astaxie1", "author1", timeoutDuration))
|
||||
|
||||
assert.True(t, bm.IsExist("astaxie1"))
|
||||
|
||||
vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if string(vv[0].([]byte)) != "author" && string(vv[0].([]byte)) != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if string(vv[1].([]byte)) != "author1" && string(vv[1].([]byte)) != "author" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
assert.Equal(t, 2, len(vv))
|
||||
assert.Equal(t, []byte("author"), vv[0])
|
||||
assert.Equal(t, []byte("author1"), vv[1])
|
||||
|
||||
// test clear all
|
||||
if err = bm.ClearAll(); err != nil {
|
||||
t.Error("clear all err")
|
||||
}
|
||||
assert.Nil(t, bm.ClearAll())
|
||||
}
|
||||
|
||||
4
adapter/cache/redis/redis.go
vendored
4
adapter/cache/redis/redis.go
vendored
@ -20,8 +20,8 @@
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/beego/beego/v2/cache/redis"
|
||||
// "github.com/beego/beego/v2/cache"
|
||||
// _ "github.com/beego/beego/v2/client/cache/redis"
|
||||
// "github.com/beego/beego/v2/client/cache"
|
||||
// )
|
||||
//
|
||||
// bm, err := cache.NewCache("redis", `{"conn":"127.0.0.1:11211"}`)
|
||||
|
||||
126
adapter/cache/redis/redis_test.go
vendored
126
adapter/cache/redis/redis_test.go
vendored
@ -21,10 +21,19 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/beego/beego/v2/adapter/cache"
|
||||
)
|
||||
|
||||
const (
|
||||
initError = "init err"
|
||||
setError = "set Error"
|
||||
checkError = "check err"
|
||||
getError = "get err"
|
||||
getMultiError = "GetMulti Error"
|
||||
)
|
||||
|
||||
func TestRedisCache(t *testing.T) {
|
||||
redisAddr := os.Getenv("REDIS_ADDR")
|
||||
if redisAddr == "" {
|
||||
@ -32,98 +41,79 @@ func TestRedisCache(t *testing.T) {
|
||||
}
|
||||
|
||||
bm, err := cache.NewCache("redis", fmt.Sprintf(`{"conn": "%s"}`, redisAddr))
|
||||
if err != nil {
|
||||
t.Error("init err")
|
||||
}
|
||||
timeoutDuration := 10 * time.Second
|
||||
if err = bm.Put("astaxie", 1, timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie") {
|
||||
t.Error("check err")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
timeoutDuration := 5 * time.Second
|
||||
|
||||
time.Sleep(11 * time.Second)
|
||||
assert.Nil(t, bm.Put("astaxie", 1, timeoutDuration))
|
||||
|
||||
if bm.IsExist("astaxie") {
|
||||
t.Error("check err")
|
||||
}
|
||||
if err = bm.Put("astaxie", 1, timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
assert.True(t, bm.IsExist("astaxie"))
|
||||
|
||||
if v, _ := redis.Int(bm.Get("astaxie"), err); v != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
time.Sleep(7 * time.Second)
|
||||
|
||||
if err = bm.Incr("astaxie"); err != nil {
|
||||
t.Error("Incr Error", err)
|
||||
}
|
||||
assert.False(t, bm.IsExist("astaxie"))
|
||||
|
||||
if v, _ := redis.Int(bm.Get("astaxie"), err); v != 2 {
|
||||
t.Error("get err")
|
||||
}
|
||||
assert.Nil(t, bm.Put("astaxie", 1, timeoutDuration))
|
||||
|
||||
if err = bm.Decr("astaxie"); err != nil {
|
||||
t.Error("Decr Error", err)
|
||||
}
|
||||
v, err := redis.Int(bm.Get("astaxie"), err)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, v)
|
||||
|
||||
if v, _ := redis.Int(bm.Get("astaxie"), err); v != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
bm.Delete("astaxie")
|
||||
if bm.IsExist("astaxie") {
|
||||
t.Error("delete err")
|
||||
}
|
||||
assert.Nil(t, bm.Incr("astaxie"))
|
||||
|
||||
// test string
|
||||
if err = bm.Put("astaxie", "author", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie") {
|
||||
t.Error("check err")
|
||||
}
|
||||
v, err = redis.Int(bm.Get("astaxie"), err)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, v)
|
||||
|
||||
if v, _ := redis.String(bm.Get("astaxie"), err); v != "author" {
|
||||
t.Error("get err")
|
||||
}
|
||||
assert.Nil(t, bm.Decr("astaxie"))
|
||||
|
||||
// test GetMulti
|
||||
if err = bm.Put("astaxie1", "author1", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !bm.IsExist("astaxie1") {
|
||||
t.Error("check err")
|
||||
}
|
||||
v, err = redis.Int(bm.Get("astaxie"), err)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, v)
|
||||
|
||||
assert.Nil(t, bm.Delete("astaxie"))
|
||||
|
||||
assert.False(t, bm.IsExist("astaxie"))
|
||||
|
||||
assert.Nil(t, bm.Put("astaxie", "author", timeoutDuration))
|
||||
assert.True(t, bm.IsExist("astaxie"))
|
||||
|
||||
vs, err := redis.String(bm.Get("astaxie"), err)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "author", vs)
|
||||
|
||||
assert.Nil(t, bm.Put("astaxie1", "author1", timeoutDuration))
|
||||
|
||||
assert.True(t, bm.IsExist("astaxie1"))
|
||||
|
||||
vv := bm.GetMulti([]string{"astaxie", "astaxie1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if v, _ := redis.String(vv[0], nil); v != "author" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if v, _ := redis.String(vv[1], nil); v != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
|
||||
assert.Equal(t, 2, len(vv))
|
||||
|
||||
vs, err = redis.String(vv[0], nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "author", vs)
|
||||
|
||||
vs, err = redis.String(vv[1], nil)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "author1", vs)
|
||||
|
||||
assert.Nil(t, bm.ClearAll())
|
||||
// test clear all
|
||||
if err = bm.ClearAll(); err != nil {
|
||||
t.Error("clear all err")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCache_Scan(t *testing.T) {
|
||||
func TestCacheScan(t *testing.T) {
|
||||
timeoutDuration := 10 * time.Second
|
||||
// init
|
||||
bm, err := cache.NewCache("redis", `{"conn": "127.0.0.1:6379"}`)
|
||||
if err != nil {
|
||||
t.Error("init err")
|
||||
t.Error(initError)
|
||||
}
|
||||
// insert all
|
||||
for i := 0; i < 10000; i++ {
|
||||
if err = bm.Put(fmt.Sprintf("astaxie%d", i), fmt.Sprintf("author%d", i), timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
t.Error(setError, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
122
adapter/cache/ssdb/ssdb_test.go
vendored
122
adapter/cache/ssdb/ssdb_test.go
vendored
@ -7,9 +7,19 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/beego/beego/v2/adapter/cache"
|
||||
)
|
||||
|
||||
const (
|
||||
initError = "init err"
|
||||
setError = "set Error"
|
||||
checkError = "check err"
|
||||
getError = "get err"
|
||||
getMultiError = "GetMulti Error"
|
||||
)
|
||||
|
||||
func TestSsdbcacheCache(t *testing.T) {
|
||||
ssdbAddr := os.Getenv("SSDB_ADDR")
|
||||
if ssdbAddr == "" {
|
||||
@ -17,95 +27,59 @@ func TestSsdbcacheCache(t *testing.T) {
|
||||
}
|
||||
|
||||
ssdb, err := cache.NewCache("ssdb", fmt.Sprintf(`{"conn": "%s"}`, ssdbAddr))
|
||||
if err != nil {
|
||||
t.Error("init err")
|
||||
}
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.False(t, ssdb.IsExist("ssdb"))
|
||||
// test put and exist
|
||||
if ssdb.IsExist("ssdb") {
|
||||
t.Error("check err")
|
||||
}
|
||||
timeoutDuration := 10 * time.Second
|
||||
timeoutDuration := 3 * time.Second
|
||||
// timeoutDuration := -10*time.Second if timeoutDuration is negtive,it means permanent
|
||||
if err = ssdb.Put("ssdb", "ssdb", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !ssdb.IsExist("ssdb") {
|
||||
t.Error("check err")
|
||||
}
|
||||
assert.Nil(t, ssdb.Put("ssdb", "ssdb", timeoutDuration))
|
||||
assert.True(t, ssdb.IsExist("ssdb"))
|
||||
|
||||
// Get test done
|
||||
if err = ssdb.Put("ssdb", "ssdb", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
assert.Nil(t, ssdb.Put("ssdb", "ssdb", timeoutDuration))
|
||||
|
||||
if v := ssdb.Get("ssdb"); v != "ssdb" {
|
||||
t.Error("get Error")
|
||||
}
|
||||
assert.Equal(t, "ssdb", ssdb.Get("ssdb"))
|
||||
|
||||
// inc/dec test done
|
||||
if err = ssdb.Put("ssdb", "2", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if err = ssdb.Incr("ssdb"); err != nil {
|
||||
t.Error("incr Error", err)
|
||||
}
|
||||
assert.Nil(t, ssdb.Put("ssdb", "2", timeoutDuration))
|
||||
|
||||
if v, err := strconv.Atoi(ssdb.Get("ssdb").(string)); err != nil || v != 3 {
|
||||
t.Error("get err")
|
||||
}
|
||||
assert.Nil(t, ssdb.Incr("ssdb"))
|
||||
|
||||
if err = ssdb.Decr("ssdb"); err != nil {
|
||||
t.Error("decr error")
|
||||
}
|
||||
v, err := strconv.Atoi(ssdb.Get("ssdb").(string))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, v)
|
||||
|
||||
assert.Nil(t, ssdb.Decr("ssdb"))
|
||||
|
||||
assert.Nil(t, ssdb.Put("ssdb", "3", timeoutDuration))
|
||||
|
||||
// test del
|
||||
if err = ssdb.Put("ssdb", "3", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if v, err := strconv.Atoi(ssdb.Get("ssdb").(string)); err != nil || v != 3 {
|
||||
t.Error("get err")
|
||||
}
|
||||
if err := ssdb.Delete("ssdb"); err == nil {
|
||||
if ssdb.IsExist("ssdb") {
|
||||
t.Error("delete err")
|
||||
}
|
||||
}
|
||||
v, err = strconv.Atoi(ssdb.Get("ssdb").(string))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, v)
|
||||
|
||||
assert.Nil(t, ssdb.Delete("ssdb"))
|
||||
assert.False(t, ssdb.IsExist("ssdb"))
|
||||
|
||||
// test string
|
||||
if err = ssdb.Put("ssdb", "ssdb", -10*time.Second); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !ssdb.IsExist("ssdb") {
|
||||
t.Error("check err")
|
||||
}
|
||||
if v := ssdb.Get("ssdb").(string); v != "ssdb" {
|
||||
t.Error("get err")
|
||||
}
|
||||
assert.Nil(t, ssdb.Put("ssdb", "ssdb", -10*time.Second))
|
||||
|
||||
assert.True(t, ssdb.IsExist("ssdb"))
|
||||
assert.Equal(t, "ssdb", ssdb.Get("ssdb"))
|
||||
|
||||
// test GetMulti done
|
||||
if err = ssdb.Put("ssdb1", "ssdb1", -10*time.Second); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if !ssdb.IsExist("ssdb1") {
|
||||
t.Error("check err")
|
||||
}
|
||||
vv := ssdb.GetMulti([]string{"ssdb", "ssdb1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("getmulti error")
|
||||
}
|
||||
if vv[0].(string) != "ssdb" {
|
||||
t.Error("getmulti error")
|
||||
}
|
||||
if vv[1].(string) != "ssdb1" {
|
||||
t.Error("getmulti error")
|
||||
}
|
||||
assert.Nil(t, ssdb.Put("ssdb1", "ssdb1", -10*time.Second))
|
||||
assert.True(t, ssdb.IsExist("ssdb1") )
|
||||
|
||||
vv := ssdb.GetMulti([]string{"ssdb", "ssdb1"})
|
||||
assert.Equal(t, 2, len(vv))
|
||||
|
||||
assert.Equal(t, "ssdb", vv[0])
|
||||
assert.Equal(t, "ssdb1", vv[1])
|
||||
|
||||
assert.Nil(t, ssdb.ClearAll())
|
||||
assert.False(t, ssdb.IsExist("ssdb"))
|
||||
assert.False(t, ssdb.IsExist("ssdb1"))
|
||||
// test clear all done
|
||||
if err = ssdb.ClearAll(); err != nil {
|
||||
t.Error("clear all err")
|
||||
}
|
||||
if ssdb.IsExist("ssdb") || ssdb.IsExist("ssdb1") {
|
||||
t.Error("check err")
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
|
||||
// Package config is used to parse config.
|
||||
// Usage:
|
||||
// import "github.com/beego/beego/v2/config"
|
||||
// import "github.com/beego/beego/v2/core/config"
|
||||
// Examples.
|
||||
//
|
||||
// cnf, err := config.NewConfig("ini", "config.conf")
|
||||
|
||||
@ -81,7 +81,8 @@ password = ${GOPATH}
|
||||
}
|
||||
)
|
||||
|
||||
f, err := os.Create("testini.conf")
|
||||
cfgFile := "testini.conf"
|
||||
f, err := os.Create(cfgFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -91,8 +92,8 @@ password = ${GOPATH}
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
defer os.Remove("testini.conf")
|
||||
iniconf, err := NewConfig("ini", "testini.conf")
|
||||
defer os.Remove(cfgFile)
|
||||
iniconf, err := NewConfig("ini", cfgFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@ -18,6 +18,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestJsonStartsWithArray(t *testing.T) {
|
||||
@ -32,7 +34,8 @@ func TestJsonStartsWithArray(t *testing.T) {
|
||||
"serviceAPI": "http://www.test.com/employee"
|
||||
}
|
||||
]`
|
||||
f, err := os.Create("testjsonWithArray.conf")
|
||||
cfgFileName := "testjsonWithArray.conf"
|
||||
f, err := os.Create(cfgFileName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -42,8 +45,8 @@ func TestJsonStartsWithArray(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
defer os.Remove("testjsonWithArray.conf")
|
||||
jsonconf, err := NewConfig("json", "testjsonWithArray.conf")
|
||||
defer os.Remove(cfgFileName)
|
||||
jsonconf, err := NewConfig("json", cfgFileName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -132,7 +135,8 @@ func TestJson(t *testing.T) {
|
||||
}
|
||||
)
|
||||
|
||||
f, err := os.Create("testjson.conf")
|
||||
cfgFileName := "testjson.conf"
|
||||
f, err := os.Create(cfgFileName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -142,8 +146,8 @@ func TestJson(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
defer os.Remove("testjson.conf")
|
||||
jsonconf, err := NewConfig("json", "testjson.conf")
|
||||
defer os.Remove(cfgFileName)
|
||||
jsonconf, err := NewConfig("json", cfgFileName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -167,56 +171,39 @@ func TestJson(t *testing.T) {
|
||||
default:
|
||||
value, err = jsonconf.DIY(k)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("get key %q value fatal,%v err %s", k, v, err)
|
||||
} else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) {
|
||||
t.Fatalf("get key %q value, want %v got %v .", k, v, value)
|
||||
}
|
||||
|
||||
}
|
||||
if err = jsonconf.Set("name", "astaxie"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if jsonconf.String("name") != "astaxie" {
|
||||
t.Fatal("get name error")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, fmt.Sprintf("%v", v), fmt.Sprintf("%v", value))
|
||||
}
|
||||
|
||||
if db, err := jsonconf.DIY("database"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if m, ok := db.(map[string]interface{}); !ok {
|
||||
t.Log(db)
|
||||
t.Fatal("db not map[string]interface{}")
|
||||
} else {
|
||||
if m["host"].(string) != "host" {
|
||||
t.Fatal("get host err")
|
||||
}
|
||||
}
|
||||
assert.Nil(t, jsonconf.Set("name", "astaxie"))
|
||||
|
||||
if _, err := jsonconf.Int("unknown"); err == nil {
|
||||
t.Error("unknown keys should return an error when expecting an Int")
|
||||
}
|
||||
assert.Equal(t, "astaxie", jsonconf.String("name"))
|
||||
|
||||
if _, err := jsonconf.Int64("unknown"); err == nil {
|
||||
t.Error("unknown keys should return an error when expecting an Int64")
|
||||
}
|
||||
db, err := jsonconf.DIY("database")
|
||||
assert.Nil(t, err)
|
||||
|
||||
if _, err := jsonconf.Float("unknown"); err == nil {
|
||||
t.Error("unknown keys should return an error when expecting a Float")
|
||||
}
|
||||
m, ok := db.(map[string]interface{})
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t,"host" , m["host"])
|
||||
|
||||
if _, err := jsonconf.DIY("unknown"); err == nil {
|
||||
t.Error("unknown keys should return an error when expecting an interface{}")
|
||||
}
|
||||
_, err = jsonconf.Int("unknown")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
if val := jsonconf.String("unknown"); val != "" {
|
||||
t.Error("unknown keys should return an empty string when expecting a String")
|
||||
}
|
||||
_, err = jsonconf.Int64("unknown")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
if _, err := jsonconf.Bool("unknown"); err == nil {
|
||||
t.Error("unknown keys should return an error when expecting a Bool")
|
||||
}
|
||||
_, err = jsonconf.Float("unknown")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
if !jsonconf.DefaultBool("unknown", true) {
|
||||
t.Error("unknown keys with default value wrong")
|
||||
}
|
||||
_, err = jsonconf.DIY("unknown")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
val := jsonconf.String("unknown")
|
||||
assert.Equal(t, "", val)
|
||||
|
||||
_, err = jsonconf.Bool("unknown")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
assert.True(t, jsonconf.DefaultBool("unknown", true))
|
||||
}
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/beego/beego/v2/config/xml"
|
||||
// "github.com/beego/beego/v2/config"
|
||||
// _ "github.com/beego/beego/v2/core/config/xml"
|
||||
// "github.com/beego/beego/v2/core/config"
|
||||
// )
|
||||
//
|
||||
// cnf, err := config.NewConfig("xml", "config.xml")
|
||||
|
||||
@ -58,7 +58,8 @@ func TestXML(t *testing.T) {
|
||||
}
|
||||
)
|
||||
|
||||
f, err := os.Create("testxml.conf")
|
||||
cfgFileName := "testxml.conf"
|
||||
f, err := os.Create(cfgFileName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -68,9 +69,9 @@ func TestXML(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
defer os.Remove("testxml.conf")
|
||||
defer os.Remove(cfgFileName)
|
||||
|
||||
xmlconf, err := config.NewConfig("xml", "testxml.conf")
|
||||
xmlconf, err := config.NewConfig("xml", cfgFileName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/beego/beego/v2/config/yaml"
|
||||
// "github.com/beego/beego/v2/config"
|
||||
// _ "github.com/beego/beego/v2/core/config/yaml"
|
||||
// "github.com/beego/beego/v2/core/config"
|
||||
// )
|
||||
//
|
||||
// cnf, err := config.NewConfig("yaml", "config.yaml")
|
||||
|
||||
@ -54,7 +54,8 @@ func TestYaml(t *testing.T) {
|
||||
"emptystrings": []string{},
|
||||
}
|
||||
)
|
||||
f, err := os.Create("testyaml.conf")
|
||||
cfgFileName := "testyaml.conf"
|
||||
f, err := os.Create(cfgFileName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -64,8 +65,8 @@ func TestYaml(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
defer os.Remove("testyaml.conf")
|
||||
yamlconf, err := config.NewConfig("yaml", "testyaml.conf")
|
||||
defer os.Remove(cfgFileName)
|
||||
yamlconf, err := config.NewConfig("yaml", cfgFileName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
// Package context provide the context utils
|
||||
// Usage:
|
||||
//
|
||||
// import "github.com/beego/beego/v2/context"
|
||||
// import "github.com/beego/beego/v2/server/web/context"
|
||||
//
|
||||
// ctx := context.Context{Request:req,ResponseWriter:rw}
|
||||
//
|
||||
|
||||
18
adapter/context/param/conv.go
Normal file
18
adapter/context/param/conv.go
Normal file
@ -0,0 +1,18 @@
|
||||
package param
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
beecontext "github.com/beego/beego/v2/adapter/context"
|
||||
"github.com/beego/beego/v2/server/web/context"
|
||||
"github.com/beego/beego/v2/server/web/context/param"
|
||||
)
|
||||
|
||||
// ConvertParams converts http method params to values that will be passed to the method controller as arguments
|
||||
func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) {
|
||||
nps := make([]*param.MethodParam, 0, len(methodParams))
|
||||
for _, mp := range methodParams {
|
||||
nps = append(nps, (*param.MethodParam)(mp))
|
||||
}
|
||||
return param.ConvertParams(nps, methodType, (*context.Context)(ctx))
|
||||
}
|
||||
41
adapter/context/param/conv_test.go
Normal file
41
adapter/context/param/conv_test.go
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2020 beego
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package param
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/beego/beego/v2/adapter/context"
|
||||
)
|
||||
|
||||
|
||||
// Demo is used to test, it's empty
|
||||
func Demo(i int) {
|
||||
|
||||
}
|
||||
|
||||
func TestConvertParams(t *testing.T) {
|
||||
res := ConvertParams(nil, reflect.TypeOf(Demo), context.NewContext())
|
||||
assert.Equal(t, 0, len(res))
|
||||
ctx := context.NewContext()
|
||||
ctx.Input.RequestBody = []byte("11")
|
||||
res = ConvertParams([]*MethodParam{
|
||||
New("A", InBody),
|
||||
}, reflect.TypeOf(Demo), ctx)
|
||||
assert.Equal(t, int64(11), res[0].Int())
|
||||
}
|
||||
29
adapter/context/param/methodparams.go
Normal file
29
adapter/context/param/methodparams.go
Normal file
@ -0,0 +1,29 @@
|
||||
package param
|
||||
|
||||
import (
|
||||
"github.com/beego/beego/v2/server/web/context/param"
|
||||
)
|
||||
|
||||
// MethodParam keeps param information to be auto passed to controller methods
|
||||
type MethodParam param.MethodParam
|
||||
|
||||
// New creates a new MethodParam with name and specific options
|
||||
func New(name string, opts ...MethodParamOption) *MethodParam {
|
||||
newOps := make([]param.MethodParamOption, 0, len(opts))
|
||||
for _, o := range opts {
|
||||
newOps = append(newOps, oldMpoToNew(o))
|
||||
}
|
||||
return (*MethodParam)(param.New(name, newOps...))
|
||||
}
|
||||
|
||||
// Make creates an array of MethodParmas or an empty array
|
||||
func Make(list ...*MethodParam) []*MethodParam {
|
||||
if len(list) > 0 {
|
||||
return list
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *MethodParam) String() string {
|
||||
return (*param.MethodParam)(mp).String()
|
||||
}
|
||||
34
adapter/context/param/methodparams_test.go
Normal file
34
adapter/context/param/methodparams_test.go
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2020 beego
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package param
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMethodParamString(t *testing.T) {
|
||||
method := New("myName", IsRequired, InHeader, Default("abc"))
|
||||
s := method.String()
|
||||
assert.Equal(t, `param.New("myName", param.IsRequired, param.InHeader, param.Default("abc"))`, s)
|
||||
}
|
||||
|
||||
func TestMake(t *testing.T) {
|
||||
res := Make()
|
||||
assert.Equal(t, 0, len(res))
|
||||
res = Make(New("myName", InBody))
|
||||
assert.Equal(t, 1, len(res))
|
||||
}
|
||||
45
adapter/context/param/options.go
Normal file
45
adapter/context/param/options.go
Normal file
@ -0,0 +1,45 @@
|
||||
package param
|
||||
|
||||
import (
|
||||
"github.com/beego/beego/v2/server/web/context/param"
|
||||
)
|
||||
|
||||
// MethodParamOption defines a func which apply options on a MethodParam
|
||||
type MethodParamOption func(*MethodParam)
|
||||
|
||||
// IsRequired indicates that this param is required and can not be omitted from the http request
|
||||
var IsRequired MethodParamOption = func(p *MethodParam) {
|
||||
param.IsRequired((*param.MethodParam)(p))
|
||||
}
|
||||
|
||||
// InHeader indicates that this param is passed via an http header
|
||||
var InHeader MethodParamOption = func(p *MethodParam) {
|
||||
param.InHeader((*param.MethodParam)(p))
|
||||
}
|
||||
|
||||
// InPath indicates that this param is part of the URL path
|
||||
var InPath MethodParamOption = func(p *MethodParam) {
|
||||
param.InPath((*param.MethodParam)(p))
|
||||
}
|
||||
|
||||
// InBody indicates that this param is passed as an http request body
|
||||
var InBody MethodParamOption = func(p *MethodParam) {
|
||||
param.InBody((*param.MethodParam)(p))
|
||||
}
|
||||
|
||||
// Default provides a default value for the http param
|
||||
func Default(defaultValue interface{}) MethodParamOption {
|
||||
return newMpoToOld(param.Default(defaultValue))
|
||||
}
|
||||
|
||||
func newMpoToOld(n param.MethodParamOption) MethodParamOption {
|
||||
return func(methodParam *MethodParam) {
|
||||
n((*param.MethodParam)(methodParam))
|
||||
}
|
||||
}
|
||||
|
||||
func oldMpoToNew(old MethodParamOption) param.MethodParamOption {
|
||||
return func(methodParam *param.MethodParam) {
|
||||
old((*MethodParam)(methodParam))
|
||||
}
|
||||
}
|
||||
@ -22,7 +22,7 @@
|
||||
// "net/http"
|
||||
// "os"
|
||||
//
|
||||
// "github.com/beego/beego/v2/grace"
|
||||
// "github.com/beego/beego/v2/server/web/grace"
|
||||
// )
|
||||
//
|
||||
// func handler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
// Package httplib is used as http.Client
|
||||
// Usage:
|
||||
//
|
||||
// import "github.com/beego/beego/v2/httplib"
|
||||
// import "github.com/beego/beego/v2/client/httplib"
|
||||
//
|
||||
// b := httplib.Post("http://beego.me/")
|
||||
// b.Param("username","astaxie")
|
||||
@ -115,12 +115,6 @@ func (b *BeegoHTTPRequest) SetUserAgent(useragent string) *BeegoHTTPRequest {
|
||||
return b
|
||||
}
|
||||
|
||||
// Debug sets show debug or not when executing request.
|
||||
func (b *BeegoHTTPRequest) Debug(isdebug bool) *BeegoHTTPRequest {
|
||||
b.delegate.Debug(isdebug)
|
||||
return b
|
||||
}
|
||||
|
||||
// Retries sets Retries times.
|
||||
// default is 0 means no retried.
|
||||
// -1 means retried forever.
|
||||
@ -135,17 +129,6 @@ func (b *BeegoHTTPRequest) RetryDelay(delay time.Duration) *BeegoHTTPRequest {
|
||||
return b
|
||||
}
|
||||
|
||||
// DumpBody setting whether need to Dump the Body.
|
||||
func (b *BeegoHTTPRequest) DumpBody(isdump bool) *BeegoHTTPRequest {
|
||||
b.delegate.DumpBody(isdump)
|
||||
return b
|
||||
}
|
||||
|
||||
// DumpRequest return the DumpRequest
|
||||
func (b *BeegoHTTPRequest) DumpRequest() []byte {
|
||||
return b.delegate.DumpRequest()
|
||||
}
|
||||
|
||||
// SetTimeout sets connect time out and read-write time out for BeegoRequest.
|
||||
func (b *BeegoHTTPRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHTTPRequest {
|
||||
b.delegate.SetTimeout(connectTimeout, readWriteTimeout)
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
package httplib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
@ -25,8 +26,11 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const getUrl = "http://httpbin.org/get"
|
||||
const ipUrl = "http://httpbin.org/ip"
|
||||
|
||||
func TestResponse(t *testing.T) {
|
||||
req := Get("http://httpbin.org/get")
|
||||
req := Get(getUrl)
|
||||
resp, err := req.Response()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -63,7 +67,8 @@ func TestDoRequest(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
req := Get("http://httpbin.org/get")
|
||||
|
||||
req := Get(getUrl)
|
||||
b, err := req.Bytes()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -205,7 +210,7 @@ func TestWithSetting(t *testing.T) {
|
||||
setting.ReadWriteTimeout = 5 * time.Second
|
||||
SetDefaultSetting(setting)
|
||||
|
||||
str, err := Get("http://httpbin.org/get").String()
|
||||
str, err := Get(getUrl).String()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -218,7 +223,8 @@ func TestWithSetting(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestToJson(t *testing.T) {
|
||||
req := Get("http://httpbin.org/ip")
|
||||
|
||||
req := Get(ipUrl)
|
||||
resp, err := req.Response()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -249,28 +255,28 @@ func TestToJson(t *testing.T) {
|
||||
|
||||
func TestToFile(t *testing.T) {
|
||||
f := "beego_testfile"
|
||||
req := Get("http://httpbin.org/ip")
|
||||
req := Get(ipUrl)
|
||||
err := req.ToFile(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(f)
|
||||
b, err := ioutil.ReadFile(f)
|
||||
if n := strings.Index(string(b), "origin"); n == -1 {
|
||||
if n := bytes.Index(b, []byte("origin")); n == -1 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToFileDir(t *testing.T) {
|
||||
f := "./files/beego_testfile"
|
||||
req := Get("http://httpbin.org/ip")
|
||||
req := Get(ipUrl)
|
||||
err := req.ToFile(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll("./files")
|
||||
b, err := ioutil.ReadFile(f)
|
||||
if n := strings.Index(string(b), "origin"); n == -1 {
|
||||
if n := bytes.Index(b, []byte("origin")); n == -1 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import (
|
||||
)
|
||||
|
||||
// Log levels to control the logging output.
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
const (
|
||||
LevelEmergency = webLog.LevelEmergency
|
||||
LevelAlert = webLog.LevelAlert
|
||||
@ -36,90 +36,90 @@ const (
|
||||
)
|
||||
|
||||
// BeeLogger references the used application logger.
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
var BeeLogger = logs.GetBeeLogger()
|
||||
|
||||
// SetLevel sets the global log level used by the simple logger.
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
func SetLevel(l int) {
|
||||
logs.SetLevel(l)
|
||||
}
|
||||
|
||||
// SetLogFuncCall set the CallDepth, default is 3
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
func SetLogFuncCall(b bool) {
|
||||
logs.SetLogFuncCall(b)
|
||||
}
|
||||
|
||||
// SetLogger sets a new logger.
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
func SetLogger(adaptername string, config string) error {
|
||||
return logs.SetLogger(adaptername, config)
|
||||
}
|
||||
|
||||
// Emergency logs a message at emergency level.
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
func Emergency(v ...interface{}) {
|
||||
logs.Emergency(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
// Alert logs a message at alert level.
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
func Alert(v ...interface{}) {
|
||||
logs.Alert(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
// Critical logs a message at critical level.
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
func Critical(v ...interface{}) {
|
||||
logs.Critical(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
// Error logs a message at error level.
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
func Error(v ...interface{}) {
|
||||
logs.Error(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
// Warning logs a message at warning level.
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
func Warning(v ...interface{}) {
|
||||
logs.Warning(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
// Warn compatibility alias for Warning()
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
func Warn(v ...interface{}) {
|
||||
logs.Warn(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
// Notice logs a message at notice level.
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
func Notice(v ...interface{}) {
|
||||
logs.Notice(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
// Informational logs a message at info level.
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
func Informational(v ...interface{}) {
|
||||
logs.Informational(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
// Info compatibility alias for Warning()
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
func Info(v ...interface{}) {
|
||||
logs.Info(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
// Debug logs a message at debug level.
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
func Debug(v ...interface{}) {
|
||||
logs.Debug(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
// Trace logs a message at trace level.
|
||||
// compatibility alias for Warning()
|
||||
// Deprecated: use github.com/beego/beego/v2/logs instead.
|
||||
// Deprecated: use github.com/beego/beego/v2/core/logs instead.
|
||||
func Trace(v ...interface{}) {
|
||||
logs.Trace(generateFmtStr(len(v)), v...)
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
// Package logs provide a general log interface
|
||||
// Usage:
|
||||
//
|
||||
// import "github.com/beego/beego/v2/logs"
|
||||
// import "github.com/beego/beego/v2/core/logs"
|
||||
//
|
||||
// log := NewLogger(10000)
|
||||
// log.SetLogger("console", "")
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBeeLogger_Info(t *testing.T) {
|
||||
func TestBeeLoggerInfo(t *testing.T) {
|
||||
log := NewLogger(1000)
|
||||
log.SetLogger("file", `{"net":"tcp","addr":":7020"}`)
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
package metric
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
@ -26,7 +27,9 @@ import (
|
||||
)
|
||||
|
||||
func TestPrometheusMiddleWare(t *testing.T) {
|
||||
middleware := PrometheusMiddleWare(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}))
|
||||
middleware := PrometheusMiddleWare(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
|
||||
fmt.Print("you are coming")
|
||||
}))
|
||||
writer := &context.Response{}
|
||||
request := &http.Request{
|
||||
URL: &url.URL{
|
||||
|
||||
@ -238,141 +238,158 @@ func AddNamespace(nl ...*Namespace) {
|
||||
|
||||
// NSCond is Namespace Condition
|
||||
func NSCond(cond namespaceCond) LinkNamespace {
|
||||
wc := web.NSCond(func(b *context.Context) bool {
|
||||
return cond((*adtContext.Context)(b))
|
||||
})
|
||||
return func(namespace *Namespace) {
|
||||
web.NSCond(func(b *context.Context) bool {
|
||||
return cond((*adtContext.Context)(b))
|
||||
})
|
||||
wc((*web.Namespace)(namespace))
|
||||
}
|
||||
}
|
||||
|
||||
// NSBefore Namespace BeforeRouter filter
|
||||
func NSBefore(filterList ...FilterFunc) LinkNamespace {
|
||||
nfs := oldToNewFilter(filterList)
|
||||
wf := web.NSBefore(nfs...)
|
||||
return func(namespace *Namespace) {
|
||||
nfs := oldToNewFilter(filterList)
|
||||
web.NSBefore(nfs...)
|
||||
wf((*web.Namespace)(namespace))
|
||||
}
|
||||
}
|
||||
|
||||
// NSAfter add Namespace FinishRouter filter
|
||||
func NSAfter(filterList ...FilterFunc) LinkNamespace {
|
||||
nfs := oldToNewFilter(filterList)
|
||||
wf := web.NSAfter(nfs...)
|
||||
return func(namespace *Namespace) {
|
||||
nfs := oldToNewFilter(filterList)
|
||||
web.NSAfter(nfs...)
|
||||
wf((*web.Namespace)(namespace))
|
||||
}
|
||||
}
|
||||
|
||||
// NSInclude Namespace Include ControllerInterface
|
||||
func NSInclude(cList ...ControllerInterface) LinkNamespace {
|
||||
nfs := oldToNewCtrlIntfs(cList)
|
||||
wi := web.NSInclude(nfs...)
|
||||
return func(namespace *Namespace) {
|
||||
nfs := oldToNewCtrlIntfs(cList)
|
||||
web.NSInclude(nfs...)
|
||||
wi((*web.Namespace)(namespace))
|
||||
}
|
||||
}
|
||||
|
||||
// NSRouter call Namespace Router
|
||||
func NSRouter(rootpath string, c ControllerInterface, mappingMethods ...string) LinkNamespace {
|
||||
wn := web.NSRouter(rootpath, c, mappingMethods...)
|
||||
return func(namespace *Namespace) {
|
||||
web.Router(rootpath, c, mappingMethods...)
|
||||
wn((*web.Namespace)(namespace))
|
||||
}
|
||||
}
|
||||
|
||||
// NSGet call Namespace Get
|
||||
func NSGet(rootpath string, f FilterFunc) LinkNamespace {
|
||||
ln := web.NSGet(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
return func(ns *Namespace) {
|
||||
web.NSGet(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
ln((*web.Namespace)(ns))
|
||||
}
|
||||
}
|
||||
|
||||
// NSPost call Namespace Post
|
||||
func NSPost(rootpath string, f FilterFunc) LinkNamespace {
|
||||
wp := web.NSPost(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
return func(ns *Namespace) {
|
||||
web.Post(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
wp((*web.Namespace)(ns))
|
||||
}
|
||||
}
|
||||
|
||||
// NSHead call Namespace Head
|
||||
func NSHead(rootpath string, f FilterFunc) LinkNamespace {
|
||||
wb := web.NSHead(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
return func(ns *Namespace) {
|
||||
web.NSHead(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
wb((*web.Namespace)(ns))
|
||||
}
|
||||
}
|
||||
|
||||
// NSPut call Namespace Put
|
||||
func NSPut(rootpath string, f FilterFunc) LinkNamespace {
|
||||
wn := web.NSPut(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
return func(ns *Namespace) {
|
||||
web.NSPut(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
wn((*web.Namespace)(ns))
|
||||
}
|
||||
}
|
||||
|
||||
// NSDelete call Namespace Delete
|
||||
func NSDelete(rootpath string, f FilterFunc) LinkNamespace {
|
||||
wn := web.NSDelete(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
return func(ns *Namespace) {
|
||||
web.NSDelete(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
wn((*web.Namespace)(ns))
|
||||
}
|
||||
}
|
||||
|
||||
// NSAny call Namespace Any
|
||||
func NSAny(rootpath string, f FilterFunc) LinkNamespace {
|
||||
wn := web.NSAny(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
return func(ns *Namespace) {
|
||||
web.NSAny(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
wn((*web.Namespace)(ns))
|
||||
}
|
||||
}
|
||||
|
||||
// NSOptions call Namespace Options
|
||||
func NSOptions(rootpath string, f FilterFunc) LinkNamespace {
|
||||
wo := web.NSOptions(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
return func(ns *Namespace) {
|
||||
web.NSOptions(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
wo((*web.Namespace)(ns))
|
||||
}
|
||||
}
|
||||
|
||||
// NSPatch call Namespace Patch
|
||||
func NSPatch(rootpath string, f FilterFunc) LinkNamespace {
|
||||
wn := web.NSPatch(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
return func(ns *Namespace) {
|
||||
web.NSPatch(rootpath, func(ctx *context.Context) {
|
||||
f((*adtContext.Context)(ctx))
|
||||
})
|
||||
wn((*web.Namespace)(ns))
|
||||
}
|
||||
}
|
||||
|
||||
// NSAutoRouter call Namespace AutoRouter
|
||||
func NSAutoRouter(c ControllerInterface) LinkNamespace {
|
||||
wn := web.NSAutoRouter(c)
|
||||
return func(ns *Namespace) {
|
||||
web.NSAutoRouter(c)
|
||||
wn((*web.Namespace)(ns))
|
||||
}
|
||||
}
|
||||
|
||||
// NSAutoPrefix call Namespace AutoPrefix
|
||||
func NSAutoPrefix(prefix string, c ControllerInterface) LinkNamespace {
|
||||
wn := web.NSAutoPrefix(prefix, c)
|
||||
return func(ns *Namespace) {
|
||||
web.NSAutoPrefix(prefix, c)
|
||||
wn((*web.Namespace)(ns))
|
||||
}
|
||||
}
|
||||
|
||||
// NSNamespace add sub Namespace
|
||||
func NSNamespace(prefix string, params ...LinkNamespace) LinkNamespace {
|
||||
nps := oldToNewLinkNs(params)
|
||||
wn := web.NSNamespace(prefix, nps...)
|
||||
return func(ns *Namespace) {
|
||||
nps := oldToNewLinkNs(params)
|
||||
web.NSNamespace(prefix, nps...)
|
||||
wn((*web.Namespace)(ns))
|
||||
}
|
||||
}
|
||||
|
||||
// NSHandler add handler
|
||||
func NSHandler(rootpath string, h http.Handler) LinkNamespace {
|
||||
wn := web.NSHandler(rootpath, h)
|
||||
return func(ns *Namespace) {
|
||||
web.NSHandler(rootpath, h)
|
||||
wn((*web.Namespace)(ns))
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ func RegisterModel(models ...interface{}) {
|
||||
|
||||
// RegisterModelWithPrefix register models with a prefix
|
||||
func RegisterModelWithPrefix(prefix string, models ...interface{}) {
|
||||
orm.RegisterModelWithPrefix(prefix, models)
|
||||
orm.RegisterModelWithPrefix(prefix, models...)
|
||||
}
|
||||
|
||||
// RegisterModelWithSuffix register models with a suffix
|
||||
|
||||
31
adapter/orm/models_boot_test.go
Normal file
31
adapter/orm/models_boot_test.go
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2020 beego
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package orm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id int
|
||||
}
|
||||
|
||||
type Seller struct {
|
||||
Id int
|
||||
}
|
||||
|
||||
func TestRegisterModelWithPrefix(t *testing.T) {
|
||||
RegisterModelWithPrefix("test", &User{}, &Seller{})
|
||||
}
|
||||
@ -21,7 +21,7 @@
|
||||
//
|
||||
// import (
|
||||
// "fmt"
|
||||
// "github.com/beego/beego/v2/orm"
|
||||
// "github.com/beego/beego/v2/client/orm"
|
||||
// _ "github.com/go-sql-driver/mysql" // import your used driver
|
||||
// )
|
||||
//
|
||||
|
||||
@ -21,14 +21,16 @@ import (
|
||||
type baseQuerySetter struct {
|
||||
}
|
||||
|
||||
const shouldNotInvoke = "you should not invoke this method."
|
||||
|
||||
func (b *baseQuerySetter) ForceIndex(indexes ...string) orm.QuerySeter {
|
||||
panic("you should not invoke this method.")
|
||||
panic(shouldNotInvoke)
|
||||
}
|
||||
|
||||
func (b *baseQuerySetter) UseIndex(indexes ...string) orm.QuerySeter {
|
||||
panic("you should not invoke this method.")
|
||||
panic(shouldNotInvoke)
|
||||
}
|
||||
|
||||
func (b *baseQuerySetter) IgnoreIndex(indexes ...string) orm.QuerySeter {
|
||||
panic("you should not invoke this method.")
|
||||
panic(shouldNotInvoke)
|
||||
}
|
||||
|
||||
@ -195,7 +195,7 @@ func snakeStringWithAcronym(s string) string {
|
||||
}
|
||||
data = append(data, d)
|
||||
}
|
||||
return strings.ToLower(string(data[:]))
|
||||
return strings.ToLower(string(data))
|
||||
}
|
||||
|
||||
// snake string, XxYy to xx_yy , XxYY to xx_y_y
|
||||
@ -213,7 +213,7 @@ func snakeString(s string) string {
|
||||
}
|
||||
data = append(data, d)
|
||||
}
|
||||
return strings.ToLower(string(data[:]))
|
||||
return strings.ToLower(string(data))
|
||||
}
|
||||
|
||||
// SetNameStrategy set different name strategy
|
||||
@ -241,7 +241,7 @@ func camelString(s string) string {
|
||||
}
|
||||
data = append(data, d)
|
||||
}
|
||||
return string(data[:])
|
||||
return string(data)
|
||||
}
|
||||
|
||||
type argString []string
|
||||
|
||||
@ -16,6 +16,8 @@ package orm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCamelString(t *testing.T) {
|
||||
@ -29,9 +31,7 @@ func TestCamelString(t *testing.T) {
|
||||
|
||||
for _, v := range snake {
|
||||
res := camelString(v)
|
||||
if res != answer[v] {
|
||||
t.Error("Unit Test Fail:", v, res, answer[v])
|
||||
}
|
||||
assert.Equal(t, answer[v], res)
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,9 +46,7 @@ func TestSnakeString(t *testing.T) {
|
||||
|
||||
for _, v := range camel {
|
||||
res := snakeString(v)
|
||||
if res != answer[v] {
|
||||
t.Error("Unit Test Fail:", v, res, answer[v])
|
||||
}
|
||||
assert.Equal(t, answer[v], res)
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,8 +61,6 @@ func TestSnakeStringWithAcronym(t *testing.T) {
|
||||
|
||||
for _, v := range camel {
|
||||
res := snakeStringWithAcronym(v)
|
||||
if res != answer[v] {
|
||||
t.Error("Unit Test Fail:", v, res, answer[v])
|
||||
}
|
||||
assert.Equal(t, answer[v], res)
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
// Simple Usage:
|
||||
// import(
|
||||
// "github.com/beego/beego/v2"
|
||||
// "github.com/beego/beego/v2/plugins/apiauth"
|
||||
// "github.com/beego/beego/v2/server/web/filter/apiauth"
|
||||
// )
|
||||
//
|
||||
// func main(){
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
// Simple Usage:
|
||||
// import(
|
||||
// "github.com/beego/beego/v2"
|
||||
// "github.com/beego/beego/v2/plugins/auth"
|
||||
// "github.com/beego/beego/v2/server/web/filter/auth"
|
||||
// )
|
||||
//
|
||||
// func main(){
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
// Simple Usage:
|
||||
// import(
|
||||
// "github.com/beego/beego/v2"
|
||||
// "github.com/beego/beego/v2/plugins/authz"
|
||||
// "github.com/beego/beego/v2/server/web/filter/authz"
|
||||
// "github.com/casbin/casbin"
|
||||
// )
|
||||
//
|
||||
|
||||
@ -26,6 +26,11 @@ import (
|
||||
"github.com/beego/beego/v2/adapter/plugins/auth"
|
||||
)
|
||||
|
||||
const (
|
||||
authCfg = "authz_model.conf"
|
||||
authCsv = "authz_policy.csv"
|
||||
)
|
||||
|
||||
func testRequest(t *testing.T, handler *beego.ControllerRegister, user string, path string, method string, code int) {
|
||||
r, _ := http.NewRequest(method, path, nil)
|
||||
r.SetBasicAuth(user, "123")
|
||||
@ -40,70 +45,79 @@ func testRequest(t *testing.T, handler *beego.ControllerRegister, user string, p
|
||||
func TestBasic(t *testing.T) {
|
||||
handler := beego.NewControllerRegister()
|
||||
|
||||
handler.InsertFilter("*", beego.BeforeRouter, auth.Basic("alice", "123"))
|
||||
handler.InsertFilter("*", beego.BeforeRouter, NewAuthorizer(casbin.NewEnforcer("authz_model.conf", "authz_policy.csv")))
|
||||
_ = handler.InsertFilter("*", beego.BeforeRouter, auth.Basic("alice", "123"))
|
||||
|
||||
_ = handler.InsertFilter("*", beego.BeforeRouter, NewAuthorizer(casbin.NewEnforcer(authCfg, authCsv)))
|
||||
|
||||
handler.Any("*", func(ctx *context.Context) {
|
||||
ctx.Output.SetStatus(200)
|
||||
})
|
||||
|
||||
testRequest(t, handler, "alice", "/dataset1/resource1", "GET", 200)
|
||||
testRequest(t, handler, "alice", "/dataset1/resource1", "POST", 200)
|
||||
testRequest(t, handler, "alice", "/dataset1/resource2", "GET", 200)
|
||||
testRequest(t, handler, "alice", "/dataset1/resource2", "POST", 403)
|
||||
const d1r1 = "/dataset1/resource1"
|
||||
testRequest(t, handler, "alice", d1r1, "GET", 200)
|
||||
testRequest(t, handler, "alice", d1r1, "POST", 200)
|
||||
const d1r2 = "/dataset1/resource2"
|
||||
testRequest(t, handler, "alice", d1r2, "GET", 200)
|
||||
testRequest(t, handler, "alice", d1r2, "POST", 403)
|
||||
}
|
||||
|
||||
func TestPathWildcard(t *testing.T) {
|
||||
handler := beego.NewControllerRegister()
|
||||
|
||||
handler.InsertFilter("*", beego.BeforeRouter, auth.Basic("bob", "123"))
|
||||
handler.InsertFilter("*", beego.BeforeRouter, NewAuthorizer(casbin.NewEnforcer("authz_model.conf", "authz_policy.csv")))
|
||||
_ = handler.InsertFilter("*", beego.BeforeRouter, auth.Basic("bob", "123"))
|
||||
_ = handler.InsertFilter("*", beego.BeforeRouter, NewAuthorizer(casbin.NewEnforcer(authCfg, authCsv)))
|
||||
|
||||
handler.Any("*", func(ctx *context.Context) {
|
||||
ctx.Output.SetStatus(200)
|
||||
})
|
||||
|
||||
testRequest(t, handler, "bob", "/dataset2/resource1", "GET", 200)
|
||||
testRequest(t, handler, "bob", "/dataset2/resource1", "POST", 200)
|
||||
testRequest(t, handler, "bob", "/dataset2/resource1", "DELETE", 200)
|
||||
testRequest(t, handler, "bob", "/dataset2/resource2", "GET", 200)
|
||||
testRequest(t, handler, "bob", "/dataset2/resource2", "POST", 403)
|
||||
testRequest(t, handler, "bob", "/dataset2/resource2", "DELETE", 403)
|
||||
const d2r1 = "/dataset2/resource1"
|
||||
testRequest(t, handler, "bob", d2r1, "GET", 200)
|
||||
testRequest(t, handler, "bob", d2r1, "POST", 200)
|
||||
testRequest(t, handler, "bob", d2r1, "DELETE", 200)
|
||||
const d2r2 = "/dataset2/resource2"
|
||||
testRequest(t, handler, "bob", d2r2, "GET", 200)
|
||||
testRequest(t, handler, "bob", d2r2, "POST", 403)
|
||||
testRequest(t, handler, "bob", d2r2, "DELETE", 403)
|
||||
|
||||
testRequest(t, handler, "bob", "/dataset2/folder1/item1", "GET", 403)
|
||||
testRequest(t, handler, "bob", "/dataset2/folder1/item1", "POST", 200)
|
||||
testRequest(t, handler, "bob", "/dataset2/folder1/item1", "DELETE", 403)
|
||||
testRequest(t, handler, "bob", "/dataset2/folder1/item2", "GET", 403)
|
||||
testRequest(t, handler, "bob", "/dataset2/folder1/item2", "POST", 200)
|
||||
testRequest(t, handler, "bob", "/dataset2/folder1/item2", "DELETE", 403)
|
||||
const item1 = "/dataset2/folder1/item1"
|
||||
testRequest(t, handler, "bob", item1, "GET", 403)
|
||||
testRequest(t, handler, "bob", item1, "POST", 200)
|
||||
testRequest(t, handler, "bob", item1, "DELETE", 403)
|
||||
const item2 = "/dataset2/folder1/item2"
|
||||
testRequest(t, handler, "bob", item2, "GET", 403)
|
||||
testRequest(t, handler, "bob", item2, "POST", 200)
|
||||
testRequest(t, handler, "bob", item2, "DELETE", 403)
|
||||
}
|
||||
|
||||
func TestRBAC(t *testing.T) {
|
||||
handler := beego.NewControllerRegister()
|
||||
|
||||
handler.InsertFilter("*", beego.BeforeRouter, auth.Basic("cathy", "123"))
|
||||
e := casbin.NewEnforcer("authz_model.conf", "authz_policy.csv")
|
||||
handler.InsertFilter("*", beego.BeforeRouter, NewAuthorizer(e))
|
||||
_ = handler.InsertFilter("*", beego.BeforeRouter, auth.Basic("cathy", "123"))
|
||||
e := casbin.NewEnforcer(authCfg, authCsv)
|
||||
_ = handler.InsertFilter("*", beego.BeforeRouter, NewAuthorizer(e))
|
||||
|
||||
handler.Any("*", func(ctx *context.Context) {
|
||||
ctx.Output.SetStatus(200)
|
||||
})
|
||||
|
||||
// cathy can access all /dataset1/* resources via all methods because it has the dataset1_admin role.
|
||||
testRequest(t, handler, "cathy", "/dataset1/item", "GET", 200)
|
||||
testRequest(t, handler, "cathy", "/dataset1/item", "POST", 200)
|
||||
testRequest(t, handler, "cathy", "/dataset1/item", "DELETE", 200)
|
||||
testRequest(t, handler, "cathy", "/dataset2/item", "GET", 403)
|
||||
testRequest(t, handler, "cathy", "/dataset2/item", "POST", 403)
|
||||
testRequest(t, handler, "cathy", "/dataset2/item", "DELETE", 403)
|
||||
const dataSet1 = "/dataset1/item"
|
||||
testRequest(t, handler, "cathy", dataSet1, "GET", 200)
|
||||
testRequest(t, handler, "cathy", dataSet1, "POST", 200)
|
||||
testRequest(t, handler, "cathy", dataSet1, "DELETE", 200)
|
||||
const dataSet2 = "/dataset2/item"
|
||||
testRequest(t, handler, "cathy", dataSet2, "GET", 403)
|
||||
testRequest(t, handler, "cathy", dataSet2, "POST", 403)
|
||||
testRequest(t, handler, "cathy", dataSet2, "DELETE", 403)
|
||||
|
||||
// delete all roles on user cathy, so cathy cannot access any resources now.
|
||||
e.DeleteRolesForUser("cathy")
|
||||
|
||||
testRequest(t, handler, "cathy", "/dataset1/item", "GET", 403)
|
||||
testRequest(t, handler, "cathy", "/dataset1/item", "POST", 403)
|
||||
testRequest(t, handler, "cathy", "/dataset1/item", "DELETE", 403)
|
||||
testRequest(t, handler, "cathy", "/dataset2/item", "GET", 403)
|
||||
testRequest(t, handler, "cathy", "/dataset2/item", "POST", 403)
|
||||
testRequest(t, handler, "cathy", "/dataset2/item", "DELETE", 403)
|
||||
testRequest(t, handler, "cathy", dataSet1, "GET", 403)
|
||||
testRequest(t, handler, "cathy", dataSet1, "POST", 403)
|
||||
testRequest(t, handler, "cathy", dataSet1, "DELETE", 403)
|
||||
testRequest(t, handler, "cathy", dataSet2, "GET", 403)
|
||||
testRequest(t, handler, "cathy", dataSet2, "POST", 403)
|
||||
testRequest(t, handler, "cathy", dataSet2, "DELETE", 403)
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
// Usage
|
||||
// import (
|
||||
// "github.com/beego/beego/v2"
|
||||
// "github.com/beego/beego/v2/plugins/cors"
|
||||
// "github.com/beego/beego/v2/server/web/filter/cors"
|
||||
// )
|
||||
//
|
||||
// func main() {
|
||||
|
||||
@ -87,7 +87,7 @@ func NewControllerRegister() *ControllerRegister {
|
||||
// Add("/api",&RestController{},"get,post:ApiFunc"
|
||||
// Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc")
|
||||
func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingMethods ...string) {
|
||||
(*web.ControllerRegister)(p).Add(pattern, c, mappingMethods...)
|
||||
(*web.ControllerRegister)(p).Add(pattern, c, web.WithRouterMethods(c, mappingMethods...))
|
||||
}
|
||||
|
||||
// Include only when the Runmode is dev will generate router file in the router/auto.go from the controller
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/beego/beego/v2/session/couchbase"
|
||||
// "github.com/beego/beego/v2/session"
|
||||
// _ "github.com/beego/beego/v2/server/web/session/couchbase"
|
||||
// "github.com/beego/beego/v2/server/web/session"
|
||||
// )
|
||||
//
|
||||
// func init() {
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/beego/beego/v2/session/memcache"
|
||||
// "github.com/beego/beego/v2/session"
|
||||
// _ "github.com/beego/beego/v2/server/web/session/memcache"
|
||||
// "github.com/beego/beego/v2/server/web/session"
|
||||
// )
|
||||
//
|
||||
// func init() {
|
||||
|
||||
@ -28,8 +28,8 @@
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/beego/beego/v2/session/mysql"
|
||||
// "github.com/beego/beego/v2/session"
|
||||
// _ "github.com/beego/beego/v2/server/web/session/mysql"
|
||||
// "github.com/beego/beego/v2/server/web/session"
|
||||
// )
|
||||
//
|
||||
// func init() {
|
||||
|
||||
@ -38,8 +38,8 @@
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/beego/beego/v2/session/postgresql"
|
||||
// "github.com/beego/beego/v2/session"
|
||||
// _ "github.com/beego/beego/v2/server/web/session/postgresql"
|
||||
// "github.com/beego/beego/v2/server/web/session"
|
||||
// )
|
||||
//
|
||||
// func init() {
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/beego/beego/v2/session/redis"
|
||||
// "github.com/beego/beego/v2/session"
|
||||
// _ "github.com/beego/beego/v2/server/web/session/redis"
|
||||
// "github.com/beego/beego/v2/server/web/session"
|
||||
// )
|
||||
//
|
||||
// func init() {
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/beego/beego/v2/session/redis_cluster"
|
||||
// "github.com/beego/beego/v2/session"
|
||||
// _ "github.com/beego/beego/v2/server/web/session/redis_cluster"
|
||||
// "github.com/beego/beego/v2/server/web/session"
|
||||
// )
|
||||
//
|
||||
// func init() {
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/beego/beego/v2/session/redis_sentinel"
|
||||
// "github.com/beego/beego/v2/session"
|
||||
// _ "github.com/beego/beego/v2/server/web/session/redis_sentinel"
|
||||
// "github.com/beego/beego/v2/server/web/session"
|
||||
// )
|
||||
//
|
||||
// func init() {
|
||||
|
||||
@ -5,6 +5,8 @@ import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/beego/beego/v2/adapter/session"
|
||||
)
|
||||
|
||||
@ -19,71 +21,55 @@ func TestRedisSentinel(t *testing.T) {
|
||||
ProviderConfig: "127.0.0.1:6379,100,,0,master",
|
||||
}
|
||||
globalSessions, e := session.NewManager("redis_sentinel", sessionConfig)
|
||||
|
||||
if e != nil {
|
||||
t.Log(e)
|
||||
return
|
||||
}
|
||||
// todo test if e==nil
|
||||
|
||||
go globalSessions.GC()
|
||||
|
||||
r, _ := http.NewRequest("GET", "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
sess, err := globalSessions.SessionStart(w, r)
|
||||
if err != nil {
|
||||
t.Fatal("session start failed:", err)
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
defer sess.SessionRelease(w)
|
||||
|
||||
// SET AND GET
|
||||
err = sess.Set("username", "astaxie")
|
||||
if err != nil {
|
||||
t.Fatal("set username failed:", err)
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
username := sess.Get("username")
|
||||
if username != "astaxie" {
|
||||
t.Fatal("get username failed")
|
||||
}
|
||||
assert.Equal(t, "astaxie", username)
|
||||
|
||||
// DELETE
|
||||
err = sess.Delete("username")
|
||||
if err != nil {
|
||||
t.Fatal("delete username failed:", err)
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
|
||||
username = sess.Get("username")
|
||||
if username != nil {
|
||||
t.Fatal("delete username failed")
|
||||
}
|
||||
assert.Nil(t, username)
|
||||
|
||||
// FLUSH
|
||||
err = sess.Set("username", "astaxie")
|
||||
if err != nil {
|
||||
t.Fatal("set failed:", err)
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = sess.Set("password", "1qaz2wsx")
|
||||
if err != nil {
|
||||
t.Fatal("set failed:", err)
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
|
||||
username = sess.Get("username")
|
||||
if username != "astaxie" {
|
||||
t.Fatal("get username failed")
|
||||
}
|
||||
assert.Equal(t, "astaxie", username)
|
||||
|
||||
password := sess.Get("password")
|
||||
if password != "1qaz2wsx" {
|
||||
t.Fatal("get password failed")
|
||||
}
|
||||
assert.Equal(t, "1qaz2wsx", password)
|
||||
|
||||
err = sess.Flush()
|
||||
if err != nil {
|
||||
t.Fatal("flush failed:", err)
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
|
||||
username = sess.Get("username")
|
||||
if username != nil {
|
||||
t.Fatal("flush failed")
|
||||
}
|
||||
assert.Nil(t, username)
|
||||
|
||||
password = sess.Get("password")
|
||||
if password != nil {
|
||||
t.Fatal("flush failed")
|
||||
}
|
||||
assert.Nil(t, password)
|
||||
|
||||
sess.SessionRelease(w)
|
||||
|
||||
|
||||
@ -22,6 +22,8 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
const setCookieKey = "Set-Cookie"
|
||||
|
||||
func TestCookie(t *testing.T) {
|
||||
config := `{"cookieName":"gosessionid","enableSetCookie":false,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}`
|
||||
conf := new(ManagerConfig)
|
||||
@ -46,7 +48,8 @@ func TestCookie(t *testing.T) {
|
||||
t.Fatal("get username error")
|
||||
}
|
||||
sess.SessionRelease(w)
|
||||
if cookiestr := w.Header().Get("Set-Cookie"); cookiestr == "" {
|
||||
|
||||
if cookiestr := w.Header().Get(setCookieKey); cookiestr == "" {
|
||||
t.Fatal("setcookie error")
|
||||
} else {
|
||||
parts := strings.Split(strings.TrimSpace(cookiestr), ";")
|
||||
@ -79,7 +82,7 @@ func TestDestorySessionCookie(t *testing.T) {
|
||||
|
||||
// request again ,will get same sesssion id .
|
||||
r1, _ := http.NewRequest("GET", "/", nil)
|
||||
r1.Header.Set("Cookie", w.Header().Get("Set-Cookie"))
|
||||
r1.Header.Set("Cookie", w.Header().Get(setCookieKey))
|
||||
w = httptest.NewRecorder()
|
||||
newSession, err := globalSessions.SessionStart(w, r1)
|
||||
if err != nil {
|
||||
@ -92,7 +95,7 @@ func TestDestorySessionCookie(t *testing.T) {
|
||||
// After destroy session , will get a new session id .
|
||||
globalSessions.SessionDestroy(w, r1)
|
||||
r2, _ := http.NewRequest("GET", "/", nil)
|
||||
r2.Header.Set("Cookie", w.Header().Get("Set-Cookie"))
|
||||
r2.Header.Set("Cookie", w.Header().Get(setCookieKey))
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
newSession, err = globalSessions.SessionStart(w, r2)
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// "github.com/beego/beego/v2/session"
|
||||
// "github.com/beego/beego/v2/server/web/session"
|
||||
// )
|
||||
//
|
||||
// func init() {
|
||||
|
||||
@ -19,19 +19,15 @@ import (
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSubstr(t *testing.T) {
|
||||
s := `012345`
|
||||
if Substr(s, 0, 2) != "01" {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
if Substr(s, 0, 100) != "012345" {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
if Substr(s, 12, 100) != "012345" {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
assert.Equal(t, "01", Substr(s, 0, 2))
|
||||
assert.Equal(t, "012345", Substr(s, 0, 100))
|
||||
assert.Equal(t, "012345", Substr(s, 12, 100))
|
||||
}
|
||||
|
||||
func TestHtml2str(t *testing.T) {
|
||||
@ -39,73 +35,51 @@ func TestHtml2str(t *testing.T) {
|
||||
|
||||
|
||||
\n`
|
||||
if HTML2str(h) != "123\\n\n\\n" {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
assert.Equal(t, "123\\n\n\\n", HTML2str(h))
|
||||
}
|
||||
|
||||
func TestDateFormat(t *testing.T) {
|
||||
ts := "Mon, 01 Jul 2013 13:27:42 CST"
|
||||
tt, _ := time.Parse(time.RFC1123, ts)
|
||||
|
||||
if ss := DateFormat(tt, "2006-01-02 15:04:05"); ss != "2013-07-01 13:27:42" {
|
||||
t.Errorf("2013-07-01 13:27:42 does not equal %v", ss)
|
||||
}
|
||||
assert.Equal(t, "2013-07-01 13:27:42", DateFormat(tt, "2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
func TestDate(t *testing.T) {
|
||||
ts := "Mon, 01 Jul 2013 13:27:42 CST"
|
||||
tt, _ := time.Parse(time.RFC1123, ts)
|
||||
|
||||
if ss := Date(tt, "Y-m-d H:i:s"); ss != "2013-07-01 13:27:42" {
|
||||
t.Errorf("2013-07-01 13:27:42 does not equal %v", ss)
|
||||
}
|
||||
if ss := Date(tt, "y-n-j h:i:s A"); ss != "13-7-1 01:27:42 PM" {
|
||||
t.Errorf("13-7-1 01:27:42 PM does not equal %v", ss)
|
||||
}
|
||||
if ss := Date(tt, "D, d M Y g:i:s a"); ss != "Mon, 01 Jul 2013 1:27:42 pm" {
|
||||
t.Errorf("Mon, 01 Jul 2013 1:27:42 pm does not equal %v", ss)
|
||||
}
|
||||
if ss := Date(tt, "l, d F Y G:i:s"); ss != "Monday, 01 July 2013 13:27:42" {
|
||||
t.Errorf("Monday, 01 July 2013 13:27:42 does not equal %v", ss)
|
||||
}
|
||||
assert.Equal(t, "2013-07-01 13:27:42", Date(tt, "Y-m-d H:i:s"))
|
||||
|
||||
assert.Equal(t, "13-7-1 01:27:42 PM", Date(tt, "y-n-j h:i:s A"))
|
||||
assert.Equal(t, "Mon, 01 Jul 2013 1:27:42 pm", Date(tt, "D, d M Y g:i:s a"))
|
||||
assert.Equal(t, "Monday, 01 July 2013 13:27:42", Date(tt, "l, d F Y G:i:s"))
|
||||
}
|
||||
|
||||
func TestCompareRelated(t *testing.T) {
|
||||
if !Compare("abc", "abc") {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
if Compare("abc", "aBc") {
|
||||
t.Error("should be not equal")
|
||||
}
|
||||
if !Compare("1", 1) {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
if CompareNot("abc", "abc") {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
if !CompareNot("abc", "aBc") {
|
||||
t.Error("should be not equal")
|
||||
}
|
||||
if !NotNil("a string") {
|
||||
t.Error("should not be nil")
|
||||
}
|
||||
assert.True(t, Compare("abc", "abc"))
|
||||
|
||||
assert.False(t, Compare("abc", "aBc"))
|
||||
|
||||
assert.True(t, Compare("1", 1))
|
||||
|
||||
assert.False(t, CompareNot("abc", "abc"))
|
||||
|
||||
assert.True(t, CompareNot("abc", "aBc"))
|
||||
assert.True(t, NotNil("a string"))
|
||||
}
|
||||
|
||||
func TestHtmlquote(t *testing.T) {
|
||||
h := `<' ”“&">`
|
||||
s := `<' ”“&">`
|
||||
if Htmlquote(s) != h {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
assert.Equal(t, h, Htmlquote(s))
|
||||
}
|
||||
|
||||
func TestHtmlunquote(t *testing.T) {
|
||||
h := `<' ”“&">`
|
||||
s := `<' ”“&">`
|
||||
if Htmlunquote(h) != s {
|
||||
t.Error("should be equal")
|
||||
}
|
||||
assert.Equal(t, s, Htmlunquote(h))
|
||||
|
||||
}
|
||||
|
||||
func TestParseForm(t *testing.T) {
|
||||
@ -148,55 +122,42 @@ func TestParseForm(t *testing.T) {
|
||||
"hobby": []string{"", "Basketball", "Football"},
|
||||
"memo": []string{"nothing"},
|
||||
}
|
||||
if err := ParseForm(form, u); err == nil {
|
||||
t.Fatal("nothing will be changed")
|
||||
}
|
||||
if err := ParseForm(form, &u); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if u.ID != 0 {
|
||||
t.Errorf("ID should equal 0 but got %v", u.ID)
|
||||
}
|
||||
if len(u.tag) != 0 {
|
||||
t.Errorf("tag's length should equal 0 but got %v", len(u.tag))
|
||||
}
|
||||
if u.Name.(string) != "test" {
|
||||
t.Errorf("Name should equal `test` but got `%v`", u.Name.(string))
|
||||
}
|
||||
if u.Age != 40 {
|
||||
t.Errorf("Age should equal 40 but got %v", u.Age)
|
||||
}
|
||||
if u.Email != "test@gmail.com" {
|
||||
t.Errorf("Email should equal `test@gmail.com` but got `%v`", u.Email)
|
||||
}
|
||||
if u.Intro != "I am an engineer!" {
|
||||
t.Errorf("Intro should equal `I am an engineer!` but got `%v`", u.Intro)
|
||||
}
|
||||
if !u.StrBool {
|
||||
t.Errorf("strboll should equal `true`, but got `%v`", u.StrBool)
|
||||
}
|
||||
|
||||
assert.NotNil(t, ParseForm(form, u))
|
||||
|
||||
assert.Nil(t, ParseForm(form, &u))
|
||||
|
||||
assert.Equal(t, 0, u.ID)
|
||||
|
||||
assert.Equal(t, 0, len(u.tag))
|
||||
|
||||
assert.Equal(t, "test", u.Name)
|
||||
|
||||
assert.Equal(t, 40, u.Age)
|
||||
|
||||
assert.Equal(t, "test@gmail.com", u.Email)
|
||||
|
||||
assert.Equal(t, "I am an engineer!", u.Intro)
|
||||
|
||||
assert.True(t, u.StrBool)
|
||||
|
||||
y, m, d := u.Date.Date()
|
||||
if y != 2014 || m.String() != "November" || d != 12 {
|
||||
t.Errorf("Date should equal `2014-11-12`, but got `%v`", u.Date.String())
|
||||
}
|
||||
if u.Organization != "beego" {
|
||||
t.Errorf("Organization should equal `beego`, but got `%v`", u.Organization)
|
||||
}
|
||||
if u.Title != "CXO" {
|
||||
t.Errorf("Title should equal `CXO`, but got `%v`", u.Title)
|
||||
}
|
||||
if u.Hobby[0] != "" {
|
||||
t.Errorf("Hobby should equal ``, but got `%v`", u.Hobby[0])
|
||||
}
|
||||
if u.Hobby[1] != "Basketball" {
|
||||
t.Errorf("Hobby should equal `Basketball`, but got `%v`", u.Hobby[1])
|
||||
}
|
||||
if u.Hobby[2] != "Football" {
|
||||
t.Errorf("Hobby should equal `Football`, but got `%v`", u.Hobby[2])
|
||||
}
|
||||
if len(u.Memo) != 0 {
|
||||
t.Errorf("Memo's length should equal 0 but got %v", len(u.Memo))
|
||||
}
|
||||
|
||||
assert.Equal(t, 2014, y)
|
||||
assert.Equal(t, "November", m.String())
|
||||
assert.Equal(t, 12, d)
|
||||
|
||||
assert.Equal(t, "beego", u.Organization)
|
||||
|
||||
assert.Equal(t, "CXO", u.Title)
|
||||
|
||||
assert.Equal(t, "", u.Hobby[0])
|
||||
|
||||
assert.Equal(t, "Basketball", u.Hobby[1])
|
||||
|
||||
assert.Equal(t, "Football", u.Hobby[2])
|
||||
|
||||
assert.Equal(t, 0, len(u.Memo))
|
||||
}
|
||||
|
||||
func TestRenderForm(t *testing.T) {
|
||||
@ -212,18 +173,14 @@ func TestRenderForm(t *testing.T) {
|
||||
|
||||
u := user{Name: "test", Intro: "Some Text"}
|
||||
output := RenderForm(u)
|
||||
if output != template.HTML("") {
|
||||
t.Errorf("output should be empty but got %v", output)
|
||||
}
|
||||
assert.Equal(t, template.HTML(""), output)
|
||||
output = RenderForm(&u)
|
||||
result := template.HTML(
|
||||
`Name: <input name="username" type="text" value="test"></br>` +
|
||||
`年龄:<input name="age" type="text" value="0"></br>` +
|
||||
`Sex: <input name="Sex" type="text" value=""></br>` +
|
||||
`Intro: <textarea name="Intro">Some Text</textarea>`)
|
||||
if output != result {
|
||||
t.Errorf("output should equal `%v` but got `%v`", result, output)
|
||||
}
|
||||
assert.Equal(t, result, output)
|
||||
}
|
||||
|
||||
func TestMapGet(t *testing.T) {
|
||||
@ -233,29 +190,18 @@ func TestMapGet(t *testing.T) {
|
||||
"1": 2,
|
||||
}
|
||||
|
||||
if res, err := MapGet(m1, "a"); err == nil {
|
||||
if res.(int64) != 1 {
|
||||
t.Errorf("Should return 1, but return %v", res)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Error happens %v", err)
|
||||
}
|
||||
res, err := MapGet(m1, "a")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(1), res)
|
||||
|
||||
if res, err := MapGet(m1, "1"); err == nil {
|
||||
if res.(int64) != 2 {
|
||||
t.Errorf("Should return 2, but return %v", res)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Error happens %v", err)
|
||||
}
|
||||
res, err = MapGet(m1, "1")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(2), res)
|
||||
|
||||
if res, err := MapGet(m1, 1); err == nil {
|
||||
if res.(int64) != 2 {
|
||||
t.Errorf("Should return 2, but return %v", res)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Error happens %v", err)
|
||||
}
|
||||
|
||||
res, err = MapGet(m1, 1)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(2), res)
|
||||
|
||||
// test 2 level map
|
||||
m2 := M{
|
||||
@ -264,13 +210,9 @@ func TestMapGet(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
if res, err := MapGet(m2, 1, 2); err == nil {
|
||||
if res.(float64) != 3.5 {
|
||||
t.Errorf("Should return 3.5, but return %v", res)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Error happens %v", err)
|
||||
}
|
||||
res, err = MapGet(m2, 1, 2)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3.5, res)
|
||||
|
||||
// test 5 level map
|
||||
m5 := M{
|
||||
@ -285,20 +227,13 @@ func TestMapGet(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
if res, err := MapGet(m5, 1, 2, 3, 4, 5); err == nil {
|
||||
if res.(float64) != 1.2 {
|
||||
t.Errorf("Should return 1.2, but return %v", res)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Error happens %v", err)
|
||||
}
|
||||
res, err = MapGet(m5, 1, 2, 3, 4, 5)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1.2, res)
|
||||
|
||||
// check whether element not exists in map
|
||||
if res, err := MapGet(m5, 5, 4, 3, 2, 1); err == nil {
|
||||
if res != nil {
|
||||
t.Errorf("Should return nil, but return %v", res)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Error happens %v", err)
|
||||
}
|
||||
res, err = MapGet(m5, 5, 4, 3, 2, 1)
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, res)
|
||||
|
||||
}
|
||||
|
||||
@ -21,13 +21,16 @@ import (
|
||||
)
|
||||
|
||||
func TestStatics(t *testing.T) {
|
||||
StatisticsMap.AddStatistics("POST", "/api/user", "&admin.user", time.Duration(2000))
|
||||
StatisticsMap.AddStatistics("POST", "/api/user", "&admin.user", time.Duration(120000))
|
||||
StatisticsMap.AddStatistics("GET", "/api/user", "&admin.user", time.Duration(13000))
|
||||
StatisticsMap.AddStatistics("POST", "/api/admin", "&admin.user", time.Duration(14000))
|
||||
StatisticsMap.AddStatistics("POST", "/api/user/astaxie", "&admin.user", time.Duration(12000))
|
||||
StatisticsMap.AddStatistics("POST", "/api/user/xiemengjun", "&admin.user", time.Duration(13000))
|
||||
StatisticsMap.AddStatistics("DELETE", "/api/user", "&admin.user", time.Duration(1400))
|
||||
userApi := "/api/user"
|
||||
post := "POST"
|
||||
adminUser := "&admin.user"
|
||||
StatisticsMap.AddStatistics(post, userApi, adminUser, time.Duration(2000))
|
||||
StatisticsMap.AddStatistics(post, userApi, adminUser, time.Duration(120000))
|
||||
StatisticsMap.AddStatistics("GET", userApi, adminUser, time.Duration(13000))
|
||||
StatisticsMap.AddStatistics(post, "/api/admin", adminUser, time.Duration(14000))
|
||||
StatisticsMap.AddStatistics(post, "/api/user/astaxie", adminUser, time.Duration(12000))
|
||||
StatisticsMap.AddStatistics(post, "/api/user/xiemengjun", adminUser, time.Duration(13000))
|
||||
StatisticsMap.AddStatistics("DELETE", userApi, adminUser, time.Duration(1400))
|
||||
t.Log(StatisticsMap.GetMap())
|
||||
|
||||
data := StatisticsMap.GetMapData()
|
||||
|
||||
@ -289,3 +289,7 @@ func (o *oldToNewAdapter) SetPrev(ctx context.Context, t time.Time) {
|
||||
func (o *oldToNewAdapter) GetPrev(ctx context.Context) time.Time {
|
||||
return o.delegate.GetPrev()
|
||||
}
|
||||
|
||||
func (o *oldToNewAdapter) GetTimeout(ctx context.Context) time.Duration {
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -7,8 +7,8 @@ package controllers
|
||||
|
||||
import (
|
||||
"github.com/beego/beego/v2"
|
||||
"github.com/beego/beego/v2/cache"
|
||||
"github.com/beego/beego/v2/utils/captcha"
|
||||
"github.com/beego/beego/v2/client/cache"
|
||||
"github.com/beego/beego/v2/server/web/captcha"
|
||||
)
|
||||
|
||||
var cpt *captcha.Captcha
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
//
|
||||
// import (
|
||||
// "github.com/beego/beego/v2"
|
||||
// "github.com/beego/beego/v2/cache"
|
||||
// "github.com/beego/beego/v2/utils/captcha"
|
||||
// "github.com/beego/beego/v2/client/cache"
|
||||
// "github.com/beego/beego/v2/server/web/captcha"
|
||||
// )
|
||||
//
|
||||
// var cpt *captcha.Captcha
|
||||
|
||||
@ -8,7 +8,7 @@ In your beego.Controller:
|
||||
|
||||
package controllers
|
||||
|
||||
import "github.com/beego/beego/v2/utils/pagination"
|
||||
import "github.com/beego/beego/v2/server/web/pagination"
|
||||
|
||||
type PostsController struct {
|
||||
beego.Controller
|
||||
|
||||
@ -16,7 +16,7 @@ package utils
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestRand_01(t *testing.T) {
|
||||
func TestRand01(t *testing.T) {
|
||||
bs0 := RandomCreateBytes(16)
|
||||
bs1 := RandomCreateBytes(16)
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
// Package validation for validations
|
||||
//
|
||||
// import (
|
||||
// "github.com/beego/beego/v2/validation"
|
||||
// "github.com/beego/beego/v2/core/validation"
|
||||
// "log"
|
||||
// )
|
||||
//
|
||||
|
||||
@ -18,131 +18,83 @@ import (
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRequired(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Required(nil, "nil").Ok {
|
||||
t.Error("nil object should be false")
|
||||
}
|
||||
if !valid.Required(true, "bool").Ok {
|
||||
t.Error("Bool value should always return true")
|
||||
}
|
||||
if !valid.Required(false, "bool").Ok {
|
||||
t.Error("Bool value should always return true")
|
||||
}
|
||||
if valid.Required("", "string").Ok {
|
||||
t.Error("\"'\" string should be false")
|
||||
}
|
||||
if valid.Required(" ", "string").Ok {
|
||||
t.Error("\" \" string should be false") // For #2361
|
||||
}
|
||||
if valid.Required("\n", "string").Ok {
|
||||
t.Error("new line string should be false") // For #2361
|
||||
}
|
||||
if !valid.Required("astaxie", "string").Ok {
|
||||
t.Error("string should be true")
|
||||
}
|
||||
if valid.Required(0, "zero").Ok {
|
||||
t.Error("Integer should not be equal 0")
|
||||
}
|
||||
if !valid.Required(1, "int").Ok {
|
||||
t.Error("Integer except 0 should be true")
|
||||
}
|
||||
if !valid.Required(time.Now(), "time").Ok {
|
||||
t.Error("time should be true")
|
||||
}
|
||||
if valid.Required([]string{}, "emptySlice").Ok {
|
||||
t.Error("empty slice should be false")
|
||||
}
|
||||
if !valid.Required([]interface{}{"ok"}, "slice").Ok {
|
||||
t.Error("slice should be true")
|
||||
}
|
||||
assert.False(t, valid.Required(nil, "nil").Ok)
|
||||
assert.True(t, valid.Required(true, "bool").Ok)
|
||||
|
||||
assert.True(t, valid.Required(false, "bool").Ok)
|
||||
assert.False(t, valid.Required("", "string").Ok)
|
||||
assert.False(t, valid.Required(" ", "string").Ok)
|
||||
assert.False(t, valid.Required("\n", "string").Ok)
|
||||
|
||||
assert.True(t, valid.Required("astaxie", "string").Ok)
|
||||
assert.False(t, valid.Required(0, "zero").Ok)
|
||||
|
||||
assert.True(t, valid.Required(1, "int").Ok)
|
||||
|
||||
assert.True(t, valid.Required(time.Now(), "time").Ok)
|
||||
|
||||
assert.False(t, valid.Required([]string{}, "emptySlice").Ok)
|
||||
|
||||
assert.True(t, valid.Required([]interface{}{"ok"}, "slice").Ok)
|
||||
}
|
||||
|
||||
func TestMin(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Min(-1, 0, "min0").Ok {
|
||||
t.Error("-1 is less than the minimum value of 0 should be false")
|
||||
}
|
||||
if !valid.Min(1, 0, "min0").Ok {
|
||||
t.Error("1 is greater or equal than the minimum value of 0 should be true")
|
||||
}
|
||||
assert.False(t, valid.Min(-1, 0, "min0").Ok)
|
||||
assert.True(t, valid.Min(1, 0, "min0").Ok)
|
||||
|
||||
}
|
||||
|
||||
func TestMax(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Max(1, 0, "max0").Ok {
|
||||
t.Error("1 is greater than the minimum value of 0 should be false")
|
||||
}
|
||||
if !valid.Max(-1, 0, "max0").Ok {
|
||||
t.Error("-1 is less or equal than the maximum value of 0 should be true")
|
||||
}
|
||||
assert.False(t, valid.Max(1, 0, "max0").Ok)
|
||||
assert.True(t, valid.Max(-1, 0, "max0").Ok)
|
||||
}
|
||||
|
||||
func TestRange(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Range(-1, 0, 1, "range0_1").Ok {
|
||||
t.Error("-1 is between 0 and 1 should be false")
|
||||
}
|
||||
if !valid.Range(1, 0, 1, "range0_1").Ok {
|
||||
t.Error("1 is between 0 and 1 should be true")
|
||||
}
|
||||
assert.False(t, valid.Range(-1, 0, 1, "range0_1").Ok)
|
||||
|
||||
assert.True(t, valid.Range(1, 0, 1, "range0_1").Ok)
|
||||
}
|
||||
|
||||
func TestMinSize(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.MinSize("", 1, "minSize1").Ok {
|
||||
t.Error("the length of \"\" is less than the minimum value of 1 should be false")
|
||||
}
|
||||
if !valid.MinSize("ok", 1, "minSize1").Ok {
|
||||
t.Error("the length of \"ok\" is greater or equal than the minimum value of 1 should be true")
|
||||
}
|
||||
if valid.MinSize([]string{}, 1, "minSize1").Ok {
|
||||
t.Error("the length of empty slice is less than the minimum value of 1 should be false")
|
||||
}
|
||||
if !valid.MinSize([]interface{}{"ok"}, 1, "minSize1").Ok {
|
||||
t.Error("the length of [\"ok\"] is greater or equal than the minimum value of 1 should be true")
|
||||
}
|
||||
assert.False(t, valid.MinSize("", 1, "minSize1").Ok)
|
||||
|
||||
assert.True(t, valid.MinSize("ok", 1, "minSize1").Ok)
|
||||
assert.False(t, valid.MinSize([]string{}, 1, "minSize1").Ok)
|
||||
assert.True(t, valid.MinSize([]interface{}{"ok"}, 1, "minSize1").Ok)
|
||||
}
|
||||
|
||||
func TestMaxSize(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.MaxSize("ok", 1, "maxSize1").Ok {
|
||||
t.Error("the length of \"ok\" is greater than the maximum value of 1 should be false")
|
||||
}
|
||||
if !valid.MaxSize("", 1, "maxSize1").Ok {
|
||||
t.Error("the length of \"\" is less or equal than the maximum value of 1 should be true")
|
||||
}
|
||||
if valid.MaxSize([]interface{}{"ok", false}, 1, "maxSize1").Ok {
|
||||
t.Error("the length of [\"ok\", false] is greater than the maximum value of 1 should be false")
|
||||
}
|
||||
if !valid.MaxSize([]string{}, 1, "maxSize1").Ok {
|
||||
t.Error("the length of empty slice is less or equal than the maximum value of 1 should be true")
|
||||
}
|
||||
assert.False(t, valid.MaxSize("ok", 1, "maxSize1").Ok)
|
||||
assert.True(t, valid.MaxSize("", 1, "maxSize1").Ok)
|
||||
assert.False(t, valid.MaxSize([]interface{}{"ok", false}, 1, "maxSize1").Ok)
|
||||
assert.True(t, valid.MaxSize([]string{}, 1, "maxSize1").Ok)
|
||||
}
|
||||
|
||||
func TestLength(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Length("", 1, "length1").Ok {
|
||||
t.Error("the length of \"\" must equal 1 should be false")
|
||||
}
|
||||
if !valid.Length("1", 1, "length1").Ok {
|
||||
t.Error("the length of \"1\" must equal 1 should be true")
|
||||
}
|
||||
if valid.Length([]string{}, 1, "length1").Ok {
|
||||
t.Error("the length of empty slice must equal 1 should be false")
|
||||
}
|
||||
if !valid.Length([]interface{}{"ok"}, 1, "length1").Ok {
|
||||
t.Error("the length of [\"ok\"] must equal 1 should be true")
|
||||
}
|
||||
assert.False(t, valid.Length("", 1, "length1").Ok)
|
||||
assert.True(t, valid.Length("1", 1, "length1").Ok)
|
||||
|
||||
assert.False(t, valid.Length([]string{}, 1, "length1").Ok)
|
||||
assert.True(t, valid.Length([]interface{}{"ok"}, 1, "length1").Ok)
|
||||
}
|
||||
|
||||
func TestAlpha(t *testing.T) {
|
||||
@ -178,13 +130,16 @@ func TestAlphaNumeric(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
const email = "suchuangji@gmail.com"
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Match("suchuangji@gmail", regexp.MustCompile(`^\w+@\w+\.\w+$`), "match").Ok {
|
||||
t.Error("\"suchuangji@gmail\" match \"^\\w+@\\w+\\.\\w+$\" should be false")
|
||||
}
|
||||
if !valid.Match("suchuangji@gmail.com", regexp.MustCompile(`^\w+@\w+\.\w+$`), "match").Ok {
|
||||
|
||||
if !valid.Match(email, regexp.MustCompile(`^\w+@\w+\.\w+$`), "match").Ok {
|
||||
t.Error("\"suchuangji@gmail\" match \"^\\w+@\\w+\\.\\w+$\" should be true")
|
||||
}
|
||||
}
|
||||
@ -217,7 +172,7 @@ func TestEmail(t *testing.T) {
|
||||
if valid.Email("not@a email", "email").Ok {
|
||||
t.Error("\"not@a email\" is a valid email address should be false")
|
||||
}
|
||||
if !valid.Email("suchuangji@gmail.com", "email").Ok {
|
||||
if !valid.Email(email, "email").Ok {
|
||||
t.Error("\"suchuangji@gmail.com\" is a valid email address should be true")
|
||||
}
|
||||
if valid.Email("@suchuangji@gmail.com", "email").Ok {
|
||||
@ -242,7 +197,7 @@ func TestIP(t *testing.T) {
|
||||
func TestBase64(t *testing.T) {
|
||||
valid := Validation{}
|
||||
|
||||
if valid.Base64("suchuangji@gmail.com", "base64").Ok {
|
||||
if valid.Base64(email, "base64").Ok {
|
||||
t.Error("\"suchuangji@gmail.com\" are a valid base64 characters should be false")
|
||||
}
|
||||
if !valid.Base64("c3VjaHVhbmdqaUBnbWFpbC5jb20=", "base64").Ok {
|
||||
@ -370,44 +325,25 @@ func TestValid(t *testing.T) {
|
||||
|
||||
u := user{Name: "test@/test/;com", Age: 40}
|
||||
b, err := valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !b {
|
||||
t.Error("validation should be passed")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, b)
|
||||
|
||||
uptr := &user{Name: "test", Age: 40}
|
||||
valid.Clear()
|
||||
b, err = valid.Valid(uptr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Error("validation should not be passed")
|
||||
}
|
||||
if len(valid.Errors) != 1 {
|
||||
t.Fatalf("valid errors len should be 1 but got %d", len(valid.Errors))
|
||||
}
|
||||
if valid.Errors[0].Key != "Name.Match" {
|
||||
t.Errorf("Message key should be `Name.Match` but got %s", valid.Errors[0].Key)
|
||||
}
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, b)
|
||||
assert.Equal(t, 1, len(valid.Errors))
|
||||
assert.Equal(t, "Name.Match", valid.Errors[0].Key)
|
||||
|
||||
u = user{Name: "test@/test/;com", Age: 180}
|
||||
valid.Clear()
|
||||
b, err = valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Error("validation should not be passed")
|
||||
}
|
||||
if len(valid.Errors) != 1 {
|
||||
t.Fatalf("valid errors len should be 1 but got %d", len(valid.Errors))
|
||||
}
|
||||
if valid.Errors[0].Key != "Age.Range." {
|
||||
t.Errorf("Message key should be `Age.Range` but got %s", valid.Errors[0].Key)
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, b)
|
||||
assert.Equal(t, 1, len(valid.Errors))
|
||||
assert.Equal(t, "Age.Range.", valid.Errors[0].Key)
|
||||
}
|
||||
|
||||
func TestRecursiveValid(t *testing.T) {
|
||||
@ -432,12 +368,8 @@ func TestRecursiveValid(t *testing.T) {
|
||||
|
||||
u := Account{Password: "abc123_", U: User{}}
|
||||
b, err := valid.RecursiveValid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Error("validation should not be passed")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, b)
|
||||
}
|
||||
|
||||
func TestSkipValid(t *testing.T) {
|
||||
@ -474,21 +406,13 @@ func TestSkipValid(t *testing.T) {
|
||||
|
||||
valid := Validation{}
|
||||
b, err := valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Fatal("validation should not be passed")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, b)
|
||||
|
||||
valid = Validation{RequiredFirst: true}
|
||||
b, err = valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !b {
|
||||
t.Fatal("validation should be passed")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, b)
|
||||
}
|
||||
|
||||
func TestPointer(t *testing.T) {
|
||||
@ -506,12 +430,8 @@ func TestPointer(t *testing.T) {
|
||||
|
||||
valid := Validation{}
|
||||
b, err := valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Fatal("validation should not be passed")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, b)
|
||||
|
||||
validEmail := "a@a.com"
|
||||
u = User{
|
||||
@ -521,12 +441,8 @@ func TestPointer(t *testing.T) {
|
||||
|
||||
valid = Validation{RequiredFirst: true}
|
||||
b, err = valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !b {
|
||||
t.Fatal("validation should be passed")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, b)
|
||||
|
||||
u = User{
|
||||
ReqEmail: &validEmail,
|
||||
@ -535,12 +451,8 @@ func TestPointer(t *testing.T) {
|
||||
|
||||
valid = Validation{}
|
||||
b, err = valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Fatal("validation should not be passed")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, b)
|
||||
|
||||
invalidEmail := "a@a"
|
||||
u = User{
|
||||
@ -550,12 +462,8 @@ func TestPointer(t *testing.T) {
|
||||
|
||||
valid = Validation{RequiredFirst: true}
|
||||
b, err = valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Fatal("validation should not be passed")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, b)
|
||||
|
||||
u = User{
|
||||
ReqEmail: &validEmail,
|
||||
@ -564,12 +472,8 @@ func TestPointer(t *testing.T) {
|
||||
|
||||
valid = Validation{}
|
||||
b, err = valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Fatal("validation should not be passed")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, b)
|
||||
}
|
||||
|
||||
func TestCanSkipAlso(t *testing.T) {
|
||||
@ -589,21 +493,14 @@ func TestCanSkipAlso(t *testing.T) {
|
||||
|
||||
valid := Validation{RequiredFirst: true}
|
||||
b, err := valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if b {
|
||||
t.Fatal("validation should not be passed")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, b)
|
||||
|
||||
valid = Validation{RequiredFirst: true}
|
||||
valid.CanSkipAlso("Range")
|
||||
b, err = valid.Valid(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !b {
|
||||
t.Fatal("validation should be passed")
|
||||
}
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, b)
|
||||
|
||||
}
|
||||
|
||||
4
client/cache/README.md
vendored
4
client/cache/README.md
vendored
@ -4,7 +4,7 @@ cache is a Go cache manager. It can use many cache adapters. The repo is inspire
|
||||
|
||||
## How to install?
|
||||
|
||||
go get github.com/beego/beego/v2/cache
|
||||
go get github.com/beego/beego/v2/client/cache
|
||||
|
||||
## What adapters are supported?
|
||||
|
||||
@ -15,7 +15,7 @@ As of now this cache support memory, Memcache and Redis.
|
||||
First you must import it
|
||||
|
||||
import (
|
||||
"github.com/beego/beego/v2/cache"
|
||||
"github.com/beego/beego/v2/client/cache"
|
||||
)
|
||||
|
||||
Then init a Cache (example with memory adapter)
|
||||
|
||||
11
client/cache/cache.go
vendored
11
client/cache/cache.go
vendored
@ -16,7 +16,7 @@
|
||||
// Usage:
|
||||
//
|
||||
// import(
|
||||
// "github.com/beego/beego/v2/cache"
|
||||
// "github.com/beego/beego/v2/client/cache"
|
||||
// )
|
||||
//
|
||||
// bm, err := cache.NewCache("memory", `{"interval":60}`)
|
||||
@ -33,8 +33,9 @@ package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
// Cache interface contains all behaviors for cache adapter.
|
||||
@ -55,12 +56,14 @@ type Cache interface {
|
||||
// Set a cached value with key and expire time.
|
||||
Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error
|
||||
// Delete cached value by key.
|
||||
// Should not return error if key not found
|
||||
Delete(ctx context.Context, key string) error
|
||||
// Increment a cached int value by key, as a counter.
|
||||
Incr(ctx context.Context, key string) error
|
||||
// Decrement a cached int value by key, as a counter.
|
||||
Decr(ctx context.Context, key string) error
|
||||
// Check if a cached value exists or not.
|
||||
// if key is expired, return (false, nil)
|
||||
IsExist(ctx context.Context, key string) (bool, error)
|
||||
// Clear all cache.
|
||||
ClearAll(ctx context.Context) error
|
||||
@ -78,7 +81,7 @@ var adapters = make(map[string]Instance)
|
||||
// it panics.
|
||||
func Register(name string, adapter Instance) {
|
||||
if adapter == nil {
|
||||
panic("cache: Register adapter is nil")
|
||||
panic(berror.Error(NilCacheAdapter, "cache: Register adapter is nil").Error())
|
||||
}
|
||||
if _, ok := adapters[name]; ok {
|
||||
panic("cache: Register called twice for adapter " + name)
|
||||
@ -92,7 +95,7 @@ func Register(name string, adapter Instance) {
|
||||
func NewCache(adapterName, config string) (adapter Cache, err error) {
|
||||
instanceFunc, ok := adapters[adapterName]
|
||||
if !ok {
|
||||
err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName)
|
||||
err = berror.Errorf(UnknownAdapter, "cache: unknown adapter name %s (forgot to import?)", adapterName)
|
||||
return
|
||||
}
|
||||
adapter = instanceFunc()
|
||||
|
||||
231
client/cache/cache_test.go
vendored
231
client/cache/cache_test.go
vendored
@ -16,17 +16,19 @@ package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCacheIncr(t *testing.T) {
|
||||
bm, err := NewCache("memory", `{"interval":20}`)
|
||||
if err != nil {
|
||||
t.Error("init err")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
// timeoutDuration := 10 * time.Second
|
||||
|
||||
bm.Put(context.Background(), "edwardhey", 0, time.Second*20)
|
||||
@ -46,11 +48,9 @@ func TestCacheIncr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
bm, err := NewCache("memory", `{"interval":20}`)
|
||||
if err != nil {
|
||||
t.Error("init err")
|
||||
}
|
||||
timeoutDuration := 10 * time.Second
|
||||
bm, err := NewCache("memory", `{"interval":1}`)
|
||||
assert.Nil(t, err)
|
||||
timeoutDuration := 5 * time.Second
|
||||
if err = bm.Put(context.Background(), "astaxie", 1, timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
@ -62,7 +62,7 @@ func TestCache(t *testing.T) {
|
||||
t.Error("get err")
|
||||
}
|
||||
|
||||
time.Sleep(30 * time.Second)
|
||||
time.Sleep(7 * time.Second)
|
||||
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie"); res {
|
||||
t.Error("check err")
|
||||
@ -73,130 +73,97 @@ func TestCache(t *testing.T) {
|
||||
}
|
||||
|
||||
// test different integer type for incr & decr
|
||||
testMultiIncrDecr(t, bm, timeoutDuration)
|
||||
testMultiTypeIncrDecr(t, bm, timeoutDuration)
|
||||
|
||||
// test overflow of incr&decr
|
||||
testIncrOverFlow(t, bm, timeoutDuration)
|
||||
testDecrOverFlow(t, bm, timeoutDuration)
|
||||
|
||||
bm.Delete(context.Background(), "astaxie")
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie"); res {
|
||||
t.Error("delete err")
|
||||
}
|
||||
res, _ := bm.IsExist(context.Background(), "astaxie")
|
||||
assert.False(t, res)
|
||||
|
||||
// test GetMulti
|
||||
if err = bm.Put(context.Background(), "astaxie", "author", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie"); !res {
|
||||
t.Error("check err")
|
||||
}
|
||||
if v, _ := bm.Get(context.Background(), "astaxie"); v.(string) != "author" {
|
||||
t.Error("get err")
|
||||
}
|
||||
assert.Nil(t, bm.Put(context.Background(), "astaxie", "author", timeoutDuration))
|
||||
|
||||
if err = bm.Put(context.Background(), "astaxie1", "author1", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie1"); !res {
|
||||
t.Error("check err")
|
||||
}
|
||||
res, _ = bm.IsExist(context.Background(), "astaxie")
|
||||
assert.True(t, res)
|
||||
|
||||
v, _ := bm.Get(context.Background(), "astaxie")
|
||||
assert.Equal(t, "author", v)
|
||||
|
||||
assert.Nil(t, bm.Put(context.Background(), "astaxie1", "author1", timeoutDuration))
|
||||
|
||||
res, _ = bm.IsExist(context.Background(), "astaxie1")
|
||||
assert.True(t, res)
|
||||
|
||||
vv, _ := bm.GetMulti(context.Background(), []string{"astaxie", "astaxie1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[0].(string) != "author" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[1].(string) != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
assert.Equal(t, 2, len(vv))
|
||||
assert.Equal(t, "author", vv[0])
|
||||
assert.Equal(t,"author1", vv[1])
|
||||
|
||||
|
||||
|
||||
vv, err = bm.GetMulti(context.Background(), []string{"astaxie0", "astaxie1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[0] != nil {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[1].(string) != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if err != nil && err.Error() != "key [astaxie0] error: the key isn't exist" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
assert.Equal(t, 2, len(vv))
|
||||
assert.Nil(t, vv[0])
|
||||
assert.Equal(t, "author1", vv[1])
|
||||
|
||||
assert.NotNil(t, err)
|
||||
assert.True(t, strings.Contains(err.Error(), "key isn't exist"))
|
||||
}
|
||||
|
||||
func TestFileCache(t *testing.T) {
|
||||
bm, err := NewCache("file", `{"CachePath":"cache","FileSuffix":".bin","DirectoryLevel":"2","EmbedExpiry":"0"}`)
|
||||
if err != nil {
|
||||
t.Error("init err")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
timeoutDuration := 10 * time.Second
|
||||
if err = bm.Put(context.Background(), "astaxie", 1, timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie"); !res {
|
||||
t.Error("check err")
|
||||
}
|
||||
assert.Nil(t, bm.Put(context.Background(), "astaxie", 1, timeoutDuration))
|
||||
|
||||
if v, _ := bm.Get(context.Background(), "astaxie"); v.(int) != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
res, _ := bm.IsExist(context.Background(), "astaxie")
|
||||
assert.True(t, res)
|
||||
v, _ := bm.Get(context.Background(), "astaxie")
|
||||
assert.Equal(t, 1, v)
|
||||
|
||||
// test different integer type for incr & decr
|
||||
testMultiIncrDecr(t, bm, timeoutDuration)
|
||||
testMultiTypeIncrDecr(t, bm, timeoutDuration)
|
||||
|
||||
// test overflow of incr&decr
|
||||
testIncrOverFlow(t, bm, timeoutDuration)
|
||||
testDecrOverFlow(t, bm, timeoutDuration)
|
||||
|
||||
bm.Delete(context.Background(), "astaxie")
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie"); res {
|
||||
t.Error("delete err")
|
||||
}
|
||||
res, _ = bm.IsExist(context.Background(), "astaxie")
|
||||
assert.False(t, res)
|
||||
|
||||
// test string
|
||||
if err = bm.Put(context.Background(), "astaxie", "author", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie"); !res {
|
||||
t.Error("check err")
|
||||
}
|
||||
if v, _ := bm.Get(context.Background(), "astaxie"); v.(string) != "author" {
|
||||
t.Error("get err")
|
||||
}
|
||||
assert.Nil(t, bm.Put(context.Background(), "astaxie", "author", timeoutDuration))
|
||||
res, _ = bm.IsExist(context.Background(), "astaxie")
|
||||
assert.True(t, res)
|
||||
|
||||
v, _ = bm.Get(context.Background(), "astaxie")
|
||||
assert.Equal(t, "author", v)
|
||||
|
||||
// test GetMulti
|
||||
if err = bm.Put(context.Background(), "astaxie1", "author1", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie1"); !res {
|
||||
t.Error("check err")
|
||||
}
|
||||
assert.Nil(t, bm.Put(context.Background(), "astaxie1", "author1", timeoutDuration))
|
||||
|
||||
res, _ = bm.IsExist(context.Background(), "astaxie1")
|
||||
assert.True(t, res)
|
||||
|
||||
vv, _ := bm.GetMulti(context.Background(), []string{"astaxie", "astaxie1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[0].(string) != "author" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[1].(string) != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
assert.Equal(t, 2, len(vv))
|
||||
assert.Equal(t, "author", vv[0])
|
||||
assert.Equal(t, "author1", vv[1])
|
||||
|
||||
vv, err = bm.GetMulti(context.Background(), []string{"astaxie0", "astaxie1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[0] != nil {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[1].(string) != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if err == nil {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
assert.Equal(t, 2, len(vv))
|
||||
|
||||
assert.Nil(t, vv[0])
|
||||
|
||||
assert.Equal(t, "author1", vv[1])
|
||||
assert.NotNil(t, err)
|
||||
os.RemoveAll("cache")
|
||||
}
|
||||
|
||||
func testMultiIncrDecr(t *testing.T, c Cache, timeout time.Duration) {
|
||||
func testMultiTypeIncrDecr(t *testing.T, c Cache, timeout time.Duration) {
|
||||
testIncrDecr(t, c, 1, 2, timeout)
|
||||
testIncrDecr(t, c, int32(1), int32(2), timeout)
|
||||
testIncrDecr(t, c, int64(1), int64(2), timeout)
|
||||
@ -206,30 +173,52 @@ func testMultiIncrDecr(t *testing.T, c Cache, timeout time.Duration) {
|
||||
}
|
||||
|
||||
func testIncrDecr(t *testing.T, c Cache, beforeIncr interface{}, afterIncr interface{}, timeout time.Duration) {
|
||||
var err error
|
||||
ctx := context.Background()
|
||||
key := "incDecKey"
|
||||
if err = c.Put(ctx, key, beforeIncr, timeout); err != nil {
|
||||
t.Error("Get Error", err)
|
||||
}
|
||||
|
||||
if err = c.Incr(ctx, key); err != nil {
|
||||
t.Error("Incr Error", err)
|
||||
}
|
||||
assert.Nil(t, c.Put(ctx, key, beforeIncr, timeout))
|
||||
assert.Nil(t, c.Incr(ctx, key))
|
||||
|
||||
if v, _ := c.Get(ctx, key); v != afterIncr {
|
||||
t.Error("Get Error")
|
||||
}
|
||||
|
||||
if err = c.Decr(ctx, key); err != nil {
|
||||
t.Error("Decr Error", err)
|
||||
}
|
||||
v, _ := c.Get(ctx, key)
|
||||
assert.Equal(t, afterIncr, v)
|
||||
|
||||
if v, _ := c.Get(ctx, key); v != beforeIncr {
|
||||
t.Error("Get Error")
|
||||
}
|
||||
assert.Nil(t, c.Decr(ctx, key))
|
||||
|
||||
if err := c.Delete(ctx, key); err != nil {
|
||||
t.Error("Delete Error")
|
||||
v, _ = c.Get(ctx, key)
|
||||
assert.Equal(t, v, beforeIncr)
|
||||
assert.Nil(t, c.Delete(ctx, key))
|
||||
}
|
||||
|
||||
func testIncrOverFlow(t *testing.T, c Cache, timeout time.Duration) {
|
||||
ctx := context.Background()
|
||||
key := "incKey"
|
||||
|
||||
assert.Nil(t, c.Put(ctx, key, int64(math.MaxInt64), timeout))
|
||||
// int64
|
||||
defer func() {
|
||||
assert.Nil(t, c.Delete(ctx, key))
|
||||
}()
|
||||
assert.NotNil(t, c.Incr(ctx, key))
|
||||
}
|
||||
|
||||
func testDecrOverFlow(t *testing.T, c Cache, timeout time.Duration) {
|
||||
var err error
|
||||
ctx := context.Background()
|
||||
key := "decKey"
|
||||
|
||||
// int64
|
||||
if err = c.Put(ctx, key, int64(math.MinInt64), timeout); err != nil {
|
||||
t.Error("Put Error: ", err.Error())
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err = c.Delete(ctx, key); err != nil {
|
||||
t.Errorf("Delete error: %s", err.Error())
|
||||
}
|
||||
}()
|
||||
if err = c.Decr(ctx, key); err == nil {
|
||||
t.Error("Decr error")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
92
client/cache/calc_utils.go
vendored
Normal file
92
client/cache/calc_utils.go
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrIncrementOverflow = berror.Error(IncrementOverflow, "this incr invocation will overflow.")
|
||||
ErrDecrementOverflow = berror.Error(DecrementOverflow, "this decr invocation will overflow.")
|
||||
ErrNotIntegerType = berror.Error(NotIntegerType, "item val is not (u)int (u)int32 (u)int64")
|
||||
)
|
||||
|
||||
|
||||
|
||||
func incr(originVal interface{}) (interface{}, error) {
|
||||
switch val := originVal.(type) {
|
||||
case int:
|
||||
tmp := val + 1
|
||||
if val > 0 && tmp < 0 {
|
||||
return nil, ErrIncrementOverflow
|
||||
}
|
||||
return tmp, nil
|
||||
case int32:
|
||||
if val == math.MaxInt32 {
|
||||
return nil, ErrIncrementOverflow
|
||||
}
|
||||
return val + 1, nil
|
||||
case int64:
|
||||
if val == math.MaxInt64 {
|
||||
return nil, ErrIncrementOverflow
|
||||
}
|
||||
return val + 1, nil
|
||||
case uint:
|
||||
tmp := val + 1
|
||||
if tmp < val {
|
||||
return nil, ErrIncrementOverflow
|
||||
}
|
||||
return tmp, nil
|
||||
case uint32:
|
||||
if val == math.MaxUint32 {
|
||||
return nil, ErrIncrementOverflow
|
||||
}
|
||||
return val + 1, nil
|
||||
case uint64:
|
||||
if val == math.MaxUint64 {
|
||||
return nil, ErrIncrementOverflow
|
||||
}
|
||||
return val + 1, nil
|
||||
default:
|
||||
return nil, ErrNotIntegerType
|
||||
}
|
||||
}
|
||||
|
||||
func decr(originVal interface{}) (interface{}, error) {
|
||||
switch val := originVal.(type) {
|
||||
case int:
|
||||
tmp := val - 1
|
||||
if val < 0 && tmp > 0 {
|
||||
return nil, ErrDecrementOverflow
|
||||
}
|
||||
return tmp, nil
|
||||
case int32:
|
||||
if val == math.MinInt32 {
|
||||
return nil, ErrDecrementOverflow
|
||||
}
|
||||
return val - 1, nil
|
||||
case int64:
|
||||
if val == math.MinInt64 {
|
||||
return nil, ErrDecrementOverflow
|
||||
}
|
||||
return val - 1, nil
|
||||
case uint:
|
||||
if val == 0 {
|
||||
return nil, ErrDecrementOverflow
|
||||
}
|
||||
return val - 1, nil
|
||||
case uint32:
|
||||
if val == 0 {
|
||||
return nil, ErrDecrementOverflow
|
||||
}
|
||||
return val - 1, nil
|
||||
case uint64:
|
||||
if val == 0 {
|
||||
return nil, ErrDecrementOverflow
|
||||
}
|
||||
return val - 1, nil
|
||||
default:
|
||||
return nil, ErrNotIntegerType
|
||||
}
|
||||
}
|
||||
140
client/cache/calc_utils_test.go
vendored
Normal file
140
client/cache/calc_utils_test.go
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIncr(t *testing.T) {
|
||||
// int
|
||||
var originVal interface{} = int(1)
|
||||
var updateVal interface{} = int(2)
|
||||
val, err := incr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = incr(int(1<<(strconv.IntSize-1) - 1))
|
||||
assert.Equal(t, ErrIncrementOverflow, err)
|
||||
|
||||
// int32
|
||||
originVal = int32(1)
|
||||
updateVal = int32(2)
|
||||
val, err = incr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = incr(int32(math.MaxInt32))
|
||||
assert.Equal(t, ErrIncrementOverflow, err)
|
||||
|
||||
// int64
|
||||
originVal = int64(1)
|
||||
updateVal = int64(2)
|
||||
val, err = incr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = incr(int64(math.MaxInt64))
|
||||
assert.Equal(t, ErrIncrementOverflow, err)
|
||||
|
||||
// uint
|
||||
originVal = uint(1)
|
||||
updateVal = uint(2)
|
||||
val, err = incr(originVal)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = incr(uint(1<<(strconv.IntSize) - 1))
|
||||
assert.Equal(t, ErrIncrementOverflow, err)
|
||||
|
||||
// uint32
|
||||
originVal = uint32(1)
|
||||
updateVal = uint32(2)
|
||||
val, err = incr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = incr(uint32(math.MaxUint32))
|
||||
assert.Equal(t, ErrIncrementOverflow, err)
|
||||
|
||||
// uint64
|
||||
originVal = uint64(1)
|
||||
updateVal = uint64(2)
|
||||
val, err = incr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = incr(uint64(math.MaxUint64))
|
||||
assert.Equal(t, ErrIncrementOverflow, err)
|
||||
// other type
|
||||
_, err = incr("string")
|
||||
assert.Equal(t, ErrNotIntegerType, err)
|
||||
}
|
||||
|
||||
func TestDecr(t *testing.T) {
|
||||
// int
|
||||
var originVal interface{} = int(2)
|
||||
var updateVal interface{} = int(1)
|
||||
val, err := decr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = decr(int(-1 << (strconv.IntSize - 1)))
|
||||
assert.Equal(t, ErrDecrementOverflow, err)
|
||||
// int32
|
||||
originVal = int32(2)
|
||||
updateVal = int32(1)
|
||||
val, err = decr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = decr(int32(math.MinInt32))
|
||||
assert.Equal(t, ErrDecrementOverflow, err)
|
||||
|
||||
// int64
|
||||
originVal = int64(2)
|
||||
updateVal = int64(1)
|
||||
val, err = decr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = decr(int64(math.MinInt64))
|
||||
assert.Equal(t, ErrDecrementOverflow, err)
|
||||
|
||||
// uint
|
||||
originVal = uint(2)
|
||||
updateVal = uint(1)
|
||||
val, err = decr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = decr(uint(0))
|
||||
assert.Equal(t, ErrDecrementOverflow, err)
|
||||
|
||||
// uint32
|
||||
originVal = uint32(2)
|
||||
updateVal = uint32(1)
|
||||
val, err = decr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = decr(uint32(0))
|
||||
assert.Equal(t, ErrDecrementOverflow, err)
|
||||
|
||||
// uint64
|
||||
originVal = uint64(2)
|
||||
updateVal = uint64(1)
|
||||
val, err = decr(originVal)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, val, updateVal)
|
||||
|
||||
_, err = decr(uint64(0))
|
||||
assert.Equal(t, ErrDecrementOverflow, err)
|
||||
|
||||
// other type
|
||||
_, err = decr("string")
|
||||
assert.Equal(t, ErrNotIntegerType, err)
|
||||
}
|
||||
120
client/cache/conv_test.go
vendored
120
client/cache/conv_test.go
vendored
@ -16,128 +16,74 @@ package cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetString(t *testing.T) {
|
||||
var t1 = "test1"
|
||||
if "test1" != GetString(t1) {
|
||||
t.Error("get string from string error")
|
||||
}
|
||||
var t2 = []byte("test2")
|
||||
if "test2" != GetString(t2) {
|
||||
t.Error("get string from byte array error")
|
||||
}
|
||||
var t3 = 1
|
||||
if "1" != GetString(t3) {
|
||||
t.Error("get string from int error")
|
||||
}
|
||||
var t4 int64 = 1
|
||||
if "1" != GetString(t4) {
|
||||
t.Error("get string from int64 error")
|
||||
}
|
||||
var t5 = 1.1
|
||||
if "1.1" != GetString(t5) {
|
||||
t.Error("get string from float64 error")
|
||||
}
|
||||
|
||||
if "" != GetString(nil) {
|
||||
t.Error("get string from nil error")
|
||||
}
|
||||
assert.Equal(t, "test1", GetString(t1))
|
||||
var t2 = []byte("test2")
|
||||
assert.Equal(t, "test2", GetString(t2))
|
||||
var t3 = 1
|
||||
assert.Equal(t, "1", GetString(t3))
|
||||
var t4 int64 = 1
|
||||
assert.Equal(t, "1", GetString(t4))
|
||||
var t5 = 1.1
|
||||
assert.Equal(t, "1.1", GetString(t5))
|
||||
assert.Equal(t, "", GetString(nil))
|
||||
}
|
||||
|
||||
func TestGetInt(t *testing.T) {
|
||||
var t1 = 1
|
||||
if 1 != GetInt(t1) {
|
||||
t.Error("get int from int error")
|
||||
}
|
||||
assert.Equal(t, 1, GetInt(t1))
|
||||
var t2 int32 = 32
|
||||
if 32 != GetInt(t2) {
|
||||
t.Error("get int from int32 error")
|
||||
}
|
||||
assert.Equal(t, 32, GetInt(t2))
|
||||
|
||||
var t3 int64 = 64
|
||||
if 64 != GetInt(t3) {
|
||||
t.Error("get int from int64 error")
|
||||
}
|
||||
assert.Equal(t, 64, GetInt(t3))
|
||||
var t4 = "128"
|
||||
if 128 != GetInt(t4) {
|
||||
t.Error("get int from num string error")
|
||||
}
|
||||
if 0 != GetInt(nil) {
|
||||
t.Error("get int from nil error")
|
||||
}
|
||||
|
||||
assert.Equal(t, 128, GetInt(t4))
|
||||
assert.Equal(t, 0, GetInt(nil))
|
||||
}
|
||||
|
||||
func TestGetInt64(t *testing.T) {
|
||||
var i int64 = 1
|
||||
var t1 = 1
|
||||
if i != GetInt64(t1) {
|
||||
t.Error("get int64 from int error")
|
||||
}
|
||||
assert.Equal(t, i, GetInt64(t1))
|
||||
var t2 int32 = 1
|
||||
if i != GetInt64(t2) {
|
||||
t.Error("get int64 from int32 error")
|
||||
}
|
||||
|
||||
assert.Equal(t, i, GetInt64(t2))
|
||||
var t3 int64 = 1
|
||||
if i != GetInt64(t3) {
|
||||
t.Error("get int64 from int64 error")
|
||||
}
|
||||
assert.Equal(t, i, GetInt64(t3))
|
||||
var t4 = "1"
|
||||
if i != GetInt64(t4) {
|
||||
t.Error("get int64 from num string error")
|
||||
}
|
||||
if 0 != GetInt64(nil) {
|
||||
t.Error("get int64 from nil")
|
||||
}
|
||||
assert.Equal(t, i, GetInt64(t4))
|
||||
assert.Equal(t, int64(0), GetInt64(nil))
|
||||
}
|
||||
|
||||
func TestGetFloat64(t *testing.T) {
|
||||
var f = 1.11
|
||||
var t1 float32 = 1.11
|
||||
if f != GetFloat64(t1) {
|
||||
t.Error("get float64 from float32 error")
|
||||
}
|
||||
assert.Equal(t, f, GetFloat64(t1))
|
||||
var t2 = 1.11
|
||||
if f != GetFloat64(t2) {
|
||||
t.Error("get float64 from float64 error")
|
||||
}
|
||||
assert.Equal(t, f, GetFloat64(t2))
|
||||
var t3 = "1.11"
|
||||
if f != GetFloat64(t3) {
|
||||
t.Error("get float64 from string error")
|
||||
}
|
||||
assert.Equal(t, f, GetFloat64(t3))
|
||||
|
||||
var f2 float64 = 1
|
||||
var t4 = 1
|
||||
if f2 != GetFloat64(t4) {
|
||||
t.Error("get float64 from int error")
|
||||
}
|
||||
assert.Equal(t, f2, GetFloat64(t4))
|
||||
|
||||
if 0 != GetFloat64(nil) {
|
||||
t.Error("get float64 from nil error")
|
||||
}
|
||||
assert.Equal(t, float64(0), GetFloat64(nil))
|
||||
}
|
||||
|
||||
func TestGetBool(t *testing.T) {
|
||||
var t1 = true
|
||||
if !GetBool(t1) {
|
||||
t.Error("get bool from bool error")
|
||||
}
|
||||
assert.True(t, GetBool(t1))
|
||||
var t2 = "true"
|
||||
if !GetBool(t2) {
|
||||
t.Error("get bool from string error")
|
||||
}
|
||||
if GetBool(nil) {
|
||||
t.Error("get bool from nil error")
|
||||
}
|
||||
}
|
||||
assert.True(t, GetBool(t2))
|
||||
|
||||
func byteArrayEquals(a []byte, b []byte) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i, v := range a {
|
||||
if v != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
assert.False(t, GetBool(nil))
|
||||
}
|
||||
|
||||
182
client/cache/error_code.go
vendored
Normal file
182
client/cache/error_code.go
vendored
Normal file
@ -0,0 +1,182 @@
|
||||
// Copyright 2021 beego
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
var NilCacheAdapter = berror.DefineCode(4002001, moduleName, "NilCacheAdapter", `
|
||||
It means that you register cache adapter by pass nil.
|
||||
A cache adapter is an instance of Cache interface.
|
||||
`)
|
||||
|
||||
var DuplicateAdapter = berror.DefineCode(4002002, moduleName, "DuplicateAdapter", `
|
||||
You register two adapter with same name. In beego cache module, one name one adapter.
|
||||
Once you got this error, please check the error stack, search adapter
|
||||
`)
|
||||
|
||||
var UnknownAdapter = berror.DefineCode(4002003, moduleName, "UnknownAdapter", `
|
||||
Unknown adapter, do you forget to register the adapter?
|
||||
You must register adapter before use it. For example, if you want to use redis implementation,
|
||||
you must import the cache/redis package.
|
||||
`)
|
||||
|
||||
var IncrementOverflow = berror.DefineCode(4002004, moduleName, "IncrementOverflow", `
|
||||
The increment operation will overflow.
|
||||
`)
|
||||
|
||||
var DecrementOverflow = berror.DefineCode(4002005, moduleName, "DecrementOverflow", `
|
||||
The decrement operation will overflow.
|
||||
`)
|
||||
|
||||
var NotIntegerType = berror.DefineCode(4002006, moduleName, "NotIntegerType", `
|
||||
The type of value is not (u)int (u)int32 (u)int64.
|
||||
When you want to call Incr or Decr function of Cache API, you must confirm that the value's type is one of (u)int (u)int32 (u)int64.
|
||||
`)
|
||||
|
||||
var InvalidFileCacheDirectoryLevelCfg = berror.DefineCode(4002007, moduleName, "InvalidFileCacheDirectoryLevelCfg", `
|
||||
You pass invalid DirectoryLevel parameter when you try to StartAndGC file cache instance.
|
||||
This parameter must be a integer, and please check your input.
|
||||
`)
|
||||
|
||||
var InvalidFileCacheEmbedExpiryCfg = berror.DefineCode(4002008, moduleName, "InvalidFileCacheEmbedExpiryCfg", `
|
||||
You pass invalid EmbedExpiry parameter when you try to StartAndGC file cache instance.
|
||||
This parameter must be a integer, and please check your input.
|
||||
`)
|
||||
|
||||
var CreateFileCacheDirFailed = berror.DefineCode(4002009, moduleName, "CreateFileCacheDirFailed", `
|
||||
Beego failed to create file cache directory. There are two cases:
|
||||
1. You pass invalid CachePath parameter. Please check your input.
|
||||
2. Beego doesn't have the permission to create this directory. Please check your file mode.
|
||||
`)
|
||||
|
||||
var InvalidFileCachePath = berror.DefineCode(4002010, moduleName, "InvalidFilePath", `
|
||||
The file path of FileCache is invalid. Please correct the config.
|
||||
`)
|
||||
|
||||
var ReadFileCacheContentFailed = berror.DefineCode(4002011, moduleName, "ReadFileCacheContentFailed", `
|
||||
Usually you won't got this error. It means that Beego cannot read the data from the file.
|
||||
You need to check whether the file exist. Sometimes it may be deleted by other processes.
|
||||
If the file exists, please check the permission that Beego is able to read data from the file.
|
||||
`)
|
||||
|
||||
var InvalidGobEncodedData = berror.DefineCode(4002012, moduleName, "InvalidEncodedData", `
|
||||
The data is invalid. When you try to decode the invalid data, you got this error.
|
||||
Please confirm that the data is encoded by GOB correctly.
|
||||
`)
|
||||
|
||||
var GobEncodeDataFailed = berror.DefineCode(4002013, moduleName, "GobEncodeDataFailed", `
|
||||
Beego could not encode the data to GOB byte array. In general, the data type is invalid.
|
||||
For example, GOB doesn't support function type.
|
||||
Basic types, string, structure, structure pointer are supported.
|
||||
`)
|
||||
|
||||
var KeyExpired = berror.DefineCode(4002014, moduleName, "KeyExpired", `
|
||||
Cache key is expired.
|
||||
You should notice that, a key is expired and then it may be deleted by GC goroutine.
|
||||
So when you query a key which may be expired, you may got this code, or KeyNotExist.
|
||||
`)
|
||||
|
||||
var KeyNotExist = berror.DefineCode(4002015, moduleName, "KeyNotExist", `
|
||||
Key not found.
|
||||
`)
|
||||
|
||||
var MultiGetFailed = berror.DefineCode(4002016, moduleName, "MultiGetFailed", `
|
||||
Get multiple keys failed. Please check the detail msg to find out the root cause.
|
||||
`)
|
||||
|
||||
var InvalidMemoryCacheCfg = berror.DefineCode(4002017, moduleName, "InvalidMemoryCacheCfg", `
|
||||
The config is invalid. Please check your input. It must be a json string.
|
||||
`)
|
||||
|
||||
var InvalidMemCacheCfg = berror.DefineCode(4002018, moduleName, "InvalidMemCacheCfg", `
|
||||
The config is invalid. Please check your input, it must be json string and contains "conn" field.
|
||||
`)
|
||||
|
||||
var InvalidMemCacheValue = berror.DefineCode(4002019, moduleName, "InvalidMemCacheValue", `
|
||||
The value must be string or byte[], please check your input.
|
||||
`)
|
||||
|
||||
var InvalidRedisCacheCfg = berror.DefineCode(4002020, moduleName, "InvalidRedisCacheCfg", `
|
||||
The config must be json string, and has "conn" field.
|
||||
`)
|
||||
|
||||
var InvalidSsdbCacheCfg = berror.DefineCode(4002021, moduleName, "InvalidSsdbCacheCfg", `
|
||||
The config must be json string, and has "conn" field. The value of "conn" field should be "host:port".
|
||||
"port" must be a valid integer.
|
||||
`)
|
||||
|
||||
var InvalidSsdbCacheValue = berror.DefineCode(4002022, moduleName, "InvalidSsdbCacheValue", `
|
||||
SSDB cache only accept string value. Please check your input.
|
||||
`)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var DeleteFileCacheItemFailed = berror.DefineCode(5002001, moduleName, "DeleteFileCacheItemFailed", `
|
||||
Beego try to delete file cache item failed.
|
||||
Please check whether Beego generated file correctly.
|
||||
And then confirm whether this file is already deleted by other processes or other people.
|
||||
`)
|
||||
|
||||
var MemCacheCurdFailed = berror.DefineCode(5002002, moduleName, "MemCacheError", `
|
||||
When you want to get, put, delete key-value from remote memcache servers, you may get error:
|
||||
1. You pass invalid servers address, so Beego could not connect to remote server;
|
||||
2. The servers address is correct, but there is some net issue. Typically there is some firewalls between application and memcache server;
|
||||
3. Key is invalid. The key's length should be less than 250 and must not contains special characters;
|
||||
4. The response from memcache server is invalid;
|
||||
`)
|
||||
|
||||
var RedisCacheCurdFailed = berror.DefineCode(5002003, moduleName, "RedisCacheCurdFailed", `
|
||||
When Beego uses client to send request to redis server, it failed.
|
||||
1. The server addresses is invalid;
|
||||
2. Network issue, firewall issue or network is unstable;
|
||||
3. Client failed to manage connection. In extreme cases, Beego's redis client didn't maintain connections correctly, for example, Beego try to send request via closed connection;
|
||||
4. The request are huge and redis server spent too much time to process it, and client is timeout;
|
||||
|
||||
In general, if you always got this error whatever you do, in most cases, it was caused by network issue.
|
||||
You could check your network state, and confirm that firewall rules are correct.
|
||||
`)
|
||||
|
||||
var InvalidConnection = berror.DefineCode(5002004, moduleName, "InvalidConnection", `
|
||||
The connection is invalid. Please check your connection info, network, firewall.
|
||||
You could simply uses ping, telnet or write some simple tests to test network.
|
||||
`)
|
||||
|
||||
var DialFailed = berror.DefineCode(5002005, moduleName, "DialFailed", `
|
||||
When Beego try to dial to remote servers, it failed. Please check your connection info and network state, server state.
|
||||
`)
|
||||
|
||||
var SsdbCacheCurdFailed = berror.DefineCode(5002006, moduleName, "SsdbCacheCurdFailed", `
|
||||
When you try to use SSDB cache, it failed. There are many cases:
|
||||
1. servers unavailable;
|
||||
2. network issue, including network unstable, firewall;
|
||||
3. connection issue;
|
||||
4. request are huge and servers spent too much time to process it, got timeout;
|
||||
`)
|
||||
|
||||
var SsdbBadResponse = berror.DefineCode(5002007, moduleName, "SsdbBadResponse", `
|
||||
The reponse from SSDB server is invalid.
|
||||
Usually it indicates something wrong on server side.
|
||||
`)
|
||||
|
||||
var ErrKeyExpired = berror.Error(KeyExpired, "the key is expired")
|
||||
var ErrKeyNotExist = berror.Error(KeyNotExist, "the key isn't exist")
|
||||
197
client/cache/file.go
vendored
197
client/cache/file.go
vendored
@ -30,7 +30,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
// FileCacheItem is basic unit of file cache adapter which
|
||||
@ -73,38 +73,60 @@ func (fc *FileCache) StartAndGC(config string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := cfg["CachePath"]; !ok {
|
||||
cfg["CachePath"] = FileCachePath
|
||||
}
|
||||
if _, ok := cfg["FileSuffix"]; !ok {
|
||||
cfg["FileSuffix"] = FileCacheFileSuffix
|
||||
}
|
||||
if _, ok := cfg["DirectoryLevel"]; !ok {
|
||||
cfg["DirectoryLevel"] = strconv.Itoa(FileCacheDirectoryLevel)
|
||||
}
|
||||
if _, ok := cfg["EmbedExpiry"]; !ok {
|
||||
cfg["EmbedExpiry"] = strconv.FormatInt(int64(FileCacheEmbedExpiry.Seconds()), 10)
|
||||
}
|
||||
fc.CachePath = cfg["CachePath"]
|
||||
fc.FileSuffix = cfg["FileSuffix"]
|
||||
fc.DirectoryLevel, _ = strconv.Atoi(cfg["DirectoryLevel"])
|
||||
fc.EmbedExpiry, _ = strconv.Atoi(cfg["EmbedExpiry"])
|
||||
|
||||
fc.Init()
|
||||
return nil
|
||||
const cpKey = "CachePath"
|
||||
const fsKey = "FileSuffix"
|
||||
const dlKey = "DirectoryLevel"
|
||||
const eeKey = "EmbedExpiry"
|
||||
|
||||
if _, ok := cfg[cpKey]; !ok {
|
||||
cfg[cpKey] = FileCachePath
|
||||
}
|
||||
|
||||
if _, ok := cfg[fsKey]; !ok {
|
||||
cfg[fsKey] = FileCacheFileSuffix
|
||||
}
|
||||
|
||||
if _, ok := cfg[dlKey]; !ok {
|
||||
cfg[dlKey] = strconv.Itoa(FileCacheDirectoryLevel)
|
||||
}
|
||||
|
||||
if _, ok := cfg[eeKey]; !ok {
|
||||
cfg[eeKey] = strconv.FormatInt(int64(FileCacheEmbedExpiry.Seconds()), 10)
|
||||
}
|
||||
fc.CachePath = cfg[cpKey]
|
||||
fc.FileSuffix = cfg[fsKey]
|
||||
fc.DirectoryLevel, err = strconv.Atoi(cfg[dlKey])
|
||||
if err != nil {
|
||||
return berror.Wrapf(err, InvalidFileCacheDirectoryLevelCfg,
|
||||
"invalid directory level config, please check your input, it must be integer: %s", cfg[dlKey])
|
||||
}
|
||||
fc.EmbedExpiry, err = strconv.Atoi(cfg[eeKey])
|
||||
if err != nil {
|
||||
return berror.Wrapf(err, InvalidFileCacheEmbedExpiryCfg,
|
||||
"invalid embed expiry config, please check your input, it must be integer: %s", cfg[eeKey])
|
||||
}
|
||||
return fc.Init()
|
||||
}
|
||||
|
||||
// Init makes new a dir for file cache if it does not already exist
|
||||
func (fc *FileCache) Init() {
|
||||
if ok, _ := exists(fc.CachePath); !ok { // todo : error handle
|
||||
_ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle
|
||||
func (fc *FileCache) Init() error {
|
||||
ok, err := exists(fc.CachePath)
|
||||
if err != nil || ok {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(fc.CachePath, os.ModePerm)
|
||||
if err != nil {
|
||||
return berror.Wrapf(err, CreateFileCacheDirFailed,
|
||||
"could not create directory, please check the config [%s] and file mode.", fc.CachePath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getCachedFilename returns an md5 encoded file name.
|
||||
func (fc *FileCache) getCacheFileName(key string) string {
|
||||
func (fc *FileCache) getCacheFileName(key string) (string, error) {
|
||||
m := md5.New()
|
||||
io.WriteString(m, key)
|
||||
_, _ = io.WriteString(m, key)
|
||||
keyMd5 := hex.EncodeToString(m.Sum(nil))
|
||||
cachePath := fc.CachePath
|
||||
switch fc.DirectoryLevel {
|
||||
@ -113,18 +135,29 @@ func (fc *FileCache) getCacheFileName(key string) string {
|
||||
case 1:
|
||||
cachePath = filepath.Join(cachePath, keyMd5[0:2])
|
||||
}
|
||||
|
||||
if ok, _ := exists(cachePath); !ok { // todo : error handle
|
||||
_ = os.MkdirAll(cachePath, os.ModePerm) // todo : error handle
|
||||
ok, err := exists(cachePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !ok {
|
||||
err = os.MkdirAll(cachePath, os.ModePerm)
|
||||
if err != nil {
|
||||
return "", berror.Wrapf(err, CreateFileCacheDirFailed,
|
||||
"could not create the directory: %s", cachePath)
|
||||
}
|
||||
}
|
||||
|
||||
return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, fc.FileSuffix))
|
||||
return filepath.Join(cachePath, fmt.Sprintf("%s%s", keyMd5, fc.FileSuffix)), nil
|
||||
}
|
||||
|
||||
// Get value from file cache.
|
||||
// if nonexistent or expired return an empty string.
|
||||
func (fc *FileCache) Get(ctx context.Context, key string) (interface{}, error) {
|
||||
fileData, err := FileGetContents(fc.getCacheFileName(key))
|
||||
fn, err := fc.getCacheFileName(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fileData, err := FileGetContents(fn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -136,7 +169,7 @@ func (fc *FileCache) Get(ctx context.Context, key string) (interface{}, error) {
|
||||
}
|
||||
|
||||
if to.Expired.Before(time.Now()) {
|
||||
return nil, errors.New("The key is expired")
|
||||
return nil, ErrKeyExpired
|
||||
}
|
||||
return to.Data, nil
|
||||
}
|
||||
@ -159,7 +192,7 @@ func (fc *FileCache) GetMulti(ctx context.Context, keys []string) ([]interface{}
|
||||
if len(keysErr) == 0 {
|
||||
return rc, nil
|
||||
}
|
||||
return rc, errors.New(strings.Join(keysErr, "; "))
|
||||
return rc, berror.Error(MultiGetFailed, strings.Join(keysErr, "; "))
|
||||
}
|
||||
|
||||
// Put value into file cache.
|
||||
@ -179,14 +212,26 @@ func (fc *FileCache) Put(ctx context.Context, key string, val interface{}, timeo
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return FilePutContents(fc.getCacheFileName(key), data)
|
||||
|
||||
fn, err := fc.getCacheFileName(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return FilePutContents(fn, data)
|
||||
}
|
||||
|
||||
// Delete file cache value.
|
||||
func (fc *FileCache) Delete(ctx context.Context, key string) error {
|
||||
filename := fc.getCacheFileName(key)
|
||||
filename, err := fc.getCacheFileName(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok, _ := exists(filename); ok {
|
||||
return os.Remove(filename)
|
||||
err = os.Remove(filename)
|
||||
if err != nil {
|
||||
return berror.Wrapf(err, DeleteFileCacheItemFailed,
|
||||
"can not delete this file cache key-value, key is %s and file name is %s", key, filename)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -199,25 +244,12 @@ func (fc *FileCache) Incr(ctx context.Context, key string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var res interface{}
|
||||
switch val := data.(type) {
|
||||
case int:
|
||||
res = val + 1
|
||||
case int32:
|
||||
res = val + 1
|
||||
case int64:
|
||||
res = val + 1
|
||||
case uint:
|
||||
res = val + 1
|
||||
case uint32:
|
||||
res = val + 1
|
||||
case uint64:
|
||||
res = val + 1
|
||||
default:
|
||||
return errors.Errorf("data is not (u)int (u)int32 (u)int64")
|
||||
val, err := incr(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fc.Put(context.Background(), key, res, time.Duration(fc.EmbedExpiry))
|
||||
return fc.Put(context.Background(), key, val, time.Duration(fc.EmbedExpiry))
|
||||
}
|
||||
|
||||
// Decr decreases cached int value.
|
||||
@ -227,43 +259,21 @@ func (fc *FileCache) Decr(ctx context.Context, key string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var res interface{}
|
||||
switch val := data.(type) {
|
||||
case int:
|
||||
res = val - 1
|
||||
case int32:
|
||||
res = val - 1
|
||||
case int64:
|
||||
res = val - 1
|
||||
case uint:
|
||||
if val > 0 {
|
||||
res = val - 1
|
||||
} else {
|
||||
return errors.New("data val is less than 0")
|
||||
}
|
||||
case uint32:
|
||||
if val > 0 {
|
||||
res = val - 1
|
||||
} else {
|
||||
return errors.New("data val is less than 0")
|
||||
}
|
||||
case uint64:
|
||||
if val > 0 {
|
||||
res = val - 1
|
||||
} else {
|
||||
return errors.New("data val is less than 0")
|
||||
}
|
||||
default:
|
||||
return errors.Errorf("data is not (u)int (u)int32 (u)int64")
|
||||
val, err := decr(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fc.Put(context.Background(), key, res, time.Duration(fc.EmbedExpiry))
|
||||
return fc.Put(context.Background(), key, val, time.Duration(fc.EmbedExpiry))
|
||||
}
|
||||
|
||||
// IsExist checks if value exists.
|
||||
func (fc *FileCache) IsExist(ctx context.Context, key string) (bool, error) {
|
||||
ret, _ := exists(fc.getCacheFileName(key))
|
||||
return ret, nil
|
||||
fn, err := fc.getCacheFileName(key)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return exists(fn)
|
||||
}
|
||||
|
||||
// ClearAll cleans cached files (not implemented)
|
||||
@ -280,13 +290,19 @@ func exists(path string) (bool, error) {
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
return false, berror.Wrapf(err, InvalidFileCachePath, "file cache path is invalid: %s", path)
|
||||
}
|
||||
|
||||
// FileGetContents Reads bytes from a file.
|
||||
// if non-existent, create this file.
|
||||
func FileGetContents(filename string) (data []byte, e error) {
|
||||
return ioutil.ReadFile(filename)
|
||||
func FileGetContents(filename string) ([]byte, error) {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, berror.Wrapf(err, ReadFileCacheContentFailed,
|
||||
"could not read the data from the file: %s, " +
|
||||
"please confirm that file exist and Beego has the permission to read the content.", filename)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// FilePutContents puts bytes into a file.
|
||||
@ -301,16 +317,21 @@ func GobEncode(data interface{}) ([]byte, error) {
|
||||
enc := gob.NewEncoder(buf)
|
||||
err := enc.Encode(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, berror.Wrap(err, GobEncodeDataFailed, "could not encode this data")
|
||||
}
|
||||
return buf.Bytes(), err
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// GobDecode Gob decodes a file cache item.
|
||||
func GobDecode(data []byte, to *FileCacheItem) error {
|
||||
buf := bytes.NewBuffer(data)
|
||||
dec := gob.NewDecoder(buf)
|
||||
return dec.Decode(&to)
|
||||
err := dec.Decode(&to)
|
||||
if err != nil {
|
||||
return berror.Wrap(err, InvalidGobEncodedData,
|
||||
"could not decode this data to FileCacheItem. Make sure that the data is encoded by GOB.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
108
client/cache/file_test.go
vendored
Normal file
108
client/cache/file_test.go
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright 2021 beego
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFileCacheStartAndGC(t *testing.T) {
|
||||
fc := NewFileCache().(*FileCache)
|
||||
err := fc.StartAndGC(`{`)
|
||||
assert.NotNil(t, err)
|
||||
err = fc.StartAndGC(`{}`)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, fc.CachePath, FileCachePath)
|
||||
assert.Equal(t, fc.DirectoryLevel, FileCacheDirectoryLevel)
|
||||
assert.Equal(t, fc.EmbedExpiry, int(FileCacheEmbedExpiry))
|
||||
assert.Equal(t, fc.FileSuffix, FileCacheFileSuffix)
|
||||
|
||||
err = fc.StartAndGC(`{"CachePath":"/cache","FileSuffix":".bin","DirectoryLevel":"2","EmbedExpiry":"0"}`)
|
||||
// could not create dir
|
||||
assert.NotNil(t, err)
|
||||
|
||||
str := getTestCacheFilePath()
|
||||
err = fc.StartAndGC(fmt.Sprintf(`{"CachePath":"%s","FileSuffix":".bin","DirectoryLevel":"2","EmbedExpiry":"0"}`, str))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, fc.CachePath, str)
|
||||
assert.Equal(t, fc.DirectoryLevel, 2)
|
||||
assert.Equal(t, fc.EmbedExpiry, 0)
|
||||
assert.Equal(t, fc.FileSuffix, ".bin")
|
||||
|
||||
err = fc.StartAndGC(fmt.Sprintf(`{"CachePath":"%s","FileSuffix":".bin","DirectoryLevel":"aaa","EmbedExpiry":"0"}`, str))
|
||||
assert.NotNil(t, err)
|
||||
|
||||
err = fc.StartAndGC(fmt.Sprintf(`{"CachePath":"%s","FileSuffix":".bin","DirectoryLevel":"2","EmbedExpiry":"aaa"}`, str))
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestFileCacheInit(t *testing.T) {
|
||||
fc := NewFileCache().(*FileCache)
|
||||
fc.CachePath = "////aaa"
|
||||
err := fc.Init()
|
||||
assert.NotNil(t, err)
|
||||
fc.CachePath = getTestCacheFilePath()
|
||||
err = fc.Init()
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestFileGetContents(t *testing.T) {
|
||||
_, err := FileGetContents("/bin/aaa")
|
||||
assert.NotNil(t, err)
|
||||
fn := filepath.Join(os.TempDir(), "fileCache.txt")
|
||||
f, err := os.Create(fn)
|
||||
assert.Nil(t, err)
|
||||
_, err = f.WriteString("text")
|
||||
assert.Nil(t, err)
|
||||
data, err := FileGetContents(fn)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "text", string(data))
|
||||
}
|
||||
|
||||
func TestGobEncodeDecode(t *testing.T) {
|
||||
_, err := GobEncode(func() {
|
||||
fmt.Print("test func")
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
data, err := GobEncode(&FileCacheItem{
|
||||
Data: "hello",
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
err = GobDecode([]byte("wrong data"), &FileCacheItem{})
|
||||
assert.NotNil(t, err)
|
||||
dci := &FileCacheItem{}
|
||||
err = GobDecode(data, dci)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "hello", dci.Data)
|
||||
}
|
||||
|
||||
func TestFileCacheDelete(t *testing.T) {
|
||||
fc := NewFileCache()
|
||||
err := fc.StartAndGC(`{}`)
|
||||
assert.Nil(t, err)
|
||||
err = fc.Delete(context.Background(), "my-key")
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func getTestCacheFilePath() string {
|
||||
return filepath.Join(os.TempDir(), "test", "file.txt")
|
||||
}
|
||||
97
client/cache/memcache/memcache.go
vendored
97
client/cache/memcache/memcache.go
vendored
@ -20,8 +20,8 @@
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/beego/beego/v2/cache/memcache"
|
||||
// "github.com/beego/beego/v2/cache"
|
||||
// _ "github.com/beego/beego/v2/client/cache/memcache"
|
||||
// "github.com/beego/beego/v2/client/cache"
|
||||
// )
|
||||
//
|
||||
// bm, err := cache.NewCache("memcache", `{"conn":"127.0.0.1:11211"}`)
|
||||
@ -32,7 +32,6 @@ package memcache
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@ -40,6 +39,7 @@ import (
|
||||
"github.com/bradfitz/gomemcache/memcache"
|
||||
|
||||
"github.com/beego/beego/v2/client/cache"
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
// Cache Memcache adapter.
|
||||
@ -55,36 +55,31 @@ func NewMemCache() cache.Cache {
|
||||
|
||||
// Get get value from memcache.
|
||||
func (rc *Cache) Get(ctx context.Context, key string) (interface{}, error) {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if item, err := rc.conn.Get(key); err == nil {
|
||||
return item.Value, nil
|
||||
} else {
|
||||
return nil, err
|
||||
return nil, berror.Wrapf(err, cache.MemCacheCurdFailed,
|
||||
"could not read data from memcache, please check your key, network and connection. Root cause: %s",
|
||||
err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// GetMulti gets a value from a key in memcache.
|
||||
func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
|
||||
rv := make([]interface{}, len(keys))
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return rv, err
|
||||
}
|
||||
}
|
||||
|
||||
mv, err := rc.conn.GetMulti(keys)
|
||||
if err != nil {
|
||||
return rv, err
|
||||
return rv, berror.Wrapf(err, cache.MemCacheCurdFailed,
|
||||
"could not read multiple key-values from memcache, " +
|
||||
"please check your keys, network and connection. Root cause: %s",
|
||||
err.Error())
|
||||
}
|
||||
|
||||
keysErr := make([]string, 0)
|
||||
for i, ki := range keys {
|
||||
if _, ok := mv[ki]; !ok {
|
||||
keysErr = append(keysErr, fmt.Sprintf("key [%s] error: %s", ki, "the key isn't exist"))
|
||||
keysErr = append(keysErr, fmt.Sprintf("key [%s] error: %s", ki, "key not exist"))
|
||||
continue
|
||||
}
|
||||
rv[i] = mv[ki].Value
|
||||
@ -93,78 +88,54 @@ func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, er
|
||||
if len(keysErr) == 0 {
|
||||
return rv, nil
|
||||
}
|
||||
return rv, fmt.Errorf(strings.Join(keysErr, "; "))
|
||||
return rv, berror.Error(cache.MultiGetFailed, strings.Join(keysErr, "; "))
|
||||
}
|
||||
|
||||
// Put puts a value into memcache.
|
||||
func (rc *Cache) Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
item := memcache.Item{Key: key, Expiration: int32(timeout / time.Second)}
|
||||
if v, ok := val.([]byte); ok {
|
||||
item.Value = v
|
||||
} else if str, ok := val.(string); ok {
|
||||
item.Value = []byte(str)
|
||||
} else {
|
||||
return errors.New("val only support string and []byte")
|
||||
return berror.Errorf(cache.InvalidMemCacheValue,
|
||||
"the value must be string or byte[]. key: %s, value:%v", key, val)
|
||||
}
|
||||
return rc.conn.Set(&item)
|
||||
return berror.Wrapf(rc.conn.Set(&item), cache.MemCacheCurdFailed,
|
||||
"could not put key-value to memcache, key: %s", key)
|
||||
}
|
||||
|
||||
// Delete deletes a value in memcache.
|
||||
func (rc *Cache) Delete(ctx context.Context, key string) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return rc.conn.Delete(key)
|
||||
return berror.Wrapf(rc.conn.Delete(key), cache.MemCacheCurdFailed,
|
||||
"could not delete key-value from memcache, key: %s", key)
|
||||
}
|
||||
|
||||
// Incr increases counter.
|
||||
func (rc *Cache) Incr(ctx context.Context, key string) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Increment(key, 1)
|
||||
return err
|
||||
return berror.Wrapf(err, cache.MemCacheCurdFailed,
|
||||
"could not increase value for key: %s", key)
|
||||
}
|
||||
|
||||
// Decr decreases counter.
|
||||
func (rc *Cache) Decr(ctx context.Context, key string) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Decrement(key, 1)
|
||||
return err
|
||||
return berror.Wrapf(err, cache.MemCacheCurdFailed,
|
||||
"could not decrease value for key: %s", key)
|
||||
}
|
||||
|
||||
// IsExist checks if a value exists in memcache.
|
||||
func (rc *Cache) IsExist(ctx context.Context, key string) (bool, error) {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Get(key)
|
||||
_, err := rc.Get(ctx, key)
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
// ClearAll clears all cache in memcache.
|
||||
func (rc *Cache) ClearAll(context.Context) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return rc.conn.FlushAll()
|
||||
return berror.Wrap(rc.conn.FlushAll(), cache.MemCacheCurdFailed,
|
||||
"try to clear all key-value pairs failed")
|
||||
}
|
||||
|
||||
// StartAndGC starts the memcache adapter.
|
||||
@ -172,21 +143,15 @@ func (rc *Cache) ClearAll(context.Context) error {
|
||||
// If an error occurs during connecting, an error is returned
|
||||
func (rc *Cache) StartAndGC(config string) error {
|
||||
var cf map[string]string
|
||||
json.Unmarshal([]byte(config), &cf)
|
||||
if err := json.Unmarshal([]byte(config), &cf); err != nil {
|
||||
return berror.Wrapf(err, cache.InvalidMemCacheCfg,
|
||||
"could not unmarshal this config, it must be valid json stringP: %s", config)
|
||||
}
|
||||
|
||||
if _, ok := cf["conn"]; !ok {
|
||||
return errors.New("config has no conn key")
|
||||
return berror.Errorf(cache.InvalidMemCacheCfg, `config must contains "conn" field: %s`, config)
|
||||
}
|
||||
rc.conninfo = strings.Split(cf["conn"], ";")
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// connect to memcache and keep the connection.
|
||||
func (rc *Cache) connectInit() error {
|
||||
rc.conn = memcache.New(rc.conninfo...)
|
||||
return nil
|
||||
}
|
||||
|
||||
112
client/cache/memcache/memcache_test.go
vendored
112
client/cache/memcache/memcache_test.go
vendored
@ -19,10 +19,12 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
_ "github.com/bradfitz/gomemcache/memcache"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/beego/beego/v2/client/cache"
|
||||
)
|
||||
@ -34,78 +36,63 @@ func TestMemcacheCache(t *testing.T) {
|
||||
}
|
||||
|
||||
bm, err := cache.NewCache("memcache", fmt.Sprintf(`{"conn": "%s"}`, addr))
|
||||
if err != nil {
|
||||
t.Error("init err")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
|
||||
timeoutDuration := 10 * time.Second
|
||||
if err = bm.Put(context.Background(), "astaxie", "1", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie"); !res {
|
||||
t.Error("check err")
|
||||
}
|
||||
|
||||
assert.Nil(t, bm.Put(context.Background(), "astaxie", "1", timeoutDuration))
|
||||
res, _ := bm.IsExist(context.Background(), "astaxie")
|
||||
assert.True(t, res)
|
||||
|
||||
time.Sleep(11 * time.Second)
|
||||
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie"); res {
|
||||
t.Error("check err")
|
||||
}
|
||||
if err = bm.Put(context.Background(), "astaxie", "1", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
res, _ = bm.IsExist(context.Background(), "astaxie")
|
||||
assert.False(t, res)
|
||||
|
||||
assert.Nil(t, bm.Put(context.Background(), "astaxie", "1", timeoutDuration))
|
||||
|
||||
val, _ := bm.Get(context.Background(), "astaxie")
|
||||
if v, err := strconv.Atoi(string(val.([]byte))); err != nil || v != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
v, err := strconv.Atoi(string(val.([]byte)))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, v)
|
||||
|
||||
if err = bm.Incr(context.Background(), "astaxie"); err != nil {
|
||||
t.Error("Incr Error", err)
|
||||
}
|
||||
assert.Nil(t, bm.Incr(context.Background(), "astaxie"))
|
||||
|
||||
val, _ = bm.Get(context.Background(), "astaxie")
|
||||
if v, err := strconv.Atoi(string(val.([]byte))); err != nil || v != 2 {
|
||||
t.Error("get err")
|
||||
}
|
||||
v, err = strconv.Atoi(string(val.([]byte)))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, v)
|
||||
|
||||
if err = bm.Decr(context.Background(), "astaxie"); err != nil {
|
||||
t.Error("Decr Error", err)
|
||||
}
|
||||
assert.Nil(t, bm.Decr(context.Background(), "astaxie"))
|
||||
|
||||
val, _ = bm.Get(context.Background(), "astaxie")
|
||||
if v, err := strconv.Atoi(string(val.([]byte))); err != nil || v != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
v, err = strconv.Atoi(string(val.([]byte)))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, v)
|
||||
bm.Delete(context.Background(), "astaxie")
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie"); res {
|
||||
t.Error("delete err")
|
||||
}
|
||||
|
||||
res, _ = bm.IsExist(context.Background(), "astaxie")
|
||||
assert.False(t, res)
|
||||
|
||||
assert.Nil(t,bm.Put(context.Background(), "astaxie", "author", timeoutDuration) )
|
||||
// test string
|
||||
if err = bm.Put(context.Background(), "astaxie", "author", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie"); !res {
|
||||
t.Error("check err")
|
||||
}
|
||||
res, _ = bm.IsExist(context.Background(), "astaxie")
|
||||
assert.True(t, res)
|
||||
|
||||
val, _ = bm.Get(context.Background(), "astaxie")
|
||||
if v := val.([]byte); string(v) != "author" {
|
||||
t.Error("get err")
|
||||
}
|
||||
vs := val.([]byte)
|
||||
assert.Equal(t, "author", string(vs))
|
||||
|
||||
// test GetMulti
|
||||
if err = bm.Put(context.Background(), "astaxie1", "author1", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie1"); !res {
|
||||
t.Error("check err")
|
||||
}
|
||||
assert.Nil(t, bm.Put(context.Background(), "astaxie1", "author1", timeoutDuration))
|
||||
|
||||
|
||||
res, _ = bm.IsExist(context.Background(), "astaxie1")
|
||||
assert.True(t, res)
|
||||
|
||||
vv, _ := bm.GetMulti(context.Background(), []string{"astaxie", "astaxie1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
assert.Equal(t, 2, len(vv))
|
||||
|
||||
if string(vv[0].([]byte)) != "author" && string(vv[0].([]byte)) != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
@ -114,21 +101,14 @@ func TestMemcacheCache(t *testing.T) {
|
||||
}
|
||||
|
||||
vv, err = bm.GetMulti(context.Background(), []string{"astaxie0", "astaxie1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if vv[0] != nil {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if string(vv[1].([]byte)) != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if err != nil && err.Error() == "key [astaxie0] error: key isn't exist" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
assert.Equal(t, 2, len(vv))
|
||||
assert.Nil(t, vv[0])
|
||||
|
||||
assert.Equal(t, "author1", string(vv[1].([]byte)))
|
||||
|
||||
assert.NotNil(t, err)
|
||||
assert.True(t, strings.Contains(err.Error(), "key not exist"))
|
||||
|
||||
assert.Nil(t, bm.ClearAll(context.Background()))
|
||||
// test clear all
|
||||
if err = bm.ClearAll(context.Background()); err != nil {
|
||||
t.Error("clear all err")
|
||||
}
|
||||
}
|
||||
|
||||
81
client/cache/memory.go
vendored
81
client/cache/memory.go
vendored
@ -17,11 +17,12 @@ package cache
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -41,7 +42,7 @@ func (mi *MemoryItem) isExpire() bool {
|
||||
if mi.lifespan == 0 {
|
||||
return false
|
||||
}
|
||||
return time.Now().Sub(mi.createdTime) > mi.lifespan
|
||||
return time.Since(mi.createdTime) > mi.lifespan
|
||||
}
|
||||
|
||||
// MemoryCache is a memory cache adapter.
|
||||
@ -64,13 +65,14 @@ func NewMemoryCache() Cache {
|
||||
func (bc *MemoryCache) Get(ctx context.Context, key string) (interface{}, error) {
|
||||
bc.RLock()
|
||||
defer bc.RUnlock()
|
||||
if itm, ok := bc.items[key]; ok {
|
||||
if itm, ok :=
|
||||
bc.items[key]; ok {
|
||||
if itm.isExpire() {
|
||||
return nil, errors.New("the key is expired")
|
||||
return nil, ErrKeyExpired
|
||||
}
|
||||
return itm.val, nil
|
||||
}
|
||||
return nil, errors.New("the key isn't exist")
|
||||
return nil, ErrKeyNotExist
|
||||
}
|
||||
|
||||
// GetMulti gets caches from memory.
|
||||
@ -91,7 +93,7 @@ func (bc *MemoryCache) GetMulti(ctx context.Context, keys []string) ([]interface
|
||||
if len(keysErr) == 0 {
|
||||
return rc, nil
|
||||
}
|
||||
return rc, errors.New(strings.Join(keysErr, "; "))
|
||||
return rc, berror.Error(MultiGetFailed, strings.Join(keysErr, "; "))
|
||||
}
|
||||
|
||||
// Put puts cache into memory.
|
||||
@ -108,16 +110,11 @@ func (bc *MemoryCache) Put(ctx context.Context, key string, val interface{}, tim
|
||||
}
|
||||
|
||||
// Delete cache in memory.
|
||||
// If the key is not found, it will not return error
|
||||
func (bc *MemoryCache) Delete(ctx context.Context, key string) error {
|
||||
bc.Lock()
|
||||
defer bc.Unlock()
|
||||
if _, ok := bc.items[key]; !ok {
|
||||
return errors.New("key not exist")
|
||||
}
|
||||
delete(bc.items, key)
|
||||
if _, ok := bc.items[key]; ok {
|
||||
return errors.New("delete key error")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -128,24 +125,14 @@ func (bc *MemoryCache) Incr(ctx context.Context, key string) error {
|
||||
defer bc.Unlock()
|
||||
itm, ok := bc.items[key]
|
||||
if !ok {
|
||||
return errors.New("key not exist")
|
||||
return ErrKeyNotExist
|
||||
}
|
||||
switch val := itm.val.(type) {
|
||||
case int:
|
||||
itm.val = val + 1
|
||||
case int32:
|
||||
itm.val = val + 1
|
||||
case int64:
|
||||
itm.val = val + 1
|
||||
case uint:
|
||||
itm.val = val + 1
|
||||
case uint32:
|
||||
itm.val = val + 1
|
||||
case uint64:
|
||||
itm.val = val + 1
|
||||
default:
|
||||
return errors.New("item val is not (u)int (u)int32 (u)int64")
|
||||
|
||||
val, err := incr(itm.val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
itm.val = val
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -155,36 +142,14 @@ func (bc *MemoryCache) Decr(ctx context.Context, key string) error {
|
||||
defer bc.Unlock()
|
||||
itm, ok := bc.items[key]
|
||||
if !ok {
|
||||
return errors.New("key not exist")
|
||||
return ErrKeyNotExist
|
||||
}
|
||||
switch val := itm.val.(type) {
|
||||
case int:
|
||||
itm.val = val - 1
|
||||
case int64:
|
||||
itm.val = val - 1
|
||||
case int32:
|
||||
itm.val = val - 1
|
||||
case uint:
|
||||
if val > 0 {
|
||||
itm.val = val - 1
|
||||
} else {
|
||||
return errors.New("item val is less than 0")
|
||||
}
|
||||
case uint32:
|
||||
if val > 0 {
|
||||
itm.val = val - 1
|
||||
} else {
|
||||
return errors.New("item val is less than 0")
|
||||
}
|
||||
case uint64:
|
||||
if val > 0 {
|
||||
itm.val = val - 1
|
||||
} else {
|
||||
return errors.New("item val is less than 0")
|
||||
}
|
||||
default:
|
||||
return errors.New("item val is not int int64 int32")
|
||||
|
||||
val, err := decr(itm.val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
itm.val = val
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -209,7 +174,9 @@ func (bc *MemoryCache) ClearAll(context.Context) error {
|
||||
// StartAndGC starts memory cache. Checks expiration in every clock time.
|
||||
func (bc *MemoryCache) StartAndGC(config string) error {
|
||||
var cf map[string]int
|
||||
json.Unmarshal([]byte(config), &cf)
|
||||
if err := json.Unmarshal([]byte(config), &cf); err != nil {
|
||||
return berror.Wrapf(err, InvalidMemoryCacheCfg, "invalid config, please check your input: %s", config)
|
||||
}
|
||||
if _, ok := cf["interval"]; !ok {
|
||||
cf = make(map[string]int)
|
||||
cf["interval"] = DefaultEvery
|
||||
|
||||
17
client/cache/module.go
vendored
Normal file
17
client/cache/module.go
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2021 beego
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cache
|
||||
|
||||
const moduleName = "cache"
|
||||
63
client/cache/redis/redis.go
vendored
63
client/cache/redis/redis.go
vendored
@ -20,8 +20,8 @@
|
||||
//
|
||||
// Usage:
|
||||
// import(
|
||||
// _ "github.com/beego/beego/v2/cache/redis"
|
||||
// "github.com/beego/beego/v2/cache"
|
||||
// _ "github.com/beego/beego/v2/client/cache/redis"
|
||||
// "github.com/beego/beego/v2/client/cache"
|
||||
// )
|
||||
//
|
||||
// bm, err := cache.NewCache("redis", `{"conn":"127.0.0.1:11211"}`)
|
||||
@ -32,7 +32,6 @@ package redis
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -41,6 +40,7 @@ import (
|
||||
"github.com/gomodule/redigo/redis"
|
||||
|
||||
"github.com/beego/beego/v2/client/cache"
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -67,15 +67,20 @@ func NewRedisCache() cache.Cache {
|
||||
}
|
||||
|
||||
// Execute the redis commands. args[0] must be the key name
|
||||
func (rc *Cache) do(commandName string, args ...interface{}) (reply interface{}, err error) {
|
||||
if len(args) < 1 {
|
||||
return nil, errors.New("missing required arguments")
|
||||
}
|
||||
func (rc *Cache) do(commandName string, args ...interface{}) (interface{}, error) {
|
||||
args[0] = rc.associate(args[0])
|
||||
c := rc.p.Get()
|
||||
defer c.Close()
|
||||
defer func() {
|
||||
_ = c.Close()
|
||||
}()
|
||||
|
||||
return c.Do(commandName, args...)
|
||||
reply, err := c.Do(commandName, args...)
|
||||
if err != nil {
|
||||
return nil, berror.Wrapf(err, cache.RedisCacheCurdFailed,
|
||||
"could not execute this command: %s", commandName)
|
||||
}
|
||||
|
||||
return reply, nil
|
||||
}
|
||||
|
||||
// associate with config key.
|
||||
@ -95,7 +100,9 @@ func (rc *Cache) Get(ctx context.Context, key string) (interface{}, error) {
|
||||
// GetMulti gets cache from redis.
|
||||
func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
|
||||
c := rc.p.Get()
|
||||
defer c.Close()
|
||||
defer func() {
|
||||
_ = c.Close()
|
||||
}()
|
||||
var args []interface{}
|
||||
for _, key := range keys {
|
||||
args = append(args, rc.associate(key))
|
||||
@ -137,13 +144,16 @@ func (rc *Cache) Decr(ctx context.Context, key string) error {
|
||||
}
|
||||
|
||||
// ClearAll deletes all cache in the redis collection
|
||||
// Be careful about this method, because it scans all keys and the delete them one by one
|
||||
func (rc *Cache) ClearAll(context.Context) error {
|
||||
cachedKeys, err := rc.Scan(rc.key + ":*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c := rc.p.Get()
|
||||
defer c.Close()
|
||||
defer func() {
|
||||
_ = c.Close()
|
||||
}()
|
||||
for _, str := range cachedKeys {
|
||||
if _, err = c.Do("DEL", str); err != nil {
|
||||
return err
|
||||
@ -155,7 +165,9 @@ func (rc *Cache) ClearAll(context.Context) error {
|
||||
// Scan scans all keys matching a given pattern.
|
||||
func (rc *Cache) Scan(pattern string) (keys []string, err error) {
|
||||
c := rc.p.Get()
|
||||
defer c.Close()
|
||||
defer func() {
|
||||
_ = c.Close()
|
||||
}()
|
||||
var (
|
||||
cursor uint64 = 0 // start
|
||||
result []interface{}
|
||||
@ -186,13 +198,16 @@ func (rc *Cache) Scan(pattern string) (keys []string, err error) {
|
||||
// Cached items in redis are stored forever, no garbage collection happens
|
||||
func (rc *Cache) StartAndGC(config string) error {
|
||||
var cf map[string]string
|
||||
json.Unmarshal([]byte(config), &cf)
|
||||
err := json.Unmarshal([]byte(config), &cf)
|
||||
if err != nil {
|
||||
return berror.Wrapf(err, cache.InvalidRedisCacheCfg, "could not unmarshal the config: %s", config)
|
||||
}
|
||||
|
||||
if _, ok := cf["key"]; !ok {
|
||||
cf["key"] = DefaultKey
|
||||
}
|
||||
if _, ok := cf["conn"]; !ok {
|
||||
return errors.New("config has no conn key")
|
||||
return berror.Wrapf(err, cache.InvalidRedisCacheCfg, "config missing conn field: %s", config)
|
||||
}
|
||||
|
||||
// Format redis://<password>@<host>:<port>
|
||||
@ -229,9 +244,16 @@ func (rc *Cache) StartAndGC(config string) error {
|
||||
rc.connectInit()
|
||||
|
||||
c := rc.p.Get()
|
||||
defer c.Close()
|
||||
defer func() {
|
||||
_ = c.Close()
|
||||
}()
|
||||
|
||||
return c.Err()
|
||||
// test connection
|
||||
if err = c.Err(); err != nil {
|
||||
return berror.Wrapf(err, cache.InvalidConnection,
|
||||
"can not connect to remote redis server, please check the connection info and network state: %s", config)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// connect to redis.
|
||||
@ -239,19 +261,20 @@ func (rc *Cache) connectInit() {
|
||||
dialFunc := func() (c redis.Conn, err error) {
|
||||
c, err = redis.Dial("tcp", rc.conninfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, berror.Wrapf(err, cache.DialFailed,
|
||||
"could not dial to remote server: %s ", rc.conninfo)
|
||||
}
|
||||
|
||||
if rc.password != "" {
|
||||
if _, err := c.Do("AUTH", rc.password); err != nil {
|
||||
c.Close()
|
||||
if _, err = c.Do("AUTH", rc.password); err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
_, selecterr := c.Do("SELECT", rc.dbNum)
|
||||
if selecterr != nil {
|
||||
c.Close()
|
||||
_ = c.Close()
|
||||
return nil, selecterr
|
||||
}
|
||||
return
|
||||
|
||||
139
client/cache/redis/redis_test.go
vendored
139
client/cache/redis/redis_test.go
vendored
@ -35,96 +35,74 @@ func TestRedisCache(t *testing.T) {
|
||||
}
|
||||
|
||||
bm, err := cache.NewCache("redis", fmt.Sprintf(`{"conn": "%s"}`, redisAddr))
|
||||
if err != nil {
|
||||
t.Error("init err")
|
||||
}
|
||||
timeoutDuration := 10 * time.Second
|
||||
if err = bm.Put(context.Background(), "astaxie", 1, timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie"); !res {
|
||||
t.Error("check err")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
timeoutDuration := 3 * time.Second
|
||||
|
||||
time.Sleep(11 * time.Second)
|
||||
assert.Nil(t, bm.Put(context.Background(), "astaxie", 1, timeoutDuration))
|
||||
|
||||
|
||||
res, _ := bm.IsExist(context.Background(), "astaxie")
|
||||
assert.True(t, res)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
res, _ = bm.IsExist(context.Background(), "astaxie")
|
||||
assert.False(t, res)
|
||||
|
||||
assert.Nil(t, bm.Put(context.Background(), "astaxie", 1, timeoutDuration))
|
||||
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie"); res {
|
||||
t.Error("check err")
|
||||
}
|
||||
if err = bm.Put(context.Background(), "astaxie", 1, timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
|
||||
val, _ := bm.Get(context.Background(), "astaxie")
|
||||
if v, _ := redis.Int(val, err); v != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
v, _ := redis.Int(val, err)
|
||||
assert.Equal(t, 1, v)
|
||||
|
||||
if err = bm.Incr(context.Background(), "astaxie"); err != nil {
|
||||
t.Error("Incr Error", err)
|
||||
}
|
||||
assert.Nil(t, bm.Incr(context.Background(), "astaxie"))
|
||||
val, _ = bm.Get(context.Background(), "astaxie")
|
||||
if v, _ := redis.Int(val, err); v != 2 {
|
||||
t.Error("get err")
|
||||
}
|
||||
v, _ = redis.Int(val, err)
|
||||
assert.Equal(t, 2, v)
|
||||
|
||||
if err = bm.Decr(context.Background(), "astaxie"); err != nil {
|
||||
t.Error("Decr Error", err)
|
||||
}
|
||||
assert.Nil(t, bm.Decr(context.Background(), "astaxie"))
|
||||
|
||||
val, _ = bm.Get(context.Background(), "astaxie")
|
||||
if v, _ := redis.Int(val, err); v != 1 {
|
||||
t.Error("get err")
|
||||
}
|
||||
v, _ = redis.Int(val, err)
|
||||
assert.Equal(t, 1, v)
|
||||
bm.Delete(context.Background(), "astaxie")
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie"); res {
|
||||
t.Error("delete err")
|
||||
}
|
||||
|
||||
res, _ = bm.IsExist(context.Background(), "astaxie")
|
||||
assert.False(t, res)
|
||||
|
||||
assert.Nil(t, bm.Put(context.Background(), "astaxie", "author", timeoutDuration))
|
||||
// test string
|
||||
if err = bm.Put(context.Background(), "astaxie", "author", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie"); !res {
|
||||
t.Error("check err")
|
||||
}
|
||||
|
||||
res, _ = bm.IsExist(context.Background(), "astaxie")
|
||||
assert.True(t, res)
|
||||
|
||||
val, _ = bm.Get(context.Background(), "astaxie")
|
||||
if v, _ := redis.String(val, err); v != "author" {
|
||||
t.Error("get err")
|
||||
}
|
||||
vs, _ := redis.String(val, err)
|
||||
assert.Equal(t, "author", vs)
|
||||
|
||||
// test GetMulti
|
||||
if err = bm.Put(context.Background(), "astaxie1", "author1", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if res, _ := bm.IsExist(context.Background(), "astaxie1"); !res {
|
||||
t.Error("check err")
|
||||
}
|
||||
assert.Nil(t, bm.Put(context.Background(), "astaxie1", "author1", timeoutDuration))
|
||||
|
||||
res, _ = bm.IsExist(context.Background(), "astaxie1")
|
||||
assert.True(t, res)
|
||||
|
||||
vv, _ := bm.GetMulti(context.Background(), []string{"astaxie", "astaxie1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if v, _ := redis.String(vv[0], nil); v != "author" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if v, _ := redis.String(vv[1], nil); v != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
assert.Equal(t, 2, len(vv))
|
||||
vs, _ = redis.String(vv[0], nil)
|
||||
assert.Equal(t, "author", vs)
|
||||
|
||||
vs, _ = redis.String(vv[1], nil)
|
||||
assert.Equal(t, "author1", vs)
|
||||
|
||||
vv, _ = bm.GetMulti(context.Background(), []string{"astaxie0", "astaxie1"})
|
||||
if vv[0] != nil {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
if v, _ := redis.String(vv[1], nil); v != "author1" {
|
||||
t.Error("GetMulti ERROR")
|
||||
}
|
||||
assert.Nil(t, vv[0])
|
||||
|
||||
vs, _ = redis.String(vv[1], nil)
|
||||
assert.Equal(t, "author1", vs)
|
||||
|
||||
// test clear all
|
||||
if err = bm.ClearAll(context.Background()); err != nil {
|
||||
t.Error("clear all err")
|
||||
}
|
||||
assert.Nil(t, bm.ClearAll(context.Background()))
|
||||
}
|
||||
|
||||
func TestCache_Scan(t *testing.T) {
|
||||
@ -137,35 +115,24 @@ func TestCache_Scan(t *testing.T) {
|
||||
|
||||
// init
|
||||
bm, err := cache.NewCache("redis", fmt.Sprintf(`{"conn": "%s"}`, addr))
|
||||
if err != nil {
|
||||
t.Error("init err")
|
||||
}
|
||||
|
||||
assert.Nil(t, err)
|
||||
// insert all
|
||||
for i := 0; i < 100; i++ {
|
||||
if err = bm.Put(context.Background(), fmt.Sprintf("astaxie%d", i), fmt.Sprintf("author%d", i), timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
assert.Nil(t, bm.Put(context.Background(), fmt.Sprintf("astaxie%d", i), fmt.Sprintf("author%d", i), timeoutDuration))
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
// scan all for the first time
|
||||
keys, err := bm.(*Cache).Scan(DefaultKey + ":*")
|
||||
if err != nil {
|
||||
t.Error("scan Error", err)
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 100, len(keys), "scan all error")
|
||||
|
||||
// clear all
|
||||
if err = bm.ClearAll(context.Background()); err != nil {
|
||||
t.Error("clear all err")
|
||||
}
|
||||
assert.Nil(t, bm.ClearAll(context.Background()))
|
||||
|
||||
// scan all for the second time
|
||||
keys, err = bm.(*Cache).Scan(DefaultKey + ":*")
|
||||
if err != nil {
|
||||
t.Error("scan Error", err)
|
||||
}
|
||||
if len(keys) != 0 {
|
||||
t.Error("scan all err")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 0, len(keys))
|
||||
}
|
||||
|
||||
111
client/cache/ssdb/ssdb.go
vendored
111
client/cache/ssdb/ssdb.go
vendored
@ -3,7 +3,6 @@ package ssdb
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -12,6 +11,7 @@ import (
|
||||
"github.com/ssdb/gossdb/ssdb"
|
||||
|
||||
"github.com/beego/beego/v2/client/cache"
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
// Cache SSDB adapter
|
||||
@ -27,31 +27,21 @@ func NewSsdbCache() cache.Cache {
|
||||
|
||||
// Get gets a key's value from memcache.
|
||||
func (rc *Cache) Get(ctx context.Context, key string) (interface{}, error) {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
value, err := rc.conn.Get(key)
|
||||
if err == nil {
|
||||
return value, nil
|
||||
}
|
||||
return nil, err
|
||||
return nil, berror.Wrapf(err, cache.SsdbCacheCurdFailed, "could not get value, key: %s", key)
|
||||
}
|
||||
|
||||
// GetMulti gets one or keys values from ssdb.
|
||||
func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
|
||||
size := len(keys)
|
||||
values := make([]interface{}, size)
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return values, err
|
||||
}
|
||||
}
|
||||
|
||||
res, err := rc.conn.Do("multi_get", keys)
|
||||
if err != nil {
|
||||
return values, err
|
||||
return values, berror.Wrapf(err, cache.SsdbCacheCurdFailed, "multi_get failed, key: %v", keys)
|
||||
}
|
||||
|
||||
resSize := len(res)
|
||||
@ -63,14 +53,14 @@ func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, er
|
||||
keysErr := make([]string, 0)
|
||||
for i, ki := range keys {
|
||||
if _, ok := keyIdx[ki]; !ok {
|
||||
keysErr = append(keysErr, fmt.Sprintf("key [%s] error: %s", ki, "the key isn't exist"))
|
||||
keysErr = append(keysErr, fmt.Sprintf("key [%s] error: %s", ki, "key not exist"))
|
||||
continue
|
||||
}
|
||||
values[i] = res[keyIdx[ki]+1]
|
||||
}
|
||||
|
||||
if len(keysErr) != 0 {
|
||||
return values, fmt.Errorf(strings.Join(keysErr, "; "))
|
||||
return values, berror.Error(cache.MultiGetFailed, strings.Join(keysErr, "; "))
|
||||
}
|
||||
|
||||
return values, nil
|
||||
@ -78,26 +68,16 @@ func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, er
|
||||
|
||||
// DelMulti deletes one or more keys from memcache
|
||||
func (rc *Cache) DelMulti(keys []string) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Do("multi_del", keys)
|
||||
return err
|
||||
return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "multi_del failed: %v", keys)
|
||||
}
|
||||
|
||||
// Put puts value into memcache.
|
||||
// value: must be of type string
|
||||
func (rc *Cache) Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
v, ok := val.(string)
|
||||
if !ok {
|
||||
return errors.New("value must string")
|
||||
return berror.Errorf(cache.InvalidSsdbCacheValue, "value must be string: %v", val)
|
||||
}
|
||||
var resp []string
|
||||
var err error
|
||||
@ -108,57 +88,37 @@ func (rc *Cache) Put(ctx context.Context, key string, val interface{}, timeout t
|
||||
resp, err = rc.conn.Do("setx", key, v, ttl)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "set or setx failed, key: %s", key)
|
||||
}
|
||||
if len(resp) == 2 && resp[0] == "ok" {
|
||||
return nil
|
||||
}
|
||||
return errors.New("bad response")
|
||||
return berror.Errorf(cache.SsdbBadResponse, "the response from SSDB server is invalid: %v", resp)
|
||||
}
|
||||
|
||||
// Delete deletes a value in memcache.
|
||||
func (rc *Cache) Delete(ctx context.Context, key string) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Del(key)
|
||||
return err
|
||||
return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "del failed: %s", key)
|
||||
}
|
||||
|
||||
// Incr increases a key's counter.
|
||||
func (rc *Cache) Incr(ctx context.Context, key string) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Do("incr", key, 1)
|
||||
return err
|
||||
return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "increase failed: %s", key)
|
||||
}
|
||||
|
||||
// Decr decrements a key's counter.
|
||||
func (rc *Cache) Decr(ctx context.Context, key string) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err := rc.conn.Do("incr", key, -1)
|
||||
return err
|
||||
return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "decrease failed: %s", key)
|
||||
}
|
||||
|
||||
// IsExist checks if a key exists in memcache.
|
||||
func (rc *Cache) IsExist(ctx context.Context, key string) (bool, error) {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
resp, err := rc.conn.Do("exists", key)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, berror.Wrapf(err, cache.SsdbCacheCurdFailed, "exists failed: %s", key)
|
||||
}
|
||||
if len(resp) == 2 && resp[1] == "1" {
|
||||
return true, nil
|
||||
@ -167,13 +127,9 @@ func (rc *Cache) IsExist(ctx context.Context, key string) (bool, error) {
|
||||
|
||||
}
|
||||
|
||||
// ClearAll clears all cached items in memcache.
|
||||
// ClearAll clears all cached items in ssdb.
|
||||
// If there are many keys, this method may spent much time.
|
||||
func (rc *Cache) ClearAll(context.Context) error {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
keyStart, keyEnd, limit := "", "", 50
|
||||
resp, err := rc.Scan(keyStart, keyEnd, limit)
|
||||
for err == nil {
|
||||
@ -187,21 +143,16 @@ func (rc *Cache) ClearAll(context.Context) error {
|
||||
}
|
||||
_, e := rc.conn.Do("multi_del", keys)
|
||||
if e != nil {
|
||||
return e
|
||||
return berror.Wrapf(e, cache.SsdbCacheCurdFailed, "multi_del failed: %v", keys)
|
||||
}
|
||||
keyStart = resp[size-2]
|
||||
resp, err = rc.Scan(keyStart, keyEnd, limit)
|
||||
}
|
||||
return err
|
||||
return berror.Wrap(err, cache.SsdbCacheCurdFailed, "scan failed")
|
||||
}
|
||||
|
||||
// Scan key all cached in ssdb.
|
||||
func (rc *Cache) Scan(keyStart string, keyEnd string, limit int) ([]string, error) {
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
resp, err := rc.conn.Do("scan", keyStart, keyEnd, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -214,30 +165,36 @@ func (rc *Cache) Scan(keyStart string, keyEnd string, limit int) ([]string, erro
|
||||
// If an error occurs during connection, an error is returned
|
||||
func (rc *Cache) StartAndGC(config string) error {
|
||||
var cf map[string]string
|
||||
json.Unmarshal([]byte(config), &cf)
|
||||
err := json.Unmarshal([]byte(config), &cf)
|
||||
if err != nil {
|
||||
return berror.Wrapf(err, cache.InvalidSsdbCacheCfg,
|
||||
"unmarshal this config failed, it must be a valid json string: %s", config)
|
||||
}
|
||||
if _, ok := cf["conn"]; !ok {
|
||||
return errors.New("config has no conn key")
|
||||
return berror.Wrapf(err, cache.InvalidSsdbCacheCfg,
|
||||
"Missing conn field: %s", config)
|
||||
}
|
||||
rc.conninfo = strings.Split(cf["conn"], ";")
|
||||
if rc.conn == nil {
|
||||
if err := rc.connectInit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return rc.connectInit()
|
||||
}
|
||||
|
||||
// connect to memcache and keep the connection.
|
||||
func (rc *Cache) connectInit() error {
|
||||
conninfoArray := strings.Split(rc.conninfo[0], ":")
|
||||
if len(conninfoArray) < 2 {
|
||||
return berror.Errorf(cache.InvalidSsdbCacheCfg, "The value of conn should be host:port: %s", rc.conninfo[0])
|
||||
}
|
||||
host := conninfoArray[0]
|
||||
port, e := strconv.Atoi(conninfoArray[1])
|
||||
if e != nil {
|
||||
return e
|
||||
return berror.Errorf(cache.InvalidSsdbCacheCfg, "Port is invalid. It must be integer, %s", rc.conninfo[0])
|
||||
}
|
||||
var err error
|
||||
rc.conn, err = ssdb.Connect(host, port)
|
||||
return err
|
||||
if rc.conn, err = ssdb.Connect(host, port); err != nil {
|
||||
return berror.Wrapf(err, cache.InvalidConnection,
|
||||
"could not connect to SSDB, please check your connection info, network and firewall: %s", rc.conninfo[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
137
client/cache/ssdb/ssdb_test.go
vendored
137
client/cache/ssdb/ssdb_test.go
vendored
@ -5,9 +5,12 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/beego/beego/v2/client/cache"
|
||||
)
|
||||
|
||||
@ -19,114 +22,80 @@ func TestSsdbcacheCache(t *testing.T) {
|
||||
}
|
||||
|
||||
ssdb, err := cache.NewCache("ssdb", fmt.Sprintf(`{"conn": "%s"}`, ssdbAddr))
|
||||
if err != nil {
|
||||
t.Error("init err")
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
|
||||
// test put and exist
|
||||
if res, _ := ssdb.IsExist(context.Background(), "ssdb"); res {
|
||||
t.Error("check err")
|
||||
}
|
||||
timeoutDuration := 10 * time.Second
|
||||
res, _ := ssdb.IsExist(context.Background(), "ssdb")
|
||||
assert.False(t, res)
|
||||
timeoutDuration := 3 * time.Second
|
||||
// timeoutDuration := -10*time.Second if timeoutDuration is negtive,it means permanent
|
||||
if err = ssdb.Put(context.Background(), "ssdb", "ssdb", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if res, _ := ssdb.IsExist(context.Background(), "ssdb"); !res {
|
||||
t.Error("check err")
|
||||
}
|
||||
|
||||
assert.Nil(t, ssdb.Put(context.Background(), "ssdb", "ssdb", timeoutDuration))
|
||||
|
||||
res, _ = ssdb.IsExist(context.Background(), "ssdb")
|
||||
assert.True(t, res)
|
||||
|
||||
// Get test done
|
||||
if err = ssdb.Put(context.Background(), "ssdb", "ssdb", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
assert.Nil(t, ssdb.Put(context.Background(), "ssdb", "ssdb", timeoutDuration))
|
||||
|
||||
if v, _ := ssdb.Get(context.Background(), "ssdb"); v != "ssdb" {
|
||||
t.Error("get Error")
|
||||
}
|
||||
v, _ := ssdb.Get(context.Background(), "ssdb")
|
||||
assert.Equal(t, "ssdb", v)
|
||||
|
||||
// inc/dec test done
|
||||
if err = ssdb.Put(context.Background(), "ssdb", "2", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if err = ssdb.Incr(context.Background(), "ssdb"); err != nil {
|
||||
t.Error("incr Error", err)
|
||||
}
|
||||
assert.Nil(t, ssdb.Put(context.Background(), "ssdb", "2", timeoutDuration))
|
||||
|
||||
assert.Nil(t, ssdb.Incr(context.Background(), "ssdb"))
|
||||
|
||||
val, _ := ssdb.Get(context.Background(), "ssdb")
|
||||
if v, err := strconv.Atoi(val.(string)); err != nil || v != 3 {
|
||||
t.Error("get err")
|
||||
}
|
||||
v, err = strconv.Atoi(val.(string))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, v)
|
||||
|
||||
if err = ssdb.Decr(context.Background(), "ssdb"); err != nil {
|
||||
t.Error("decr error")
|
||||
}
|
||||
assert.Nil(t, ssdb.Decr(context.Background(), "ssdb"))
|
||||
|
||||
// test del
|
||||
if err = ssdb.Put(context.Background(), "ssdb", "3", timeoutDuration); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
assert.Nil(t, ssdb.Put(context.Background(), "ssdb", "3", timeoutDuration))
|
||||
|
||||
val, _ = ssdb.Get(context.Background(), "ssdb")
|
||||
if v, err := strconv.Atoi(val.(string)); err != nil || v != 3 {
|
||||
t.Error("get err")
|
||||
}
|
||||
if err := ssdb.Delete(context.Background(), "ssdb"); err == nil {
|
||||
if e, _ := ssdb.IsExist(context.Background(), "ssdb"); e {
|
||||
t.Error("delete err")
|
||||
}
|
||||
}
|
||||
v, err = strconv.Atoi(val.(string))
|
||||
assert.Equal(t, 3, v)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Nil(t, ssdb.Delete(context.Background(), "ssdb"))
|
||||
assert.Nil(t, ssdb.Put(context.Background(), "ssdb", "ssdb", -10*time.Second))
|
||||
// test string
|
||||
if err = ssdb.Put(context.Background(), "ssdb", "ssdb", -10*time.Second); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if res, _ := ssdb.IsExist(context.Background(), "ssdb"); !res {
|
||||
t.Error("check err")
|
||||
}
|
||||
if v, _ := ssdb.Get(context.Background(), "ssdb"); v.(string) != "ssdb" {
|
||||
t.Error("get err")
|
||||
}
|
||||
|
||||
res, _ = ssdb.IsExist(context.Background(), "ssdb")
|
||||
assert.True(t, res)
|
||||
|
||||
v, _ = ssdb.Get(context.Background(), "ssdb")
|
||||
assert.Equal(t, "ssdb", v.(string))
|
||||
|
||||
// test GetMulti done
|
||||
if err = ssdb.Put(context.Background(), "ssdb1", "ssdb1", -10*time.Second); err != nil {
|
||||
t.Error("set Error", err)
|
||||
}
|
||||
if res, _ := ssdb.IsExist(context.Background(), "ssdb1"); !res {
|
||||
t.Error("check err")
|
||||
}
|
||||
assert.Nil(t, ssdb.Put(context.Background(), "ssdb1", "ssdb1", -10*time.Second))
|
||||
|
||||
res, _ = ssdb.IsExist(context.Background(), "ssdb1")
|
||||
assert.True(t, res)
|
||||
vv, _ := ssdb.GetMulti(context.Background(), []string{"ssdb", "ssdb1"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("getmulti error")
|
||||
}
|
||||
if vv[0].(string) != "ssdb" {
|
||||
t.Error("getmulti error")
|
||||
}
|
||||
if vv[1].(string) != "ssdb1" {
|
||||
t.Error("getmulti error")
|
||||
}
|
||||
assert.Equal(t, 2, len(vv))
|
||||
|
||||
assert.Equal(t, "ssdb", vv[0])
|
||||
assert.Equal(t, "ssdb1", vv[1])
|
||||
|
||||
vv, err = ssdb.GetMulti(context.Background(), []string{"ssdb", "ssdb11"})
|
||||
if len(vv) != 2 {
|
||||
t.Error("getmulti error")
|
||||
}
|
||||
if vv[0].(string) != "ssdb" {
|
||||
t.Error("getmulti error")
|
||||
}
|
||||
if vv[1] != nil {
|
||||
t.Error("getmulti error")
|
||||
}
|
||||
if err != nil && err.Error() != "key [ssdb11] error: the key isn't exist" {
|
||||
t.Error("getmulti error")
|
||||
}
|
||||
|
||||
assert.Equal(t, 2, len(vv))
|
||||
|
||||
assert.Equal(t, "ssdb", vv[0])
|
||||
assert.Nil(t, vv[1])
|
||||
|
||||
assert.NotNil(t, err)
|
||||
assert.True(t, strings.Contains(err.Error(), "key not exist"))
|
||||
|
||||
// test clear all done
|
||||
if err = ssdb.ClearAll(context.Background()); err != nil {
|
||||
t.Error("clear all err")
|
||||
}
|
||||
assert.Nil(t, ssdb.ClearAll(context.Background()))
|
||||
e1, _ := ssdb.IsExist(context.Background(), "ssdb")
|
||||
e2, _ := ssdb.IsExist(context.Background(), "ssdb1")
|
||||
if e1 || e2 {
|
||||
t.Error("check err")
|
||||
}
|
||||
assert.False(t, e1)
|
||||
assert.False(t, e2)
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ httplib is an libs help you to curl remote url.
|
||||
|
||||
you can use Get to crawl data.
|
||||
|
||||
import "github.com/beego/beego/v2/httplib"
|
||||
import "github.com/beego/beego/v2/client/httplib"
|
||||
|
||||
str, err := httplib.Get("http://beego.me/").String()
|
||||
if err != nil {
|
||||
@ -95,4 +95,4 @@ httplib support mutil file upload, use `req.PostFile()`
|
||||
|
||||
See godoc for further documentation and examples.
|
||||
|
||||
* [godoc.org/github.com/beego/beego/v2/httplib](https://godoc.org/github.com/beego/beego/v2/httplib)
|
||||
* [godoc.org/github.com/beego/beego/v2/client/httplib](https://godoc.org/github.com/beego/beego/v2/client/httplib)
|
||||
|
||||
126
client/httplib/error_code.go
Normal file
126
client/httplib/error_code.go
Normal file
@ -0,0 +1,126 @@
|
||||
// Copyright 2020 beego
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package httplib
|
||||
|
||||
import (
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
)
|
||||
|
||||
var InvalidUrl = berror.DefineCode(4001001, moduleName, "InvalidUrl", `
|
||||
You pass a invalid url to httplib module. Please check your url, be careful about special character.
|
||||
`)
|
||||
|
||||
var InvalidUrlProtocolVersion = berror.DefineCode(4001002, moduleName, "InvalidUrlProtocolVersion", `
|
||||
You pass a invalid protocol version. In practice, we use HTTP/1.0, HTTP/1.1, HTTP/1.2
|
||||
But something like HTTP/3.2 is valid for client, and the major version is 3, minor version is 2.
|
||||
but you must confirm that server support those abnormal protocol version.
|
||||
`)
|
||||
|
||||
var UnsupportedBodyType = berror.DefineCode(4001003, moduleName, "UnsupportedBodyType", `
|
||||
You use a invalid data as request body.
|
||||
For now, we only support type string and byte[].
|
||||
`)
|
||||
|
||||
var InvalidXMLBody = berror.DefineCode(4001004, moduleName, "InvalidXMLBody", `
|
||||
You pass invalid data which could not be converted to XML documents. In general, if you pass structure, it works well.
|
||||
Sometimes you got XML document and you want to make it as request body. So you call XMLBody.
|
||||
If you do this, you got this code. Instead, you should call Header to set Content-type and call Body to set body data.
|
||||
`)
|
||||
|
||||
var InvalidYAMLBody = berror.DefineCode(4001005, moduleName, "InvalidYAMLBody", `
|
||||
You pass invalid data which could not be converted to YAML documents. In general, if you pass structure, it works well.
|
||||
Sometimes you got YAML document and you want to make it as request body. So you call YAMLBody.
|
||||
If you do this, you got this code. Instead, you should call Header to set Content-type and call Body to set body data.
|
||||
`)
|
||||
|
||||
var InvalidJSONBody = berror.DefineCode(4001006, moduleName, "InvalidJSONBody", `
|
||||
You pass invalid data which could not be converted to JSON documents. In general, if you pass structure, it works well.
|
||||
Sometimes you got JSON document and you want to make it as request body. So you call JSONBody.
|
||||
If you do this, you got this code. Instead, you should call Header to set Content-type and call Body to set body data.
|
||||
`)
|
||||
|
||||
// start with 5 --------------------------------------------------------------------------
|
||||
|
||||
var CreateFormFileFailed = berror.DefineCode(5001001, moduleName, "CreateFormFileFailed", `
|
||||
In normal case than handling files with BeegoRequest, you should not see this error code.
|
||||
Unexpected EOF, invalid characters, bad file descriptor may cause this error.
|
||||
`)
|
||||
|
||||
var ReadFileFailed = berror.DefineCode(5001002, moduleName, "ReadFileFailed", `
|
||||
There are several cases that cause this error:
|
||||
1. file not found. Please check the file name;
|
||||
2. file not found, but file name is correct. If you use relative file path, it's very possible for you to see this code.
|
||||
make sure that this file is in correct directory which Beego looks for;
|
||||
3. Beego don't have the privilege to read this file, please change file mode;
|
||||
`)
|
||||
|
||||
var CopyFileFailed = berror.DefineCode(5001003, moduleName, "CopyFileFailed", `
|
||||
When we try to read file content and then copy it to another writer, and failed.
|
||||
1. Unexpected EOF;
|
||||
2. Bad file descriptor;
|
||||
3. Write conflict;
|
||||
|
||||
Please check your file content, and confirm that file is not processed by other process (or by user manually).
|
||||
`)
|
||||
|
||||
var CloseFileFailed = berror.DefineCode(5001004, moduleName, "CloseFileFailed", `
|
||||
After handling files, Beego try to close file but failed. Usually it was caused by bad file descriptor.
|
||||
`)
|
||||
|
||||
var SendRequestFailed = berror.DefineCode(5001005, moduleName, "SendRequestRetryExhausted", `
|
||||
Beego send HTTP request, but it failed.
|
||||
If you config retry times, it means that Beego had retried and failed.
|
||||
When you got this error, there are vary kind of reason:
|
||||
1. Network unstable and timeout. In this case, sometimes server has received the request.
|
||||
2. Server error. Make sure that server works well.
|
||||
3. The request is invalid, which means that you pass some invalid parameter.
|
||||
`)
|
||||
|
||||
var ReadGzipBodyFailed = berror.DefineCode(5001006, moduleName, "BuildGzipReaderFailed", `
|
||||
Beego parse gzip-encode body failed. Usually Beego got invalid response.
|
||||
Please confirm that server returns gzip data.
|
||||
`)
|
||||
|
||||
var CreateFileIfNotExistFailed = berror.DefineCode(5001007, moduleName, "CreateFileIfNotExist", `
|
||||
Beego want to create file if not exist and failed.
|
||||
In most cases, it means that Beego doesn't have the privilege to create this file.
|
||||
Please change file mode to ensure that Beego is able to create files on specific directory.
|
||||
Or you can run Beego with higher authority.
|
||||
In some cases, you pass invalid filename. Make sure that the file name is valid on your system.
|
||||
`)
|
||||
|
||||
var UnmarshalJSONResponseToObjectFailed = berror.DefineCode(5001008, moduleName,
|
||||
"UnmarshalResponseToObjectFailed", `
|
||||
Beego trying to unmarshal response's body to structure but failed.
|
||||
Make sure that:
|
||||
1. You pass valid structure pointer to the function;
|
||||
2. The body is valid json document
|
||||
`)
|
||||
|
||||
var UnmarshalXMLResponseToObjectFailed = berror.DefineCode(5001009, moduleName,
|
||||
"UnmarshalResponseToObjectFailed", `
|
||||
Beego trying to unmarshal response's body to structure but failed.
|
||||
Make sure that:
|
||||
1. You pass valid structure pointer to the function;
|
||||
2. The body is valid XML document
|
||||
`)
|
||||
|
||||
var UnmarshalYAMLResponseToObjectFailed = berror.DefineCode(5001010, moduleName,
|
||||
"UnmarshalResponseToObjectFailed", `
|
||||
Beego trying to unmarshal response's body to structure but failed.
|
||||
Make sure that:
|
||||
1. You pass valid structure pointer to the function;
|
||||
2. The body is valid YAML document
|
||||
`)
|
||||
130
client/httplib/filter/log/filter.go
Normal file
130
client/httplib/filter/log/filter.go
Normal file
@ -0,0 +1,130 @@
|
||||
// Copyright 2020 beego
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
|
||||
"github.com/beego/beego/v2/client/httplib"
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
)
|
||||
|
||||
// FilterChainBuilder can build a log filter
|
||||
type FilterChainBuilder struct {
|
||||
printableContentTypes []string // only print the body of included mime types of request and response
|
||||
log func(f interface{}, v ...interface{}) // custom log function
|
||||
}
|
||||
|
||||
// BuilderOption option constructor
|
||||
type BuilderOption func(*FilterChainBuilder)
|
||||
|
||||
type logInfo struct {
|
||||
req []byte
|
||||
resp []byte
|
||||
err error
|
||||
}
|
||||
|
||||
var defaultprintableContentTypes = []string{
|
||||
"text/plain", "text/xml", "text/html", "text/csv",
|
||||
"text/calendar", "text/javascript", "text/javascript",
|
||||
"text/css",
|
||||
}
|
||||
|
||||
// NewFilterChainBuilder initialize a filterChainBuilder, pass options to customize
|
||||
func NewFilterChainBuilder(opts ...BuilderOption) *FilterChainBuilder {
|
||||
res := &FilterChainBuilder{
|
||||
printableContentTypes: defaultprintableContentTypes,
|
||||
log: logs.Debug,
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(res)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// WithLog return option constructor modify log function
|
||||
func WithLog(f func(f interface{}, v ...interface{})) BuilderOption {
|
||||
return func(h *FilterChainBuilder) {
|
||||
h.log = f
|
||||
}
|
||||
}
|
||||
|
||||
// WithprintableContentTypes return option constructor modify printableContentTypes
|
||||
func WithprintableContentTypes(types []string) BuilderOption {
|
||||
return func(h *FilterChainBuilder) {
|
||||
h.printableContentTypes = types
|
||||
}
|
||||
}
|
||||
|
||||
// FilterChain can print the request after FilterChain processing and response before processsing
|
||||
func (builder *FilterChainBuilder) FilterChain(next httplib.Filter) httplib.Filter {
|
||||
return func(ctx context.Context, req *httplib.BeegoHTTPRequest) (*http.Response, error) {
|
||||
info := &logInfo{}
|
||||
defer info.print(builder.log)
|
||||
resp, err := next(ctx, req)
|
||||
info.err = err
|
||||
contentType := req.GetRequest().Header.Get("Content-Type")
|
||||
shouldPrintBody := builder.shouldPrintBody(contentType, req.GetRequest().Body)
|
||||
dump, err := httputil.DumpRequest(req.GetRequest(), shouldPrintBody)
|
||||
info.req = dump
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
}
|
||||
if resp != nil {
|
||||
contentType = resp.Header.Get("Content-Type")
|
||||
shouldPrintBody = builder.shouldPrintBody(contentType, resp.Body)
|
||||
dump, err = httputil.DumpResponse(resp, shouldPrintBody)
|
||||
info.resp = dump
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
}
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
func (builder *FilterChainBuilder) shouldPrintBody(contentType string, body io.ReadCloser) bool {
|
||||
if contains(builder.printableContentTypes, contentType) {
|
||||
return true
|
||||
}
|
||||
if body != nil {
|
||||
logs.Warn("printableContentTypes do not contain %s, if you want to print request and response body please add it.", contentType)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func contains(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (info *logInfo) print(log func(f interface{}, v ...interface{})) {
|
||||
log("Request: ====================")
|
||||
log("%q", info.req)
|
||||
log("Response: ===================")
|
||||
log("%q", info.resp)
|
||||
if info.err != nil {
|
||||
log("Error: ======================")
|
||||
log("%q", info.err)
|
||||
}
|
||||
}
|
||||
62
client/httplib/filter/log/filter_test.go
Normal file
62
client/httplib/filter/log/filter_test.go
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright 2020 beego
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/client/httplib"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFilterChain(t *testing.T) {
|
||||
next := func(ctx context.Context, req *httplib.BeegoHTTPRequest) (*http.Response, error) {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
return &http.Response{
|
||||
StatusCode: 404,
|
||||
}, nil
|
||||
}
|
||||
builder := NewFilterChainBuilder()
|
||||
filter := builder.FilterChain(next)
|
||||
req := httplib.Get("https://github.com/notifications?query=repo%3Aastaxie%2Fbeego")
|
||||
resp, err := filter(context.Background(), req)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestContains(t *testing.T) {
|
||||
jsonType := "application/json"
|
||||
cases := []struct {
|
||||
Name string
|
||||
Types []string
|
||||
ContentType string
|
||||
Expected bool
|
||||
}{
|
||||
{"case1", []string{jsonType}, jsonType, true},
|
||||
{"case2", []string{"text/plain"}, jsonType, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.Name, func(t *testing.T) {
|
||||
if ans := contains(c.Types, c.ContentType); ans != c.Expected {
|
||||
t.Fatalf("Types: %v, ContentType: %v, expected %v, but %v got",
|
||||
c.Types, c.ContentType, c.Expected, ans)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -21,11 +21,14 @@ import (
|
||||
logKit "github.com/go-kit/kit/log"
|
||||
opentracingKit "github.com/go-kit/kit/tracing/opentracing"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/log"
|
||||
|
||||
"github.com/beego/beego/v2/client/httplib"
|
||||
)
|
||||
|
||||
type FilterChainBuilder struct {
|
||||
// TagURL true will tag span with url
|
||||
TagURL bool
|
||||
// CustomSpanFunc users are able to custom their span
|
||||
CustomSpanFunc func(span opentracing.Span, ctx context.Context,
|
||||
req *httplib.BeegoHTTPRequest, resp *http.Response, err error)
|
||||
@ -50,13 +53,19 @@ func (builder *FilterChainBuilder) FilterChain(next httplib.Filter) httplib.Filt
|
||||
}
|
||||
span.SetTag("http.method", method)
|
||||
span.SetTag("peer.hostname", req.GetRequest().URL.Host)
|
||||
span.SetTag("http.url", req.GetRequest().URL.String())
|
||||
|
||||
span.SetTag("http.scheme", req.GetRequest().URL.Scheme)
|
||||
span.SetTag("span.kind", "client")
|
||||
span.SetTag("component", "beego")
|
||||
|
||||
if builder.TagURL {
|
||||
span.SetTag("http.url", req.GetRequest().URL.String())
|
||||
}
|
||||
span.LogFields(log.String("http.url", req.GetRequest().URL.String()))
|
||||
|
||||
if err != nil {
|
||||
span.SetTag("error", true)
|
||||
span.SetTag("message", err.Error())
|
||||
span.LogFields(log.String("message", err.Error()))
|
||||
} else if resp != nil && !(resp.StatusCode < 300 && resp.StatusCode >= 200) {
|
||||
span.SetTag("error", true)
|
||||
}
|
||||
|
||||
@ -26,14 +26,16 @@ import (
|
||||
"github.com/beego/beego/v2/client/httplib"
|
||||
)
|
||||
|
||||
func TestFilterChainBuilder_FilterChain(t *testing.T) {
|
||||
func TestFilterChainBuilderFilterChain(t *testing.T) {
|
||||
next := func(ctx context.Context, req *httplib.BeegoHTTPRequest) (*http.Response, error) {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
return &http.Response{
|
||||
StatusCode: 404,
|
||||
}, errors.New("hello")
|
||||
}
|
||||
builder := &FilterChainBuilder{}
|
||||
builder := &FilterChainBuilder{
|
||||
TagURL: true,
|
||||
}
|
||||
filter := builder.FilterChain(next)
|
||||
req := httplib.Get("https://github.com/notifications?query=repo%3Aastaxie%2Fbeego")
|
||||
resp, err := filter(context.Background(), req)
|
||||
|
||||
@ -25,7 +25,7 @@ import (
|
||||
"github.com/beego/beego/v2/client/httplib"
|
||||
)
|
||||
|
||||
func TestFilterChainBuilder_FilterChain(t *testing.T) {
|
||||
func TestFilterChainBuilderFilterChain(t *testing.T) {
|
||||
next := func(ctx context.Context, req *httplib.BeegoHTTPRequest) (*http.Response, error) {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
return &http.Response{
|
||||
|
||||
39
client/httplib/http_response.go
Normal file
39
client/httplib/http_response.go
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2020 beego
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package httplib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// NewHttpResponseWithJsonBody will try to convert the data to json format
|
||||
// usually you only use this when you want to mock http Response
|
||||
func NewHttpResponseWithJsonBody(data interface{}) *http.Response {
|
||||
var body []byte
|
||||
if str, ok := data.(string); ok {
|
||||
body = []byte(str)
|
||||
} else if bts, ok := data.([]byte); ok {
|
||||
body = bts
|
||||
} else {
|
||||
body, _ = json.Marshal(data)
|
||||
}
|
||||
return &http.Response{
|
||||
ContentLength: int64(len(body)),
|
||||
Body: ioutil.NopCloser(bytes.NewReader(body)),
|
||||
}
|
||||
}
|
||||
35
client/httplib/http_response_test.go
Normal file
35
client/httplib/http_response_test.go
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2020 beego
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package httplib
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewHttpResponseWithJsonBody(t *testing.T) {
|
||||
// string
|
||||
resp := NewHttpResponseWithJsonBody("{}")
|
||||
assert.Equal(t, int64(2), resp.ContentLength)
|
||||
|
||||
resp = NewHttpResponseWithJsonBody([]byte("{}"))
|
||||
assert.Equal(t, int64(2), resp.ContentLength)
|
||||
|
||||
resp = NewHttpResponseWithJsonBody(&user{
|
||||
Name: "Tom",
|
||||
})
|
||||
assert.True(t, resp.ContentLength > 0)
|
||||
}
|
||||
@ -15,7 +15,7 @@
|
||||
// Package httplib is used as http.Client
|
||||
// Usage:
|
||||
//
|
||||
// import "github.com/beego/beego/v2/httplib"
|
||||
// import "github.com/beego/beego/v2/client/httplib"
|
||||
//
|
||||
// b := httplib.Post("http://beego.me/")
|
||||
// b.Param("username","astaxie")
|
||||
@ -40,58 +40,36 @@ import (
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/beego/beego/v2/core/berror"
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
)
|
||||
|
||||
var defaultSetting = BeegoHTTPSettings{
|
||||
UserAgent: "beegoServer",
|
||||
ConnectTimeout: 60 * time.Second,
|
||||
ReadWriteTimeout: 60 * time.Second,
|
||||
Gzip: true,
|
||||
DumpBody: true,
|
||||
}
|
||||
|
||||
var defaultCookieJar http.CookieJar
|
||||
var settingMutex sync.Mutex
|
||||
|
||||
const contentTypeKey = "Content-Type"
|
||||
// it will be the last filter and execute request.Do
|
||||
var doRequestFilter = func(ctx context.Context, req *BeegoHTTPRequest) (*http.Response, error) {
|
||||
return req.doRequest(ctx)
|
||||
}
|
||||
|
||||
// createDefaultCookie creates a global cookiejar to store cookies.
|
||||
func createDefaultCookie() {
|
||||
settingMutex.Lock()
|
||||
defer settingMutex.Unlock()
|
||||
defaultCookieJar, _ = cookiejar.New(nil)
|
||||
}
|
||||
|
||||
// SetDefaultSetting overwrites default settings
|
||||
func SetDefaultSetting(setting BeegoHTTPSettings) {
|
||||
settingMutex.Lock()
|
||||
defer settingMutex.Unlock()
|
||||
defaultSetting = setting
|
||||
}
|
||||
|
||||
// NewBeegoRequest returns *BeegoHttpRequest with specific method
|
||||
// TODO add error as return value
|
||||
// I think if we don't return error
|
||||
// users are hard to check whether we create Beego request successfully
|
||||
func NewBeegoRequest(rawurl, method string) *BeegoHTTPRequest {
|
||||
var resp http.Response
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
log.Println("Httplib:", err)
|
||||
logs.Error("%+v", berror.Wrapf(err, InvalidUrl, "invalid raw url: %s", rawurl))
|
||||
}
|
||||
req := http.Request{
|
||||
URL: u,
|
||||
@ -136,24 +114,6 @@ func Head(url string) *BeegoHTTPRequest {
|
||||
return NewBeegoRequest(url, "HEAD")
|
||||
}
|
||||
|
||||
// BeegoHTTPSettings is the http.Client setting
|
||||
type BeegoHTTPSettings struct {
|
||||
ShowDebug bool
|
||||
UserAgent string
|
||||
ConnectTimeout time.Duration
|
||||
ReadWriteTimeout time.Duration
|
||||
TLSClientConfig *tls.Config
|
||||
Proxy func(*http.Request) (*url.URL, error)
|
||||
Transport http.RoundTripper
|
||||
CheckRedirect func(req *http.Request, via []*http.Request) error
|
||||
EnableCookie bool
|
||||
Gzip bool
|
||||
DumpBody bool
|
||||
Retries int // if set to -1 means will retry forever
|
||||
RetryDelay time.Duration
|
||||
FilterChains []FilterChain
|
||||
}
|
||||
|
||||
// BeegoHTTPRequest provides more useful methods than http.Request for requesting a url.
|
||||
type BeegoHTTPRequest struct {
|
||||
url string
|
||||
@ -195,12 +155,6 @@ func (b *BeegoHTTPRequest) SetUserAgent(useragent string) *BeegoHTTPRequest {
|
||||
return b
|
||||
}
|
||||
|
||||
// Debug sets show debug or not when executing request.
|
||||
func (b *BeegoHTTPRequest) Debug(isdebug bool) *BeegoHTTPRequest {
|
||||
b.setting.ShowDebug = isdebug
|
||||
return b
|
||||
}
|
||||
|
||||
// Retries sets Retries times.
|
||||
// default is 0 (never retry)
|
||||
// -1 retry indefinitely (forever)
|
||||
@ -216,17 +170,6 @@ func (b *BeegoHTTPRequest) RetryDelay(delay time.Duration) *BeegoHTTPRequest {
|
||||
return b
|
||||
}
|
||||
|
||||
// DumpBody sets the DumbBody field
|
||||
func (b *BeegoHTTPRequest) DumpBody(isdump bool) *BeegoHTTPRequest {
|
||||
b.setting.DumpBody = isdump
|
||||
return b
|
||||
}
|
||||
|
||||
// DumpRequest returns the DumpRequest
|
||||
func (b *BeegoHTTPRequest) DumpRequest() []byte {
|
||||
return b.dump
|
||||
}
|
||||
|
||||
// SetTimeout sets connect time out and read-write time out for BeegoRequest.
|
||||
func (b *BeegoHTTPRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHTTPRequest {
|
||||
b.setting.ConnectTimeout = connectTimeout
|
||||
@ -253,7 +196,7 @@ func (b *BeegoHTTPRequest) SetHost(host string) *BeegoHTTPRequest {
|
||||
}
|
||||
|
||||
// SetProtocolVersion sets the protocol version for incoming requests.
|
||||
// Client requests always use HTTP/1.1.
|
||||
// Client requests always use HTTP/1.1
|
||||
func (b *BeegoHTTPRequest) SetProtocolVersion(vers string) *BeegoHTTPRequest {
|
||||
if len(vers) == 0 {
|
||||
vers = "HTTP/1.1"
|
||||
@ -264,8 +207,9 @@ func (b *BeegoHTTPRequest) SetProtocolVersion(vers string) *BeegoHTTPRequest {
|
||||
b.req.Proto = vers
|
||||
b.req.ProtoMajor = major
|
||||
b.req.ProtoMinor = minor
|
||||
return b
|
||||
}
|
||||
|
||||
logs.Error("%+v", berror.Errorf(InvalidUrlProtocolVersion, "invalid protocol: %s", vers))
|
||||
return b
|
||||
}
|
||||
|
||||
@ -333,16 +277,25 @@ func (b *BeegoHTTPRequest) PostFile(formname, filename string) *BeegoHTTPRequest
|
||||
|
||||
// Body adds request raw body.
|
||||
// Supports string and []byte.
|
||||
// TODO return error if data is invalid
|
||||
func (b *BeegoHTTPRequest) Body(data interface{}) *BeegoHTTPRequest {
|
||||
switch t := data.(type) {
|
||||
case string:
|
||||
bf := bytes.NewBufferString(t)
|
||||
b.req.Body = ioutil.NopCloser(bf)
|
||||
b.req.GetBody = func() (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(bf), nil
|
||||
}
|
||||
b.req.ContentLength = int64(len(t))
|
||||
case []byte:
|
||||
bf := bytes.NewBuffer(t)
|
||||
b.req.Body = ioutil.NopCloser(bf)
|
||||
b.req.GetBody = func() (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(bf), nil
|
||||
}
|
||||
b.req.ContentLength = int64(len(t))
|
||||
default:
|
||||
logs.Error("%+v", berror.Errorf(UnsupportedBodyType, "unsupported body data type: %s", t))
|
||||
}
|
||||
return b
|
||||
}
|
||||
@ -352,11 +305,14 @@ func (b *BeegoHTTPRequest) XMLBody(obj interface{}) (*BeegoHTTPRequest, error) {
|
||||
if b.req.Body == nil && obj != nil {
|
||||
byts, err := xml.Marshal(obj)
|
||||
if err != nil {
|
||||
return b, err
|
||||
return b, berror.Wrap(err, InvalidXMLBody, "obj could not be converted to XML data")
|
||||
}
|
||||
b.req.Body = ioutil.NopCloser(bytes.NewReader(byts))
|
||||
b.req.GetBody = func() (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(bytes.NewReader(byts)), nil
|
||||
}
|
||||
b.req.ContentLength = int64(len(byts))
|
||||
b.req.Header.Set("Content-Type", "application/xml")
|
||||
b.req.Header.Set(contentTypeKey, "application/xml")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
@ -366,11 +322,11 @@ func (b *BeegoHTTPRequest) YAMLBody(obj interface{}) (*BeegoHTTPRequest, error)
|
||||
if b.req.Body == nil && obj != nil {
|
||||
byts, err := yaml.Marshal(obj)
|
||||
if err != nil {
|
||||
return b, err
|
||||
return b, berror.Wrap(err, InvalidYAMLBody, "obj could not be converted to YAML data")
|
||||
}
|
||||
b.req.Body = ioutil.NopCloser(bytes.NewReader(byts))
|
||||
b.req.ContentLength = int64(len(byts))
|
||||
b.req.Header.Set("Content-Type", "application/x+yaml")
|
||||
b.req.Header.Set(contentTypeKey, "application/x+yaml")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
@ -380,11 +336,11 @@ func (b *BeegoHTTPRequest) JSONBody(obj interface{}) (*BeegoHTTPRequest, error)
|
||||
if b.req.Body == nil && obj != nil {
|
||||
byts, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return b, err
|
||||
return b, berror.Wrap(err, InvalidJSONBody, "obj could not be converted to JSON body")
|
||||
}
|
||||
b.req.Body = ioutil.NopCloser(bytes.NewReader(byts))
|
||||
b.req.ContentLength = int64(len(byts))
|
||||
b.req.Header.Set("Content-Type", "application/json")
|
||||
b.req.Header.Set(contentTypeKey, "application/json")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
@ -404,47 +360,61 @@ func (b *BeegoHTTPRequest) buildURL(paramBody string) {
|
||||
if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH" || b.req.Method == "DELETE") && b.req.Body == nil {
|
||||
// with files
|
||||
if len(b.files) > 0 {
|
||||
pr, pw := io.Pipe()
|
||||
bodyWriter := multipart.NewWriter(pw)
|
||||
go func() {
|
||||
for formname, filename := range b.files {
|
||||
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
|
||||
if err != nil {
|
||||
log.Println("Httplib:", err)
|
||||
}
|
||||
fh, err := os.Open(filename)
|
||||
if err != nil {
|
||||
log.Println("Httplib:", err)
|
||||
}
|
||||
// iocopy
|
||||
_, err = io.Copy(fileWriter, fh)
|
||||
fh.Close()
|
||||
if err != nil {
|
||||
log.Println("Httplib:", err)
|
||||
}
|
||||
}
|
||||
for k, v := range b.params {
|
||||
for _, vv := range v {
|
||||
bodyWriter.WriteField(k, vv)
|
||||
}
|
||||
}
|
||||
bodyWriter.Close()
|
||||
pw.Close()
|
||||
}()
|
||||
b.Header("Content-Type", bodyWriter.FormDataContentType())
|
||||
b.req.Body = ioutil.NopCloser(pr)
|
||||
b.Header("Transfer-Encoding", "chunked")
|
||||
b.handleFiles()
|
||||
return
|
||||
}
|
||||
|
||||
// with params
|
||||
if len(paramBody) > 0 {
|
||||
b.Header("Content-Type", "application/x-www-form-urlencoded")
|
||||
b.Header(contentTypeKey, "application/x-www-form-urlencoded")
|
||||
b.Body(paramBody)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BeegoHTTPRequest) handleFiles() {
|
||||
pr, pw := io.Pipe()
|
||||
bodyWriter := multipart.NewWriter(pw)
|
||||
go func() {
|
||||
for formname, filename := range b.files {
|
||||
b.handleFileToBody(bodyWriter, formname, filename)
|
||||
}
|
||||
for k, v := range b.params {
|
||||
for _, vv := range v {
|
||||
_ = bodyWriter.WriteField(k, vv)
|
||||
}
|
||||
}
|
||||
_ = bodyWriter.Close()
|
||||
_ = pw.Close()
|
||||
}()
|
||||
b.Header(contentTypeKey, bodyWriter.FormDataContentType())
|
||||
b.req.Body = ioutil.NopCloser(pr)
|
||||
b.Header("Transfer-Encoding", "chunked")
|
||||
}
|
||||
|
||||
func (b *BeegoHTTPRequest) handleFileToBody(bodyWriter *multipart.Writer, formname string, filename string) {
|
||||
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
|
||||
const errFmt = "Httplib: %+v"
|
||||
if err != nil {
|
||||
logs.Error(errFmt, berror.Wrapf(err, CreateFormFileFailed,
|
||||
"could not create form file, formname: %s, filename: %s", formname, filename))
|
||||
}
|
||||
fh, err := os.Open(filename)
|
||||
|
||||
if err != nil {
|
||||
logs.Error(errFmt, berror.Wrapf(err, ReadFileFailed, "could not open this file %s", filename))
|
||||
}
|
||||
// iocopy
|
||||
_, err = io.Copy(fileWriter, fh)
|
||||
if err != nil {
|
||||
logs.Error(errFmt, berror.Wrapf(err, CopyFileFailed, "could not copy this file %s", filename))
|
||||
}
|
||||
err = fh.Close()
|
||||
if err != nil {
|
||||
logs.Error(errFmt, berror.Wrapf(err, CloseFileFailed, "could not close this file %s", filename))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BeegoHTTPRequest) getResponse() (*http.Response, error) {
|
||||
if b.resp.StatusCode != 0 {
|
||||
return b.resp, nil
|
||||
@ -463,7 +433,6 @@ func (b *BeegoHTTPRequest) DoRequest() (resp *http.Response, err error) {
|
||||
}
|
||||
|
||||
func (b *BeegoHTTPRequest) DoRequestWithCtx(ctx context.Context) (resp *http.Response, err error) {
|
||||
|
||||
root := doRequestFilter
|
||||
if len(b.setting.FilterChains) > 0 {
|
||||
for i := len(b.setting.FilterChains) - 1; i >= 0; i-- {
|
||||
@ -473,62 +442,20 @@ func (b *BeegoHTTPRequest) DoRequestWithCtx(ctx context.Context) (resp *http.Res
|
||||
return root(ctx, b)
|
||||
}
|
||||
|
||||
func (b *BeegoHTTPRequest) doRequest(ctx context.Context) (resp *http.Response, err error) {
|
||||
var paramBody string
|
||||
if len(b.params) > 0 {
|
||||
var buf bytes.Buffer
|
||||
for k, v := range b.params {
|
||||
for _, vv := range v {
|
||||
buf.WriteString(url.QueryEscape(k))
|
||||
buf.WriteByte('=')
|
||||
buf.WriteString(url.QueryEscape(vv))
|
||||
buf.WriteByte('&')
|
||||
}
|
||||
}
|
||||
paramBody = buf.String()
|
||||
paramBody = paramBody[0 : len(paramBody)-1]
|
||||
}
|
||||
func (b *BeegoHTTPRequest) doRequest(ctx context.Context) (*http.Response, error) {
|
||||
paramBody := b.buildParamBody()
|
||||
|
||||
b.buildURL(paramBody)
|
||||
urlParsed, err := url.Parse(b.url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, berror.Wrapf(err, InvalidUrl, "parse url failed, the url is %s", b.url)
|
||||
}
|
||||
|
||||
b.req.URL = urlParsed
|
||||
|
||||
trans := b.setting.Transport
|
||||
trans := b.buildTrans()
|
||||
|
||||
if trans == nil {
|
||||
// create default transport
|
||||
trans = &http.Transport{
|
||||
TLSClientConfig: b.setting.TLSClientConfig,
|
||||
Proxy: b.setting.Proxy,
|
||||
Dial: TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout),
|
||||
MaxIdleConnsPerHost: 100,
|
||||
}
|
||||
} else {
|
||||
// if b.transport is *http.Transport then set the settings.
|
||||
if t, ok := trans.(*http.Transport); ok {
|
||||
if t.TLSClientConfig == nil {
|
||||
t.TLSClientConfig = b.setting.TLSClientConfig
|
||||
}
|
||||
if t.Proxy == nil {
|
||||
t.Proxy = b.setting.Proxy
|
||||
}
|
||||
if t.Dial == nil {
|
||||
t.Dial = TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var jar http.CookieJar
|
||||
if b.setting.EnableCookie {
|
||||
if defaultCookieJar == nil {
|
||||
createDefaultCookie()
|
||||
}
|
||||
jar = defaultCookieJar
|
||||
}
|
||||
jar := b.buildCookieJar()
|
||||
|
||||
client := &http.Client{
|
||||
Transport: trans,
|
||||
@ -543,13 +470,10 @@ func (b *BeegoHTTPRequest) doRequest(ctx context.Context) (resp *http.Response,
|
||||
client.CheckRedirect = b.setting.CheckRedirect
|
||||
}
|
||||
|
||||
if b.setting.ShowDebug {
|
||||
dump, err := httputil.DumpRequest(b.req, b.setting.DumpBody)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
b.dump = dump
|
||||
}
|
||||
return b.sendRequest(client)
|
||||
}
|
||||
|
||||
func (b *BeegoHTTPRequest) sendRequest(client *http.Client) (resp *http.Response, err error) {
|
||||
// retries default value is 0, it will run once.
|
||||
// retries equal to -1, it will run forever until success
|
||||
// retries is setted, it will retries fixed times.
|
||||
@ -557,11 +481,68 @@ func (b *BeegoHTTPRequest) doRequest(ctx context.Context) (resp *http.Response,
|
||||
for i := 0; b.setting.Retries == -1 || i <= b.setting.Retries; i++ {
|
||||
resp, err = client.Do(b.req)
|
||||
if err == nil {
|
||||
break
|
||||
return
|
||||
}
|
||||
time.Sleep(b.setting.RetryDelay)
|
||||
}
|
||||
return resp, err
|
||||
return nil, berror.Wrap(err, SendRequestFailed, "sending request fail")
|
||||
}
|
||||
|
||||
func (b *BeegoHTTPRequest) buildCookieJar() http.CookieJar {
|
||||
var jar http.CookieJar
|
||||
if b.setting.EnableCookie {
|
||||
if defaultCookieJar == nil {
|
||||
createDefaultCookie()
|
||||
}
|
||||
jar = defaultCookieJar
|
||||
}
|
||||
return jar
|
||||
}
|
||||
|
||||
func (b *BeegoHTTPRequest) buildTrans() http.RoundTripper {
|
||||
trans := b.setting.Transport
|
||||
|
||||
if trans == nil {
|
||||
// create default transport
|
||||
trans = &http.Transport{
|
||||
TLSClientConfig: b.setting.TLSClientConfig,
|
||||
Proxy: b.setting.Proxy,
|
||||
DialContext: TimeoutDialerCtx(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout),
|
||||
MaxIdleConnsPerHost: 100,
|
||||
}
|
||||
} else {
|
||||
// if b.transport is *http.Transport then set the settings.
|
||||
if t, ok := trans.(*http.Transport); ok {
|
||||
if t.TLSClientConfig == nil {
|
||||
t.TLSClientConfig = b.setting.TLSClientConfig
|
||||
}
|
||||
if t.Proxy == nil {
|
||||
t.Proxy = b.setting.Proxy
|
||||
}
|
||||
if t.DialContext == nil {
|
||||
t.DialContext = TimeoutDialerCtx(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
return trans
|
||||
}
|
||||
|
||||
func (b *BeegoHTTPRequest) buildParamBody() string {
|
||||
var paramBody string
|
||||
if len(b.params) > 0 {
|
||||
var buf bytes.Buffer
|
||||
for k, v := range b.params {
|
||||
for _, vv := range v {
|
||||
buf.WriteString(url.QueryEscape(k))
|
||||
buf.WriteByte('=')
|
||||
buf.WriteString(url.QueryEscape(vv))
|
||||
buf.WriteByte('&')
|
||||
}
|
||||
}
|
||||
paramBody = buf.String()
|
||||
paramBody = paramBody[0 : len(paramBody)-1]
|
||||
}
|
||||
return paramBody
|
||||
}
|
||||
|
||||
// String returns the body string in response.
|
||||
@ -592,10 +573,10 @@ func (b *BeegoHTTPRequest) Bytes() ([]byte, error) {
|
||||
if b.setting.Gzip && resp.Header.Get("Content-Encoding") == "gzip" {
|
||||
reader, err := gzip.NewReader(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, berror.Wrap(err, ReadGzipBodyFailed, "building gzip reader failed")
|
||||
}
|
||||
b.body, err = ioutil.ReadAll(reader)
|
||||
return b.body, err
|
||||
return b.body, berror.Wrap(err, ReadGzipBodyFailed, "reading gzip data failed")
|
||||
}
|
||||
b.body, err = ioutil.ReadAll(resp.Body)
|
||||
return b.body, err
|
||||
@ -638,7 +619,7 @@ func pathExistAndMkdir(filename string) (err error) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
return berror.Wrapf(err, CreateFileIfNotExistFailed, "try to create(if not exist) failed: %s", filename)
|
||||
}
|
||||
|
||||
// ToJSON returns the map that marshals from the body bytes as json in response.
|
||||
@ -648,7 +629,8 @@ func (b *BeegoHTTPRequest) ToJSON(v interface{}) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(data, v)
|
||||
return berror.Wrap(json.Unmarshal(data, v),
|
||||
UnmarshalJSONResponseToObjectFailed, "unmarshal json body to object failed.")
|
||||
}
|
||||
|
||||
// ToXML returns the map that marshals from the body bytes as xml in response .
|
||||
@ -658,7 +640,8 @@ func (b *BeegoHTTPRequest) ToXML(v interface{}) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return xml.Unmarshal(data, v)
|
||||
return berror.Wrap(xml.Unmarshal(data, v),
|
||||
UnmarshalXMLResponseToObjectFailed, "unmarshal xml body to object failed.")
|
||||
}
|
||||
|
||||
// ToYAML returns the map that marshals from the body bytes as yaml in response .
|
||||
@ -668,7 +651,8 @@ func (b *BeegoHTTPRequest) ToYAML(v interface{}) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return yaml.Unmarshal(data, v)
|
||||
return berror.Wrap(yaml.Unmarshal(data, v),
|
||||
UnmarshalYAMLResponseToObjectFailed, "unmarshal yaml body to object failed.")
|
||||
}
|
||||
|
||||
// Response executes request client gets response manually.
|
||||
@ -677,8 +661,18 @@ func (b *BeegoHTTPRequest) Response() (*http.Response, error) {
|
||||
}
|
||||
|
||||
// TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
|
||||
// Deprecated
|
||||
// we will move this at the end of 2021
|
||||
// please use TimeoutDialerCtx
|
||||
func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
|
||||
return func(netw, addr string) (net.Conn, error) {
|
||||
return TimeoutDialerCtx(cTimeout, rwTimeout)(context.Background(), netw, addr)
|
||||
}
|
||||
}
|
||||
|
||||
func TimeoutDialerCtx(cTimeout time.Duration,
|
||||
rwTimeout time.Duration) func(ctx context.Context, net, addr string) (c net.Conn, err error) {
|
||||
return func(ctx context.Context, netw, addr string) (net.Conn, error) {
|
||||
conn, err := net.DialTimeout(netw, addr, cTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
package httplib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
@ -259,7 +260,7 @@ func TestToFile(t *testing.T) {
|
||||
}
|
||||
defer os.Remove(f)
|
||||
b, err := ioutil.ReadFile(f)
|
||||
if n := strings.Index(string(b), "origin"); n == -1 {
|
||||
if n := bytes.Index(b, []byte("origin")); n == -1 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@ -273,7 +274,7 @@ func TestToFileDir(t *testing.T) {
|
||||
}
|
||||
defer os.RemoveAll("./files")
|
||||
b, err := ioutil.ReadFile(f)
|
||||
if n := strings.Index(string(b), "origin"); n == -1 {
|
||||
if n := bytes.Index(b, []byte("origin")); n == -1 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@ -300,3 +301,135 @@ func TestAddFilter(t *testing.T) {
|
||||
r := Get("http://beego.me")
|
||||
assert.Equal(t, 1, len(req.setting.FilterChains)-len(r.setting.FilterChains))
|
||||
}
|
||||
|
||||
func TestFilterChainOrder(t *testing.T) {
|
||||
req := Get("http://beego.me")
|
||||
req.AddFilters(func(next Filter) Filter {
|
||||
return func(ctx context.Context, req *BeegoHTTPRequest) (*http.Response, error) {
|
||||
return NewHttpResponseWithJsonBody("first"), nil
|
||||
}
|
||||
})
|
||||
|
||||
req.AddFilters(func(next Filter) Filter {
|
||||
return func(ctx context.Context, req *BeegoHTTPRequest) (*http.Response, error) {
|
||||
return NewHttpResponseWithJsonBody("second"), nil
|
||||
}
|
||||
})
|
||||
|
||||
resp, err := req.DoRequestWithCtx(context.Background())
|
||||
assert.Nil(t, err)
|
||||
data := make([]byte, 5)
|
||||
_, _ = resp.Body.Read(data)
|
||||
assert.Equal(t, "first", string(data))
|
||||
}
|
||||
|
||||
func TestHead(t *testing.T) {
|
||||
req := Head("http://beego.me")
|
||||
assert.NotNil(t, req)
|
||||
assert.Equal(t, "HEAD", req.req.Method)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
req := Delete("http://beego.me")
|
||||
assert.NotNil(t, req)
|
||||
assert.Equal(t, "DELETE", req.req.Method)
|
||||
}
|
||||
|
||||
func TestPost(t *testing.T) {
|
||||
req := Post("http://beego.me")
|
||||
assert.NotNil(t, req)
|
||||
assert.Equal(t, "POST", req.req.Method)
|
||||
}
|
||||
|
||||
func TestNewBeegoRequest(t *testing.T) {
|
||||
req := NewBeegoRequest("http://beego.me", "GET")
|
||||
assert.NotNil(t, req)
|
||||
assert.Equal(t, "GET", req.req.Method)
|
||||
|
||||
// invalid case but still go request
|
||||
req = NewBeegoRequest("httpa\ta://beego.me", "GET")
|
||||
assert.NotNil(t, req)
|
||||
}
|
||||
|
||||
func TestBeegoHTTPRequest_SetProtocolVersion(t *testing.T) {
|
||||
req := NewBeegoRequest("http://beego.me", "GET")
|
||||
req.SetProtocolVersion("HTTP/3.10")
|
||||
assert.Equal(t, "HTTP/3.10", req.req.Proto)
|
||||
assert.Equal(t, 3, req.req.ProtoMajor)
|
||||
assert.Equal(t, 10, req.req.ProtoMinor)
|
||||
|
||||
req.SetProtocolVersion("")
|
||||
assert.Equal(t, "HTTP/1.1", req.req.Proto)
|
||||
assert.Equal(t, 1, req.req.ProtoMajor)
|
||||
assert.Equal(t, 1, req.req.ProtoMinor)
|
||||
|
||||
// invalid case
|
||||
req.SetProtocolVersion("HTTP/aaa1.1")
|
||||
assert.Equal(t, "HTTP/1.1", req.req.Proto)
|
||||
assert.Equal(t, 1, req.req.ProtoMajor)
|
||||
assert.Equal(t, 1, req.req.ProtoMinor)
|
||||
}
|
||||
|
||||
func TestPut(t *testing.T) {
|
||||
req := Put("http://beego.me")
|
||||
assert.NotNil(t, req)
|
||||
assert.Equal(t, "PUT", req.req.Method)
|
||||
}
|
||||
|
||||
func TestBeegoHTTPRequest_Header(t *testing.T) {
|
||||
req := Post("http://beego.me")
|
||||
key, value := "test-header", "test-header-value"
|
||||
req.Header(key, value)
|
||||
assert.Equal(t, value, req.req.Header.Get(key))
|
||||
}
|
||||
|
||||
func TestBeegoHTTPRequest_SetHost(t *testing.T) {
|
||||
req := Post("http://beego.me")
|
||||
host := "test-hose"
|
||||
req.SetHost(host)
|
||||
assert.Equal(t, host, req.req.Host)
|
||||
}
|
||||
|
||||
func TestBeegoHTTPRequest_Param(t *testing.T) {
|
||||
req := Post("http://beego.me")
|
||||
key, value := "test-param", "test-param-value"
|
||||
req.Param(key, value)
|
||||
assert.Equal(t, value, req.params[key][0])
|
||||
|
||||
value1 := "test-param-value-1"
|
||||
req.Param(key, value1)
|
||||
assert.Equal(t, value1, req.params[key][1])
|
||||
}
|
||||
|
||||
func TestBeegoHTTPRequest_Body(t *testing.T) {
|
||||
req := Post("http://beego.me")
|
||||
body := `hello, world`
|
||||
req.Body([]byte(body))
|
||||
assert.Equal(t, int64(len(body)), req.req.ContentLength)
|
||||
assert.NotNil(t, req.req.GetBody)
|
||||
assert.NotNil(t, req.req.Body)
|
||||
|
||||
body = "hhhh, i am test"
|
||||
req.Body(body)
|
||||
assert.Equal(t, int64(len(body)), req.req.ContentLength)
|
||||
assert.NotNil(t, req.req.GetBody)
|
||||
assert.NotNil(t, req.req.Body)
|
||||
|
||||
// invalid case
|
||||
req.Body(13)
|
||||
}
|
||||
|
||||
type user struct {
|
||||
Name string `xml:"name"`
|
||||
}
|
||||
|
||||
func TestBeegoHTTPRequest_XMLBody(t *testing.T) {
|
||||
req := Post("http://beego.me")
|
||||
body := &user{
|
||||
Name: "Tom",
|
||||
}
|
||||
_, err := req.XMLBody(body)
|
||||
assert.True(t, req.req.ContentLength > 0)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, req.req.GetBody)
|
||||
}
|
||||
|
||||
78
client/httplib/mock/mock.go
Normal file
78
client/httplib/mock/mock.go
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright 2020 beego
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/beego/beego/v2/client/httplib"
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
)
|
||||
|
||||
const mockCtxKey = "beego-httplib-mock"
|
||||
|
||||
func init() {
|
||||
InitMockSetting()
|
||||
}
|
||||
|
||||
type Stub interface {
|
||||
Mock(cond RequestCondition, resp *http.Response, err error)
|
||||
Clear()
|
||||
MockByPath(path string, resp *http.Response, err error)
|
||||
}
|
||||
|
||||
var mockFilter = &MockResponseFilter{}
|
||||
|
||||
func InitMockSetting() {
|
||||
httplib.AddDefaultFilter(mockFilter.FilterChain)
|
||||
}
|
||||
|
||||
func StartMock() Stub {
|
||||
return mockFilter
|
||||
}
|
||||
|
||||
func CtxWithMock(ctx context.Context, mock ...*Mock) context.Context {
|
||||
return context.WithValue(ctx, mockCtxKey, mock)
|
||||
}
|
||||
|
||||
func mockFromCtx(ctx context.Context) []*Mock {
|
||||
ms := ctx.Value(mockCtxKey)
|
||||
if ms != nil {
|
||||
if res, ok := ms.([]*Mock); ok {
|
||||
return res
|
||||
}
|
||||
logs.Error("mockCtxKey found in context, but value is not type []*Mock")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Mock struct {
|
||||
cond RequestCondition
|
||||
resp *http.Response
|
||||
err error
|
||||
}
|
||||
|
||||
func NewMockByPath(path string, resp *http.Response, err error) *Mock {
|
||||
return NewMock(NewSimpleCondition(path), resp, err)
|
||||
}
|
||||
|
||||
func NewMock(con RequestCondition, resp *http.Response, err error) *Mock {
|
||||
return &Mock{
|
||||
cond: con,
|
||||
resp: resp,
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
176
client/httplib/mock/mock_condition.go
Normal file
176
client/httplib/mock/mock_condition.go
Normal file
@ -0,0 +1,176 @@
|
||||
// Copyright 2020 beego
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/textproto"
|
||||
"regexp"
|
||||
|
||||
"github.com/beego/beego/v2/client/httplib"
|
||||
)
|
||||
|
||||
type RequestCondition interface {
|
||||
Match(ctx context.Context, req *httplib.BeegoHTTPRequest) bool
|
||||
}
|
||||
|
||||
// reqCondition create condition
|
||||
// - path: same path
|
||||
// - pathReg: request path match pathReg
|
||||
// - method: same method
|
||||
// - Query parameters (key, value)
|
||||
// - header (key, value)
|
||||
// - Body json format, contains specific (key, value).
|
||||
type SimpleCondition struct {
|
||||
pathReg string
|
||||
path string
|
||||
method string
|
||||
query map[string]string
|
||||
header map[string]string
|
||||
body map[string]interface{}
|
||||
}
|
||||
|
||||
func NewSimpleCondition(path string, opts ...simpleConditionOption) *SimpleCondition {
|
||||
sc := &SimpleCondition{
|
||||
path: path,
|
||||
query: make(map[string]string),
|
||||
header: make(map[string]string),
|
||||
body: map[string]interface{}{},
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(sc)
|
||||
}
|
||||
return sc
|
||||
}
|
||||
|
||||
func (sc *SimpleCondition) Match(ctx context.Context, req *httplib.BeegoHTTPRequest) bool {
|
||||
res := true
|
||||
if len(sc.path) > 0 {
|
||||
res = sc.matchPath(ctx, req)
|
||||
} else if len(sc.pathReg) > 0 {
|
||||
res = sc.matchPathReg(ctx, req)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return res &&
|
||||
sc.matchMethod(ctx, req) &&
|
||||
sc.matchQuery(ctx, req) &&
|
||||
sc.matchHeader(ctx, req) &&
|
||||
sc.matchBodyFields(ctx, req)
|
||||
}
|
||||
|
||||
func (sc *SimpleCondition) matchPath(ctx context.Context, req *httplib.BeegoHTTPRequest) bool {
|
||||
path := req.GetRequest().URL.Path
|
||||
return path == sc.path
|
||||
}
|
||||
|
||||
func (sc *SimpleCondition) matchPathReg(ctx context.Context, req *httplib.BeegoHTTPRequest) bool {
|
||||
path := req.GetRequest().URL.Path
|
||||
if b, err := regexp.Match(sc.pathReg, []byte(path)); err == nil {
|
||||
return b
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (sc *SimpleCondition) matchQuery(ctx context.Context, req *httplib.BeegoHTTPRequest) bool {
|
||||
qs := req.GetRequest().URL.Query()
|
||||
for k, v := range sc.query {
|
||||
if uv, ok := qs[k]; !ok || uv[0] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (sc *SimpleCondition) matchHeader(ctx context.Context, req *httplib.BeegoHTTPRequest) bool {
|
||||
headers := req.GetRequest().Header
|
||||
for k, v := range sc.header {
|
||||
if uv, ok := headers[k]; !ok || uv[0] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (sc *SimpleCondition) matchBodyFields(ctx context.Context, req *httplib.BeegoHTTPRequest) bool {
|
||||
if len(sc.body) == 0 {
|
||||
return true
|
||||
}
|
||||
getBody := req.GetRequest().GetBody
|
||||
body, err := getBody()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
bytes := make([]byte, req.GetRequest().ContentLength)
|
||||
_, err = body.Read(bytes)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
m := make(map[string]interface{})
|
||||
|
||||
err = json.Unmarshal(bytes, &m)
|
||||
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for k, v := range sc.body {
|
||||
if uv, ok := m[k]; !ok || uv != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (sc *SimpleCondition) matchMethod(ctx context.Context, req *httplib.BeegoHTTPRequest) bool {
|
||||
if len(sc.method) > 0 {
|
||||
return sc.method == req.GetRequest().Method
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type simpleConditionOption func(sc *SimpleCondition)
|
||||
|
||||
func WithPathReg(pathReg string) simpleConditionOption {
|
||||
return func(sc *SimpleCondition) {
|
||||
sc.pathReg = pathReg
|
||||
}
|
||||
}
|
||||
|
||||
func WithQuery(key, value string) simpleConditionOption {
|
||||
return func(sc *SimpleCondition) {
|
||||
sc.query[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
func WithHeader(key, value string) simpleConditionOption {
|
||||
return func(sc *SimpleCondition) {
|
||||
sc.header[textproto.CanonicalMIMEHeaderKey(key)] = value
|
||||
}
|
||||
}
|
||||
|
||||
func WithJsonBodyFields(field string, value interface{}) simpleConditionOption {
|
||||
return func(sc *SimpleCondition) {
|
||||
sc.body[field] = value
|
||||
}
|
||||
}
|
||||
|
||||
func WithMethod(method string) simpleConditionOption {
|
||||
return func(sc *SimpleCondition) {
|
||||
sc.method = method
|
||||
}
|
||||
}
|
||||
124
client/httplib/mock/mock_condition_test.go
Normal file
124
client/httplib/mock/mock_condition_test.go
Normal file
@ -0,0 +1,124 @@
|
||||
// Copyright 2020 beego
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/beego/beego/v2/client/httplib"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
}
|
||||
|
||||
func TestSimpleCondition_MatchPath(t *testing.T) {
|
||||
sc := NewSimpleCondition("/abc/s")
|
||||
res := sc.Match(context.Background(), httplib.Get("http://localhost:8080/abc/s"))
|
||||
assert.True(t, res)
|
||||
}
|
||||
|
||||
func TestSimpleCondition_MatchQuery(t *testing.T) {
|
||||
k, v := "my-key", "my-value"
|
||||
sc := NewSimpleCondition("/abc/s")
|
||||
res := sc.Match(context.Background(), httplib.Get("http://localhost:8080/abc/s?my-key=my-value"))
|
||||
assert.True(t, res)
|
||||
|
||||
sc = NewSimpleCondition("/abc/s", WithQuery(k, v))
|
||||
res = sc.Match(context.Background(), httplib.Get("http://localhost:8080/abc/s?my-key=my-value"))
|
||||
assert.True(t, res)
|
||||
|
||||
res = sc.Match(context.Background(), httplib.Get("http://localhost:8080/abc/s?my-key=my-valuesss"))
|
||||
assert.False(t, res)
|
||||
|
||||
res = sc.Match(context.Background(), httplib.Get("http://localhost:8080/abc/s?my-key-a=my-value"))
|
||||
assert.False(t, res)
|
||||
|
||||
res = sc.Match(context.Background(), httplib.Get("http://localhost:8080/abc/s?my-key=my-value&abc=hello"))
|
||||
assert.True(t, res)
|
||||
}
|
||||
|
||||
func TestSimpleCondition_MatchHeader(t *testing.T) {
|
||||
k, v := "my-header", "my-header-value"
|
||||
sc := NewSimpleCondition("/abc/s")
|
||||
req := httplib.Get("http://localhost:8080/abc/s")
|
||||
assert.True(t, sc.Match(context.Background(), req))
|
||||
|
||||
req = httplib.Get("http://localhost:8080/abc/s")
|
||||
req.Header(k, v)
|
||||
assert.True(t, sc.Match(context.Background(), req))
|
||||
|
||||
sc = NewSimpleCondition("/abc/s", WithHeader(k, v))
|
||||
req.Header(k, v)
|
||||
assert.True(t, sc.Match(context.Background(), req))
|
||||
|
||||
req.Header(k, "invalid")
|
||||
assert.False(t, sc.Match(context.Background(), req))
|
||||
}
|
||||
|
||||
func TestSimpleCondition_MatchBodyField(t *testing.T) {
|
||||
|
||||
sc := NewSimpleCondition("/abc/s")
|
||||
req := httplib.Post("http://localhost:8080/abc/s")
|
||||
|
||||
assert.True(t, sc.Match(context.Background(), req))
|
||||
|
||||
req.Body(`{
|
||||
"body-field": 123
|
||||
}`)
|
||||
assert.True(t, sc.Match(context.Background(), req))
|
||||
|
||||
k := "body-field"
|
||||
v := float64(123)
|
||||
sc = NewSimpleCondition("/abc/s", WithJsonBodyFields(k, v))
|
||||
assert.True(t, sc.Match(context.Background(), req))
|
||||
|
||||
sc = NewSimpleCondition("/abc/s", WithJsonBodyFields(k, v))
|
||||
req.Body(`{
|
||||
"body-field": abc
|
||||
}`)
|
||||
assert.False(t, sc.Match(context.Background(), req))
|
||||
|
||||
sc = NewSimpleCondition("/abc/s", WithJsonBodyFields("body-field", "abc"))
|
||||
req.Body(`{
|
||||
"body-field": "abc"
|
||||
}`)
|
||||
assert.True(t, sc.Match(context.Background(), req))
|
||||
}
|
||||
|
||||
func TestSimpleCondition_Match(t *testing.T) {
|
||||
sc := NewSimpleCondition("/abc/s")
|
||||
req := httplib.Post("http://localhost:8080/abc/s")
|
||||
|
||||
assert.True(t, sc.Match(context.Background(), req))
|
||||
|
||||
sc = NewSimpleCondition("/abc/s", WithMethod("POST"))
|
||||
assert.True(t, sc.Match(context.Background(), req))
|
||||
|
||||
sc = NewSimpleCondition("/abc/s", WithMethod("GET"))
|
||||
assert.False(t, sc.Match(context.Background(), req))
|
||||
}
|
||||
|
||||
func TestSimpleCondition_MatchPathReg(t *testing.T) {
|
||||
sc := NewSimpleCondition("", WithPathReg(`\/abc\/.*`))
|
||||
req := httplib.Post("http://localhost:8080/abc/s")
|
||||
assert.True(t, sc.Match(context.Background(), req))
|
||||
|
||||
req = httplib.Post("http://localhost:8080/abcd/s")
|
||||
assert.False(t, sc.Match(context.Background(), req))
|
||||
}
|
||||
61
client/httplib/mock/mock_filter.go
Normal file
61
client/httplib/mock/mock_filter.go
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright 2020 beego
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/beego/beego/v2/client/httplib"
|
||||
)
|
||||
|
||||
// MockResponse will return mock response if find any suitable mock data
|
||||
// if you want to test your code using httplib, you need this.
|
||||
type MockResponseFilter struct {
|
||||
ms []*Mock
|
||||
}
|
||||
|
||||
func NewMockResponseFilter() *MockResponseFilter {
|
||||
return &MockResponseFilter{
|
||||
ms: make([]*Mock, 0, 1),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockResponseFilter) FilterChain(next httplib.Filter) httplib.Filter {
|
||||
return func(ctx context.Context, req *httplib.BeegoHTTPRequest) (*http.Response, error) {
|
||||
ms := mockFromCtx(ctx)
|
||||
ms = append(ms, m.ms...)
|
||||
for _, mock := range ms {
|
||||
if mock.cond.Match(ctx, req) {
|
||||
return mock.resp, mock.err
|
||||
}
|
||||
}
|
||||
return next(ctx, req)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockResponseFilter) MockByPath(path string, resp *http.Response, err error) {
|
||||
m.Mock(NewSimpleCondition(path), resp, err)
|
||||
}
|
||||
|
||||
func (m *MockResponseFilter) Clear() {
|
||||
m.ms = make([]*Mock, 0, 1)
|
||||
}
|
||||
|
||||
// Mock add mock data
|
||||
// If the cond.Match(...) = true, the resp and err will be returned
|
||||
func (m *MockResponseFilter) Mock(cond RequestCondition, resp *http.Response, err error) {
|
||||
m.ms = append(m.ms, NewMock(cond, resp, err))
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user