Merge pull request #4768 from beego/develop

Develop to master
This commit is contained in:
Ming Deng 2021-09-15 11:04:21 +08:00 committed by GitHub
commit 46a0dbbd1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
336 changed files with 13219 additions and 4756 deletions

View File

@ -1,4 +1,4 @@
**English Only**. Please use English because other could join the discussion if they got similar issue!
**English Only**. Please use English because others could join the discussion if they got similar issue!
Please answer these questions before submitting your issue. Thanks!
@ -12,6 +12,7 @@ Please answer these questions before submitting your issue. Thanks!
If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
If this is ORM issue, please provide the DB schemas.
4. What did you expect to see?

17
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,17 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 10
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

43
.github/linters/.golangci.yml vendored Normal file
View File

@ -0,0 +1,43 @@
run:
timeout: 5m
skip-files:
- generated.*
issues:
new: true
linters:
enable:
- asciicheck
- bodyclose
- deadcode
- depguard
- gci
- gocritic
- gofmt
- gofumpt
- goimports
- goprintffuncname
- gosimple
- govet
- ineffassign
- misspell
- nilerr
- rowserrcheck
- staticcheck
- structcheck
- stylecheck
- typecheck
- unconvert
- unused
- unparam
- varcheck
- whitespace
disable:
- errcheck
linters-settings:
gci:
local-prefixes: github.com/beego/beego
goimports:
local-prefixes: github.com/beego/beego

34
.github/workflows/changelog.yml vendored Normal file
View 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

26
.github/workflows/golangci-lint.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name: golangci-lint
on:
push:
branches:
- master
paths:
- "**/*.go"
- ".github/workflows/golangci-lint.yml"
pull_request:
types: [opened, synchronize, reopened]
paths:
- "**/*.go"
- ".github/workflows/golangci-lint.yml"
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout codebase
uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: latest
args: --config=.github/linters/.golangci.yml
only-new-issues: true

19
.github/workflows/need-feedback.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: need-feeback-issues
on:
schedule:
- cron: "0 * * * *" # pick a cron here, this is every 1h
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: luanpotter/changes-requested@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
# these are optional, if you want to configure:
days-until-close: 5
trigger-label: status/need-feedback
closing-comment: This issue was closed by the need-feedback bot due to without feebacks.
dry-run: false

19
.github/workflows/need-translation.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: need-translation-issues
on:
schedule:
- cron: "0 * * * *" # pick a cron here, this is every 1h
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: luanpotter/changes-requested@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
# these are optional, if you want to configure:
days-until-close: 5
trigger-label: status/need-translation
closing-comment: This issue was closed by the need-translation bot. Please transalate your issue to English so it could help others.
dry-run: false

View File

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v1
- uses: actions/stale@v4
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue is inactive for a long time.'

124
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,124 @@
name: Test
on:
push:
branches:
- master
- develop
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/test.yml"
pull_request:
types: [opened, synchronize, reopened]
branches:
- master
- develop
paths:
- "**/*.go"
- "go.mod"
- "go.sum"
- ".github/workflows/test.yml"
jobs:
test:
strategy:
fail-fast: false
matrix:
go-version: [1.14, 1.15, 1.16]
runs-on: ubuntu-latest
services:
redis:
image: redis:latest
ports:
- 6379:6379
memcached:
image: memcached:latest
ports:
- 11211:11211
ssdb:
image: wendal/ssdb:latest
ports:
- 8888:8888
postgres:
image: postgres:latest
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: orm_test
ports:
- 5432/tcp
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout codebase
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Run etcd
env:
ETCD_VERSION: v3.4.16
run: |
rm -rf /tmp/etcd-data.tmp
mkdir -p /tmp/etcd-data.tmp
docker rmi gcr.io/etcd-development/etcd:${ETCD_VERSION} || true && \
docker run -d \
-p 2379:2379 \
-p 2380:2380 \
--mount type=bind,source=/tmp/etcd-data.tmp,destination=/etcd-data \
--name etcd-gcr-${ETCD_VERSION} \
gcr.io/etcd-development/etcd:${ETCD_VERSION} \
/usr/local/bin/etcd \
--name s1 \
--data-dir /etcd-data \
--listen-client-urls http://0.0.0.0:2379 \
--advertise-client-urls http://0.0.0.0:2379 \
--listen-peer-urls http://0.0.0.0:2380 \
--initial-advertise-peer-urls http://0.0.0.0:2380 \
--initial-cluster s1=http://0.0.0.0:2380 \
--initial-cluster-token tkn \
--initial-cluster-state new
docker exec etcd-gcr-${ETCD_VERSION} /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl put current.float 1.23"
docker exec etcd-gcr-${ETCD_VERSION} /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl put current.bool true"
docker exec etcd-gcr-${ETCD_VERSION} /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl put current.int 11"
docker exec etcd-gcr-${ETCD_VERSION} /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl put current.string hello"
docker exec etcd-gcr-${ETCD_VERSION} /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl put current.serialize.name test"
docker exec etcd-gcr-${ETCD_VERSION} /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl put sub.sub.key1 sub.sub.key"
- name: Run ORM tests on sqlite3
env:
GOPATH: /home/runner/go
ORM_DRIVER: sqlite3
ORM_SOURCE: /tmp/sqlite3/orm_test.db
run: |
mkdir -p /tmp/sqlite3 && touch /tmp/sqlite3/orm_test.db
go test -coverprofile=coverage_sqlite3.txt -covermode=atomic $(go list ./... | grep client/orm)
- name: Run ORM tests on postgres
env:
GOPATH: /home/runner/go
ORM_DRIVER: postgres
ORM_SOURCE: host=localhost port=${{ job.services.postgres.ports[5432] }} user=postgres password=postgres dbname=orm_test sslmode=disable
run: |
go test -coverprofile=coverage_postgres.txt -covermode=atomic $(go list ./... | grep client/orm)
- name: Run tests on mysql
env:
GOPATH: /home/runner/go
ORM_DRIVER: mysql
ORM_SOURCE: root:root@/orm_test?charset=utf8
run: |
sudo systemctl start mysql
mysql -u root -proot -e 'create database orm_test;'
go test -coverprofile=coverage.txt -covermode=atomic ./...
- name: Upload codecov
env:
CODECOV_TOKEN: 4f4bc484-32a8-43b7-9f48-20966bd48ceb
run: bash <(curl -s https://codecov.io/bash)

5
.gitignore vendored
View File

@ -10,3 +10,8 @@ _beeTmp2/
pkg/_beeTmp/
pkg/_beeTmp2/
test/tmp/
core/config/env/pkg/
my save path/
profile.out

View File

@ -1,105 +0,0 @@
language: go
go:
- "1.14.x"
services:
- redis-server
- mysql
- postgresql
- memcached
- docker
env:
global:
- GO_REPO_FULLNAME="github.com/beego/beego/v2"
matrix:
- ORM_DRIVER=sqlite3 ORM_SOURCE=$TRAVIS_BUILD_DIR/orm_test.db
- 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:
# link the local repo with ${GOPATH}/src/<namespace>/<repo>
- GO_REPO_NAMESPACE=${GO_REPO_FULLNAME%/*}
# relies on GOPATH to contain only one directory...
- mkdir -p ${GOPATH}/src/${GO_REPO_NAMESPACE}
- ln -sv ${TRAVIS_BUILD_DIR} ${GOPATH}/src/${GO_REPO_FULLNAME}
- cd ${GOPATH}/src/${GO_REPO_FULLNAME}
# get and build ssdb
- git clone git://github.com/ideawu/ssdb.git
- cd ssdb
- make
- cd ..
# - prepare etcd
# - prepare for etcd unit tests
- rm -rf /tmp/etcd-data.tmp
- mkdir -p /tmp/etcd-data.tmp
- docker rmi gcr.io/etcd-development/etcd:v3.3.25 || true &&
docker run -d
-p 2379:2379
-p 2380:2380
--mount type=bind,source=/tmp/etcd-data.tmp,destination=/etcd-data
--name etcd-gcr-v3.3.25
gcr.io/etcd-development/etcd:v3.3.25
/usr/local/bin/etcd
--name s1
--data-dir /etcd-data
--listen-client-urls http://0.0.0.0:2379
--advertise-client-urls http://0.0.0.0:2379
--listen-peer-urls http://0.0.0.0:2380
--initial-advertise-peer-urls http://0.0.0.0:2380
--initial-cluster s1=http://0.0.0.0:2380
--initial-cluster-token tkn
--initial-cluster-state new
- docker exec etcd-gcr-v3.3.25 /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl put current.float 1.23"
- docker exec etcd-gcr-v3.3.25 /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl put current.bool true"
- docker exec etcd-gcr-v3.3.25 /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl put current.int 11"
- docker exec etcd-gcr-v3.3.25 /bin/sh -c "ETCDCTL_API=3 /usr/local/bin/etcdctl put current.string hello"
- 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:
# -
- psql --version
# - prepare for orm unit tests
- 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/*
script:
- go test ./...
- 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"

View File

@ -1,4 +1,28 @@
# developing
- Add a custom option for whether to escape HTML special characters when processing http request parameters. [4701](https://github.com/beego/beego/pull/4701)
- Always set the response status in the CustomAbort function. [4686](https://github.com/beego/beego/pull/4686)
- Add template functions eq,lt to support uint and int compare. [4607](https://github.com/beego/beego/pull/4607)
- Migrate tests to GitHub Actions. [4663](https://github.com/beego/beego/issues/4663)
- Add http client and option func. [4455](https://github.com/beego/beego/issues/4455)
- Add: Convenient way to generate mock object [4620](https://github.com/beego/beego/issues/4620)
- Infra: use dependabot to update dependencies. [4623](https://github.com/beego/beego/pull/4623)
- Lint: use golangci-lint. [4619](https://github.com/beego/beego/pull/4619)
- Chore: format code. [4615](https://github.com/beego/beego/pull/4615)
- Test on Go v1.15.x & v1.16.x. [4614](https://github.com/beego/beego/pull/4614)
- Env: non-empty GOBIN & GOPATH. [4613](https://github.com/beego/beego/pull/4613)
- Chore: update dependencies. [4611](https://github.com/beego/beego/pull/4611)
- Update orm_test.go/TestInsertOrUpdate with table-driven. [4609](https://github.com/beego/beego/pull/4609)
- Add: Resp() method for web.Controller. [4588](https://github.com/beego/beego/pull/4588)
- Web mock and test support. [4565](https://github.com/beego/beego/pull/4565) [4574](https://github.com/beego/beego/pull/4574)
- 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)
@ -7,6 +31,65 @@
- Fix 4727: CSS when request URI is invalid. [4729](https://github.com/beego/beego/pull/4729)
- 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)
- Hotfix:reflect.ValueOf(nil) in getFlatParams [4716](https://github.com/beego/beego/issues/4716)
- Hotfix:reflect.ValueOf(nil) in getFlatParams [4716](https://github.com/beego/beego/issues/4716)
- 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)
- Integration: Remove unnecessary function call [4577](https://github.com/beego/beego/pull/4577)
- Feature issue #4402 finish router get example. [4416](https://github.com/beego/beego/pull/4416)
- Proposal: Add Bind() method for `web.Controller` [4491](https://github.com/beego/beego/issues/4579)
- Optimize AddAutoPrefix: only register one router in case-insensitive mode. [4582](https://github.com/beego/beego/pull/4582)
- Init exceptMethod by using reflection. [4583](https://github.com/beego/beego/pull/4583)
- Deprecated BeeMap and replace all usage with `sync.map` [4616](https://github.com/beego/beego/pull/4616)
- TaskManager support graceful shutdown [4635](https://github.com/beego/beego/pull/4635)
- Add comments to `web.Config`, rename `RouterXXX` to `CtrlXXX`, define `HandleFunc` [4714](https://github.com/beego/beego/pull/4714)
- Refactor: Move `BindXXX` and `XXXResp` methods to `context.Context`. [4718](https://github.com/beego/beego/pull/4718)
- Fix 4728: Print wrong file name. [4737](https://github.com/beego/beego/pull/4737)
- fix bug:reflect.ValueOf(nil) in getFlatParams [4715](https://github.com/beego/beego/pull/4715)
- Fix 4736: set a fixed value "/" to the "Path" of "_xsrf" cookie. [4736](https://github.com/beego/beego/issues/4735) [4739](https://github.com/beego/beego/issues/4739)
- Fix 4734: do not reset id in Delete function. [4738](https://github.com/beego/beego/pull/4738) [4742](https://github.com/beego/beego/pull/4742)
- Fix 4699: Remove Remove goyaml2 dependency. [4755](https://github.com/beego/beego/pull/4755)
- Fix 4698: Prompt error when config format is incorrect. [4757](https://github.com/beego/beego/pull/4757)
- Fix 4674: Tx Orm missing debug log [4756](https://github.com/beego/beego/pull/4756)
- Fix 4759: fix numeric notation of permissions [4759](https://github.com/beego/beego/pull/4759)
## Fix Sonar
- [4677](https://github.com/beego/beego/pull/4677)
- [4624](https://github.com/beego/beego/pull/4624)
- [4608](https://github.com/beego/beego/pull/4608)
- [4473](https://github.com/beego/beego/pull/4473)
- [4474](https://github.com/beego/beego/pull/4474)
- [4479](https://github.com/beego/beego/pull/4479)
- [4639](https://github.com/beego/beego/pull/4639)
- [4668](https://github.com/beego/beego/pull/4668)
## Fix lint and format code
- [4644](https://github.com/beego/beego/pull/4644)
- [4645](https://github.com/beego/beego/pull/4645)
- [4646](https://github.com/beego/beego/pull/4646)
- [4647](https://github.com/beego/beego/pull/4647)
- [4648](https://github.com/beego/beego/pull/4648)
- [4649](https://github.com/beego/beego/pull/4649)
- [4651](https://github.com/beego/beego/pull/4651)
- [4652](https://github.com/beego/beego/pull/4652)
- [4653](https://github.com/beego/beego/pull/4653)
- [4654](https://github.com/beego/beego/pull/4654)
- [4655](https://github.com/beego/beego/pull/4655)
- [4656](https://github.com/beego/beego/pull/4656)
- [4660](https://github.com/beego/beego/pull/4660)

View File

@ -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
View File

@ -0,0 +1,5 @@
# Error Module
## Module code
- httplib 1
- cache 2

View File

@ -1,14 +1,12 @@
# Beego [![Build Status](https://travis-ci.org/astaxie/beego.svg?branch=master)](https://travis-ci.org/astaxie/beego) [![GoDoc](http://godoc.org/github.com/beego/beego/v2?status.svg)](http://godoc.org/github.com/beego/beego/v2) [![Foundation](https://img.shields.io/badge/Golang-Foundation-green.svg)](http://golangfoundation.org) [![Go Report Card](https://goreportcard.com/badge/github.com/beego/beego/v2)](https://goreportcard.com/report/github.com/beego/beego/v2)
# Beego [![Test](https://github.com/beego/beego/actions/workflows/test.yml/badge.svg?branch=develop)](https://github.com/beego/beego/actions/workflows/test.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/beego/beego)](https://goreportcard.com/report/github.com/beego/beego) [![Go Reference](https://pkg.go.dev/badge/github.com/beego/beego/v2.svg)](https://pkg.go.dev/github.com/beego/beego/v2)
Beego is used for rapid development of enterprise application in Go, including RESTful APIs, web apps and backend
services.
Beego is used for rapid development of enterprise application in Go, including RESTful APIs, web apps and backend services.
It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct
embedding.
It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding.
![architecture](https://cdn.nlark.com/yuque/0/2020/png/755700/1607857489109-1e267fce-d65f-4c5e-b915-5c475df33c58.png)
Beego is compos of four parts:
Beego is composed of four parts:
1. Base modules: including log module, config module, governor module;
2. Task: is used for running timed tasks or periodic tasks;
@ -19,7 +17,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 +38,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,12 +88,11 @@ 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).
## License
beego source code is licensed under the Apache Licence, Version 2.0
(http://www.apache.org/licenses/LICENSE-2.0.html).
([https://www.apache.org/licenses/LICENSE-2.0.html](https://www.apache.org/licenses/LICENSE-2.0.html)).

View File

@ -22,10 +22,8 @@ import (
"github.com/beego/beego/v2/server/web/context"
)
var (
// BeeApp is an application instance
BeeApp *App
)
// BeeApp is an application instance
var BeeApp *App
func init() {
// create beego application
@ -139,7 +137,7 @@ func RESTRouter(rootpath string, c ControllerInterface) *App {
// AutoRouter adds defined controller handler to BeeApp.
// it's same to HttpServer.AutoRouter.
// if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page,
// if beego.AddAuto(&MainController{}) and MainController has methods List and Page,
// visit the url /main/list to exec List function or /main/page to exec Page function.
func AutoRouter(c ControllerInterface) *App {
return (*App)(web.AutoRouter(c))
@ -147,7 +145,7 @@ func AutoRouter(c ControllerInterface) *App {
// AutoPrefix adds controller handler to BeeApp with prefix.
// it's same to HttpServer.AutoRouterWithPrefix.
// if beego.AutoPrefix("/admin",&MainContorlller{}) and MainController has methods List and Page,
// if beego.AutoPrefix("/admin",&MainController{}) and MainController has methods List and Page,
// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function.
func AutoPrefix(prefix string, c ControllerInterface) *App {
return (*App)(web.AutoPrefix(prefix, c))

View File

@ -36,10 +36,6 @@ type M web.M
// Hook function to run
type hookfunc func() error
var (
hooks = make([]hookfunc, 0) // hook function slice to store the hookfunc
)
// AddAPPStartHook is used to register the hookfunc
// The hookfuncs will run in beego.Run()
// such as initiating session , starting middleware , building template, starting admin control and so on.

View File

@ -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}`)

View File

@ -19,12 +19,16 @@ import (
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
const initError = "init err"
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 +49,94 @@ 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"))
}

View File

@ -19,15 +19,15 @@ import (
)
func TestGetString(t *testing.T) {
var t1 = "test1"
t1 := "test1"
if "test1" != GetString(t1) {
t.Error("get string from string error")
}
var t2 = []byte("test2")
t2 := []byte("test2")
if "test2" != GetString(t2) {
t.Error("get string from byte array error")
}
var t3 = 1
t3 := 1
if "1" != GetString(t3) {
t.Error("get string from int error")
}
@ -35,7 +35,7 @@ func TestGetString(t *testing.T) {
if "1" != GetString(t4) {
t.Error("get string from int64 error")
}
var t5 = 1.1
t5 := 1.1
if "1.1" != GetString(t5) {
t.Error("get string from float64 error")
}
@ -46,7 +46,7 @@ func TestGetString(t *testing.T) {
}
func TestGetInt(t *testing.T) {
var t1 = 1
t1 := 1
if 1 != GetInt(t1) {
t.Error("get int from int error")
}
@ -58,7 +58,7 @@ func TestGetInt(t *testing.T) {
if 64 != GetInt(t3) {
t.Error("get int from int64 error")
}
var t4 = "128"
t4 := "128"
if 128 != GetInt(t4) {
t.Error("get int from num string error")
}
@ -69,7 +69,7 @@ func TestGetInt(t *testing.T) {
func TestGetInt64(t *testing.T) {
var i int64 = 1
var t1 = 1
t1 := 1
if i != GetInt64(t1) {
t.Error("get int64 from int error")
}
@ -81,7 +81,7 @@ func TestGetInt64(t *testing.T) {
if i != GetInt64(t3) {
t.Error("get int64 from int64 error")
}
var t4 = "1"
t4 := "1"
if i != GetInt64(t4) {
t.Error("get int64 from num string error")
}
@ -91,22 +91,22 @@ func TestGetInt64(t *testing.T) {
}
func TestGetFloat64(t *testing.T) {
var f = 1.11
f := 1.11
var t1 float32 = 1.11
if f != GetFloat64(t1) {
t.Error("get float64 from float32 error")
}
var t2 = 1.11
t2 := 1.11
if f != GetFloat64(t2) {
t.Error("get float64 from float64 error")
}
var t3 = "1.11"
t3 := "1.11"
if f != GetFloat64(t3) {
t.Error("get float64 from string error")
}
var f2 float64 = 1
var t4 = 1
t4 := 1
if f2 != GetFloat64(t4) {
t.Error("get float64 from int error")
}
@ -117,11 +117,11 @@ func TestGetFloat64(t *testing.T) {
}
func TestGetBool(t *testing.T) {
var t1 = true
t1 := true
if !GetBool(t1) {
t.Error("get bool from bool error")
}
var t2 = "true"
t2 := "true"
if !GetBool(t2) {
t.Error("get bool from string error")
}

View File

@ -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"}`)

View File

@ -21,94 +21,64 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/beego/beego/v2/adapter/cache"
)
func TestMemcacheCache(t *testing.T) {
addr := os.Getenv("MEMCACHE_ADDR")
if addr == "" {
addr = "127.0.0.1:11211"
}
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())
}

View File

@ -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"}`)
@ -34,10 +34,8 @@ import (
redis2 "github.com/beego/beego/v2/client/cache/redis"
)
var (
// DefaultKey the collection name of redis for cache adapter.
DefaultKey = "beecacheRedis"
)
// DefaultKey the collection name of redis for cache adapter.
var DefaultKey = "beecacheRedis"
// NewRedisCache create new redis cache with default collection name.
func NewRedisCache() cache.Cache {

View File

@ -21,10 +21,16 @@ 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"
)
func TestRedisCache(t *testing.T) {
redisAddr := os.Getenv("REDIS_ADDR")
if redisAddr == "" {
@ -32,98 +38,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)
}
}
@ -131,5 +118,4 @@ func TestCache_Scan(t *testing.T) {
if err = bm.ClearAll(); err != nil {
t.Error("clear all err")
}
}

View File

@ -7,6 +7,8 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/beego/beego/v2/adapter/cache"
)
@ -17,95 +19,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")
}
}

View File

@ -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")

View File

@ -20,7 +20,6 @@ import (
)
func TestExpandValueEnv(t *testing.T) {
testCases := []struct {
item string
want string
@ -51,5 +50,4 @@ func TestExpandValueEnv(t *testing.T) {
t.Errorf("expand value error, item %q want %q, got %q", c.item, c.want, got)
}
}
}

View File

@ -20,6 +20,6 @@ import (
// NewFakeConfig return a fake Configer
func NewFakeConfig() Configer {
new := config.NewFakeConfig()
return &newToOldConfigerAdapter{delegate: new}
config := config.NewFakeConfig()
return &newToOldConfigerAdapter{delegate: config}
}

View File

@ -23,7 +23,6 @@ import (
)
func TestIni(t *testing.T) {
var (
inicontext = `
;comment one
@ -81,7 +80,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 +91,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)
}
@ -128,11 +128,9 @@ password = ${GOPATH}
if iniconf.String("name") != "astaxie" {
t.Fatal("get name error")
}
}
func TestIniSave(t *testing.T) {
const (
inicontext = `
app = app

View File

@ -18,10 +18,11 @@ import (
"fmt"
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestJsonStartsWithArray(t *testing.T) {
const jsoncontextwitharray = `[
{
"url": "user",
@ -32,7 +33,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 +44,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)
}
@ -68,7 +70,6 @@ func TestJsonStartsWithArray(t *testing.T) {
}
func TestJson(t *testing.T) {
var (
jsoncontext = `{
"appname": "beeapi",
@ -132,7 +133,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 +144,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 +169,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))
}

View File

@ -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")

View File

@ -23,7 +23,6 @@ import (
)
func TestXML(t *testing.T) {
var (
// xml parse should incluce in <config></config> tags
xmlcontext = `<?xml version="1.0" encoding="UTF-8"?>
@ -58,7 +57,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 +68,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)
}

View File

@ -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")

View File

@ -23,7 +23,6 @@ import (
)
func TestYaml(t *testing.T) {
var (
yamlcontext = `
"appname": beeapi
@ -54,7 +53,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 +64,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)
}
@ -111,5 +111,4 @@ func TestYaml(t *testing.T) {
if yamlconf.String("name") != "astaxie" {
t.Fatal("get name error")
}
}

View File

@ -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}
//

View 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))
}

View 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 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())
}

View 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()
}

View 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))
}

View 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))
}
}

View File

@ -19,9 +19,8 @@ import (
"net/url"
"github.com/beego/beego/v2/adapter/session"
webContext "github.com/beego/beego/v2/server/web/context"
"github.com/beego/beego/v2/server/web"
webContext "github.com/beego/beego/v2/server/web/context"
)
var (
@ -49,9 +48,11 @@ type ControllerCommentsSlice web.ControllerCommentsSlice
func (p ControllerCommentsSlice) Len() int {
return (web.ControllerCommentsSlice)(p).Len()
}
func (p ControllerCommentsSlice) Less(i, j int) bool {
return (web.ControllerCommentsSlice)(p).Less(i, j)
}
func (p ControllerCommentsSlice) Swap(i, j int) {
(web.ControllerCommentsSlice)(p).Swap(i, j)
}

View File

@ -18,9 +18,8 @@ import (
"net/http"
"github.com/beego/beego/v2/adapter/context"
beecontext "github.com/beego/beego/v2/server/web/context"
"github.com/beego/beego/v2/server/web"
beecontext "github.com/beego/beego/v2/server/web/context"
)
const (

View File

@ -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) {

View File

@ -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)

View File

@ -15,6 +15,7 @@
package httplib
import (
"bytes"
"errors"
"io/ioutil"
"net"
@ -25,8 +26,13 @@ import (
"time"
)
const (
getURL = "http://httpbin.org/get"
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)
@ -59,11 +65,10 @@ func TestDoRequest(t *testing.T) {
if elapsedTime < delayedTime {
t.Errorf("Not enough retries. Took %dms. Delay was meant to take %dms", elapsedTime, delayedTime)
}
}
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,7 @@ 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)
@ -244,33 +249,32 @@ func TestToJson(t *testing.T) {
t.Fatal("response is not valid ip")
}
}
}
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)
}
}

View File

@ -18,108 +18,106 @@ import (
"strings"
"github.com/beego/beego/v2/core/logs"
webLog "github.com/beego/beego/v2/core/logs"
)
// 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
LevelCritical = webLog.LevelCritical
LevelError = webLog.LevelError
LevelWarning = webLog.LevelWarning
LevelNotice = webLog.LevelNotice
LevelInformational = webLog.LevelInformational
LevelDebug = webLog.LevelDebug
LevelEmergency = logs.LevelEmergency
LevelAlert = logs.LevelAlert
LevelCritical = logs.LevelCritical
LevelError = logs.LevelError
LevelWarning = logs.LevelWarning
LevelNotice = logs.LevelNotice
LevelInformational = logs.LevelInformational
LevelDebug = logs.LevelDebug
)
// 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...)
}

View File

@ -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", "")

View File

@ -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"}`)
}

View File

@ -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{

View File

@ -18,9 +18,8 @@ import (
"net/http"
adtContext "github.com/beego/beego/v2/adapter/context"
"github.com/beego/beego/v2/server/web/context"
"github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/server/web/context"
)
type namespaceCond func(*adtContext.Context) bool
@ -240,141 +239,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))
}
}

View File

@ -18,7 +18,5 @@ import (
"github.com/beego/beego/v2/client/orm"
)
var (
// ErrMissPK missing pk error
ErrMissPK = orm.ErrMissPK
)
// ErrMissPK missing pk error
var ErrMissPK = orm.ErrMissPK

View File

@ -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

View File

@ -1,4 +1,4 @@
// Copyright 2020
// 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.
@ -15,20 +15,17 @@
package orm
import (
"github.com/beego/beego/v2/client/orm"
"testing"
)
type baseQuerySetter struct {
type User struct {
Id int
}
func (b *baseQuerySetter) ForceIndex(indexes ...string) orm.QuerySeter {
panic("you should not invoke this method.")
type Seller struct {
Id int
}
func (b *baseQuerySetter) UseIndex(indexes ...string) orm.QuerySeter {
panic("you should not invoke this method.")
}
func (b *baseQuerySetter) IgnoreIndex(indexes ...string) orm.QuerySeter {
panic("you should not invoke this method.")
func TestRegisterModelWithPrefix(t *testing.T) {
RegisterModelWithPrefix("test", &User{}, &Seller{})
}

View File

@ -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
// )
//

View File

@ -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

View File

@ -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)
}
}

View File

@ -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(){

View File

@ -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(){

View File

@ -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"
// )
//

View File

@ -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)
}

View File

@ -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() {
@ -37,10 +37,9 @@ package cors
import (
beego "github.com/beego/beego/v2/adapter"
"github.com/beego/beego/v2/adapter/context"
beecontext "github.com/beego/beego/v2/server/web/context"
"github.com/beego/beego/v2/server/web/filter/cors"
"github.com/beego/beego/v2/adapter/context"
)
// Options represents Access Control options.

View File

@ -19,9 +19,8 @@ import (
"time"
beecontext "github.com/beego/beego/v2/adapter/context"
"github.com/beego/beego/v2/server/web/context"
"github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/server/web/context"
)
// default filter execution points
@ -87,7 +86,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
@ -217,7 +216,7 @@ func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...
}
// AddAuto router to ControllerRegister.
// example beego.AddAuto(&MainContorlller{}),
// example beego.AddAuto(&MainController{}),
// MainController has method List and Page.
// visit the url /main/list to execute List function
// /main/page to execute Page function.
@ -226,7 +225,7 @@ func (p *ControllerRegister) AddAuto(c ControllerInterface) {
}
// AddAutoPrefix Add auto router to ControllerRegister with prefix.
// example beego.AddAutoPrefix("/admin",&MainContorlller{}),
// example beego.AddAutoPrefix("/admin",&MainController{}),
// MainController has method List and Page.
// visit the url /admin/main/list to execute List function
// /admin/main/page to execute Page function.

View File

@ -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() {

View File

@ -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() {
@ -37,7 +37,6 @@ import (
"net/http"
"github.com/beego/beego/v2/adapter/session"
beemem "github.com/beego/beego/v2/server/web/session/memcache"
)

View File

@ -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() {
@ -44,11 +44,11 @@ import (
"context"
"net/http"
"github.com/beego/beego/v2/adapter/session"
"github.com/beego/beego/v2/server/web/session/mysql"
// import mysql driver
_ "github.com/go-sql-driver/mysql"
"github.com/beego/beego/v2/adapter/session"
"github.com/beego/beego/v2/server/web/session/mysql"
)
var (

View File

@ -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() {
@ -54,10 +54,10 @@ import (
"context"
"net/http"
"github.com/beego/beego/v2/adapter/session"
// import postgresql Driver
_ "github.com/lib/pq"
"github.com/beego/beego/v2/adapter/session"
"github.com/beego/beego/v2/server/web/session/postgres"
)

View File

@ -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() {
@ -37,7 +37,6 @@ import (
"net/http"
"github.com/beego/beego/v2/adapter/session"
beeRedis "github.com/beego/beego/v2/server/web/session/redis"
)

View File

@ -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() {

View File

@ -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() {
@ -37,7 +37,6 @@ import (
"net/http"
"github.com/beego/beego/v2/adapter/session"
sentinel "github.com/beego/beego/v2/server/web/session/redis_sentinel"
)

View File

@ -5,6 +5,8 @@ import (
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/beego/beego/v2/adapter/session"
)
@ -19,72 +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)
}

View File

@ -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)

View File

@ -22,15 +22,15 @@ import (
"time"
)
const sid = "Session_id"
const sidNew = "Session_id_new"
const sessionPath = "./_session_runtime"
var (
mutex sync.Mutex
const (
sid = "Session_id"
sidNew = "Session_id_new"
sessionPath = "./_session_runtime"
)
func TestFileProvider_SessionExist(t *testing.T) {
var mutex sync.Mutex
func TestFileProviderSessionExist(t *testing.T) {
mutex.Lock()
defer mutex.Unlock()
os.RemoveAll(sessionPath)
@ -53,7 +53,7 @@ func TestFileProvider_SessionExist(t *testing.T) {
}
}
func TestFileProvider_SessionExist2(t *testing.T) {
func TestFileProviderSessionExist2(t *testing.T) {
mutex.Lock()
defer mutex.Unlock()
os.RemoveAll(sessionPath)
@ -75,7 +75,7 @@ func TestFileProvider_SessionExist2(t *testing.T) {
}
}
func TestFileProvider_SessionRead(t *testing.T) {
func TestFileProviderSessionRead(t *testing.T) {
mutex.Lock()
defer mutex.Unlock()
os.RemoveAll(sessionPath)
@ -97,7 +97,7 @@ func TestFileProvider_SessionRead(t *testing.T) {
}
}
func TestFileProvider_SessionRead1(t *testing.T) {
func TestFileProviderSessionRead1(t *testing.T) {
mutex.Lock()
defer mutex.Unlock()
os.RemoveAll(sessionPath)
@ -117,7 +117,7 @@ func TestFileProvider_SessionRead1(t *testing.T) {
}
}
func TestFileProvider_SessionAll(t *testing.T) {
func TestFileProviderSessionAll(t *testing.T) {
mutex.Lock()
defer mutex.Unlock()
os.RemoveAll(sessionPath)
@ -140,7 +140,7 @@ func TestFileProvider_SessionAll(t *testing.T) {
}
}
func TestFileProvider_SessionRegenerate(t *testing.T) {
func TestFileProviderSessionRegenerate(t *testing.T) {
mutex.Lock()
defer mutex.Unlock()
os.RemoveAll(sessionPath)
@ -172,7 +172,7 @@ func TestFileProvider_SessionRegenerate(t *testing.T) {
}
}
func TestFileProvider_SessionDestroy(t *testing.T) {
func TestFileProviderSessionDestroy(t *testing.T) {
mutex.Lock()
defer mutex.Unlock()
os.RemoveAll(sessionPath)
@ -200,7 +200,7 @@ func TestFileProvider_SessionDestroy(t *testing.T) {
}
}
func TestFileProvider_SessionGC(t *testing.T) {
func TestFileProviderSessionGC(t *testing.T) {
mutex.Lock()
defer mutex.Unlock()
os.RemoveAll(sessionPath)
@ -226,7 +226,7 @@ func TestFileProvider_SessionGC(t *testing.T) {
}
}
func TestFileSessionStore_Set(t *testing.T) {
func TestFileSessionStoreSet(t *testing.T) {
mutex.Lock()
defer mutex.Unlock()
os.RemoveAll(sessionPath)
@ -245,7 +245,7 @@ func TestFileSessionStore_Set(t *testing.T) {
}
}
func TestFileSessionStore_Get(t *testing.T) {
func TestFileSessionStoreGet(t *testing.T) {
mutex.Lock()
defer mutex.Unlock()
os.RemoveAll(sessionPath)
@ -266,7 +266,7 @@ func TestFileSessionStore_Get(t *testing.T) {
}
}
func TestFileSessionStore_Delete(t *testing.T) {
func TestFileSessionStoreDelete(t *testing.T) {
mutex.Lock()
defer mutex.Unlock()
os.RemoveAll(sessionPath)
@ -289,7 +289,7 @@ func TestFileSessionStore_Delete(t *testing.T) {
}
}
func TestFileSessionStore_Flush(t *testing.T) {
func TestFileSessionStoreFlush(t *testing.T) {
mutex.Lock()
defer mutex.Unlock()
os.RemoveAll(sessionPath)
@ -313,7 +313,7 @@ func TestFileSessionStore_Flush(t *testing.T) {
}
}
func TestFileSessionStore_SessionID(t *testing.T) {
func TestFileSessionStoreSessionID(t *testing.T) {
mutex.Lock()
defer mutex.Unlock()
os.RemoveAll(sessionPath)

View File

@ -18,7 +18,7 @@ import (
"testing"
)
func Test_gob(t *testing.T) {
func TestGob(t *testing.T) {
a := make(map[interface{}]interface{})
a["username"] = "astaxie"
a[12] = 234

View File

@ -16,7 +16,7 @@
//
// Usage:
// import(
// "github.com/beego/beego/v2/session"
// "github.com/beego/beego/v2/server/web/session"
// )
//
// func init() {

View File

@ -5,7 +5,6 @@ import (
"net/http"
"github.com/beego/beego/v2/adapter/session"
beeSsdb "github.com/beego/beego/v2/server/web/session/ssdb"
)

View File

@ -118,7 +118,6 @@ func AssetsJs(text string) template.HTML {
// AssetsCSS returns stylesheet link tag with src string.
func AssetsCSS(text string) template.HTML {
text = "<link href=\"" + text + "\" rel=\"stylesheet\" />"
return template.HTML(text)

View File

@ -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,50 @@ 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 := `&lt;&#39;&nbsp;&rdquo;&ldquo;&amp;&#34;&gt;`
s := `<' ”“&">`
if Htmlquote(s) != h {
t.Error("should be equal")
}
assert.Equal(t, h, Htmlquote(s))
}
func TestHtmlunquote(t *testing.T) {
h := `&lt;&#39;&nbsp;&rdquo;&ldquo;&amp;&#34;&gt;`
s := `<' ”“&">`
if Htmlunquote(h) != s {
t.Error("should be equal")
}
assert.Equal(t, s, Htmlunquote(h))
}
func TestParseForm(t *testing.T) {
@ -148,55 +121,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 +172,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 +189,17 @@ 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 +208,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 +225,12 @@ 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)
}

View File

@ -14,12 +14,7 @@
package testing
import (
"github.com/beego/beego/v2/client/httplib/testing"
)
var port = ""
var baseURL = "http://localhost:"
import "github.com/beego/beego/v2/client/httplib/testing"
// TestHTTPRequest beego test request client
type TestHTTPRequest testing.TestHTTPRequest

View File

@ -16,19 +16,10 @@ package toolbox
import (
"io"
"os"
"time"
"github.com/beego/beego/v2/core/admin"
)
var startTime = time.Now()
var pid int
func init() {
pid = os.Getpid()
}
// ProcessInput parse input command string
func ProcessInput(input string, w io.Writer) {
admin.ProcessInput(input, w)

View File

@ -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()

View File

@ -80,7 +80,6 @@ type Task struct {
// NewTask add new task with name, time and func
func NewTask(tname string, spec string, f TaskFunc) *Task {
task := task.NewTask(tname, spec, func(ctx context.Context) error {
return f()
})
@ -98,7 +97,6 @@ func (t *Task) GetSpec() string {
// GetStatus get current task status
func (t *Task) GetStatus() string {
t.initDelegate()
return t.delegate.GetStatus(context.Background())
@ -222,7 +220,6 @@ type MapSorter task.MapSorter
// NewMapSorter create new tasker map
func NewMapSorter(m map[string]Tasker) *MapSorter {
newTaskerMap := make(map[string]task.Tasker, len(m))
for key, value := range m {
@ -249,6 +246,7 @@ func (ms *MapSorter) Less(i, j int) bool {
}
return ms.Vals[i].GetNext(context.Background()).Before(ms.Vals[j].GetNext(context.Background()))
}
func (ms *MapSorter) Swap(i, j int) {
ms.Vals[i], ms.Vals[j] = ms.Vals[j], ms.Vals[i]
ms.Keys[i], ms.Keys[j] = ms.Keys[j], ms.Keys[i]
@ -289,3 +287,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
}

View File

@ -16,9 +16,8 @@ package adapter
import (
"github.com/beego/beego/v2/adapter/context"
beecontext "github.com/beego/beego/v2/server/web/context"
"github.com/beego/beego/v2/server/web"
beecontext "github.com/beego/beego/v2/server/web/context"
)
// Tree has three elements: FixRouter/wildcard/leaves

View File

@ -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

View File

@ -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
@ -63,16 +63,13 @@ import (
"net/http"
"time"
"github.com/beego/beego/v2/server/web/captcha"
beecontext "github.com/beego/beego/v2/server/web/context"
"github.com/beego/beego/v2/adapter/cache"
"github.com/beego/beego/v2/adapter/context"
"github.com/beego/beego/v2/server/web/captcha"
beecontext "github.com/beego/beego/v2/server/web/context"
)
var (
defaultChars = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
)
var defaultChars = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
const (
// default captcha attributes

View File

@ -28,8 +28,8 @@ func TestPrint(t *testing.T) {
}
func TestPrintPoint(t *testing.T) {
var v1 = new(mytype)
var v2 = new(mytype)
v1 := new(mytype)
v2 := new(mytype)
v1.prev = nil
v1.next = v2

View File

@ -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

View File

@ -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)

View File

@ -19,6 +19,7 @@ import (
)
type reducetype func(interface{}) interface{}
type filtertype func(interface{}) bool
// InSlice checks given string in string slice or not.

View File

@ -27,9 +27,7 @@ const (
LabelTag = validation.LabelTag
)
var (
ErrInt64On32 = validation.ErrInt64On32
)
var ErrInt64On32 = validation.ErrInt64On32
// CustomFunc is for custom validate function
type CustomFunc func(v *Validation, obj interface{}, key string)

View File

@ -15,7 +15,7 @@
// Package validation for validations
//
// import (
// "github.com/beego/beego/v2/validation"
// "github.com/beego/beego/v2/core/validation"
// "log"
// )
//

View File

@ -18,131 +18,82 @@ 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 +129,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 +171,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 +196,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 +324,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 +367,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 +405,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 +429,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 +440,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 +450,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 +461,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 +471,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 +492,13 @@ 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)
}

View File

@ -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
View File

@ -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()

View File

@ -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,95 @@ 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 +171,51 @@ 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")
}
v, _ := c.Get(ctx, key)
assert.Equal(t, afterIncr, v)
if err = c.Decr(ctx, key); err != nil {
t.Error("Decr Error", err)
}
assert.Nil(t, c.Decr(ctx, key))
if v, _ := c.Get(ctx, key); v != beforeIncr {
t.Error("Get Error")
}
v, _ = c.Get(ctx, key)
assert.Equal(t, v, beforeIncr)
assert.Nil(t, c.Delete(ctx, key))
}
if err := c.Delete(ctx, key); err != nil {
t.Error("Delete Error")
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
}
}

95
client/cache/calc_utils.go vendored Normal file
View File

@ -0,0 +1,95 @@
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")
)
const (
MinUint32 uint32 = 0
MinUint64 uint64 = 0
)
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 == MinUint32 {
return nil, ErrDecrementOverflow
}
return val - 1, nil
case uint64:
if val == MinUint64 {
return nil, ErrDecrementOverflow
}
return val - 1, nil
default:
return nil, ErrNotIntegerType
}
}

140
client/cache/calc_utils_test.go vendored Normal file
View 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)
}

View File

@ -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")
}
t1 := "test1"
if "" != GetString(nil) {
t.Error("get string from nil error")
}
assert.Equal(t, "test1", GetString(t1))
t2 := []byte("test2")
assert.Equal(t, "test2", GetString(t2))
t3 := 1
assert.Equal(t, "1", GetString(t3))
var t4 int64 = 1
assert.Equal(t, "1", GetString(t4))
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")
}
t1 := 1
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")
}
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, 64, GetInt(t3))
t4 := "128"
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")
}
t1 := 1
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")
}
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(t3))
t4 := "1"
assert.Equal(t, i, GetInt64(t4))
assert.Equal(t, int64(0), GetInt64(nil))
}
func TestGetFloat64(t *testing.T) {
var f = 1.11
f := 1.11
var t1 float32 = 1.11
if f != GetFloat64(t1) {
t.Error("get float64 from float32 error")
}
var t2 = 1.11
if f != GetFloat64(t2) {
t.Error("get float64 from float64 error")
}
var t3 = "1.11"
if f != GetFloat64(t3) {
t.Error("get float64 from string error")
}
assert.Equal(t, f, GetFloat64(t1))
t2 := 1.11
assert.Equal(t, f, GetFloat64(t2))
t3 := "1.11"
assert.Equal(t, f, GetFloat64(t3))
var f2 float64 = 1
var t4 = 1
if f2 != GetFloat64(t4) {
t.Error("get float64 from int error")
}
t4 := 1
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")
}
var t2 = "true"
if !GetBool(t2) {
t.Error("get bool from string error")
}
if GetBool(nil) {
t.Error("get bool from nil error")
}
}
t1 := true
assert.True(t, GetBool(t1))
t2 := "true"
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))
}

176
client/cache/error_code.go vendored Normal file
View File

@ -0,0 +1,176 @@
// 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")
ErrKeyNotExist = berror.Error(KeyNotExist, "the key isn't exist")
)

198
client/cache/file.go vendored
View File

@ -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
@ -67,44 +67,65 @@ func NewFileCache() Cache {
// StartAndGC starts gc for file cache.
// config must be in the format {CachePath:"/cache","FileSuffix":".bin","DirectoryLevel":"2","EmbedExpiry":"0"}
func (fc *FileCache) StartAndGC(config string) error {
cfg := make(map[string]string)
err := json.Unmarshal([]byte(config), &cfg)
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 +134,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 +168,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 +191,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 +211,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 +243,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 +258,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 +289,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 +316,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
View 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")
}

Some files were not shown because too many files have changed in this diff Show More