Compare commits

...

136 Commits

Author SHA1 Message Date
Kevin Wan
57b73d8b49 make sure offset less than size even it's checked inside (#354) 2021-01-05 16:06:36 +08:00
Kevin Wan
a79cee12ee add godoc for RollingWindow (#351) 2021-01-04 22:43:55 +08:00
zjbztianya
7a921f66e6 simple rolling windows code (#346) 2021-01-04 22:11:18 +08:00
kingxt
12e235efb0 optimized goctl format (#336)
* fix format

* refactor

* refactor

* optimized

* refactor

* refactor

* refactor

* add js path prefix
2021-01-04 18:59:48 +08:00
Kevin Wan
01060cf16d close issue of #337 (#347) 2021-01-04 16:36:27 +08:00
Kevin Wan
0786862a35 align bucket boundary to interval in rolling window (#345) 2021-01-04 11:17:59 +08:00
Kevin Wan
efa43483b2 fix potential data race in PeriodicalExecutor (#344)
* fix potential data race in PeriodicalExecutor

* add comment
2021-01-03 20:56:17 +08:00
Kevin Wan
771371e051 simplify rolling window code, and make tests run faster (#343) 2021-01-03 20:47:29 +08:00
zjbztianya
2ee95f8981 fix rolling window bug (#340) 2021-01-03 20:27:47 +08:00
Kevin Wan
5bc01e4bfd set guarded to false only on quitting background flush (#342)
* set guarded to false only on quitting background flush

* set guarded to false only on quitting background flush, cont.
2021-01-03 19:54:11 +08:00
Kevin Wan
510e966982 simplify periodical executor background routine (#339) 2021-01-03 14:02:51 +08:00
Kevin Wan
10e3b8ac80 optimize code that fixes issue #317 (#338) 2021-01-02 19:01:37 +08:00
Kevin Wan
04059bbf5a add discord chat group in readme 2021-01-02 18:35:33 +08:00
weibobo
d643007c79 fix bug #317 (#335)
* fix bug #317.
* add counter for current task. If it's bigger then zero, do not quit background thread

* Revert "fix issue #317 (#331)"

This reverts commit fc43876cc5.
2021-01-02 18:04:04 +08:00
Kevin Wan
fc43876cc5 fix issue #317 (#331) 2021-01-01 13:24:28 +08:00
FengZhang
a926cb514f modify the goctl gensvc template (#323) 2020-12-30 10:05:26 +08:00
kingxt
25cab2f273 Java (#327)
* add g4 file

* new define api by g4

* reactor parser to g4gen

* add syntax parser & test

* add syntax parser & test

* add syntax parser & test

* update g4 file

* add import parse & test

* ractor AT lexer

* panic with error

* revert AT

* update g4 file

* update g4 file

* update g4 file

* optimize parser

* update g4 file

* parse info

* optimized java generator

* revert

* optimize java generator

* update java generator

* update java generator

* update java generator

* update java generator

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2020-12-29 17:50:41 +08:00
Kevin Wan
8d2e2753a2 simplify http.Flusher implementation (#326)
* simplify code with http.Flusher type conversion

* simplify code with http.Flusher type conversion, better version
2020-12-29 15:02:36 +08:00
Kevin Wan
cc4c50e3eb fix broken link. 2020-12-29 11:54:32 +08:00
Kevin Wan
751072bdb0 fix broken doc link 2020-12-29 11:52:55 +08:00
Kevin Wan
e97e1f10db simplify code with http.Flusher type conversion (#325)
* simplify code with http.Flusher type conversion

* simplify code with http.Flusher type conversion, better version
2020-12-29 10:25:55 +08:00
jichangyun
0bd2a0656c The ResponseWriters defined in rest.handler add Flush interface. (#318) 2020-12-28 21:30:24 +08:00
Kevin Wan
71a2b20301 add more tests for prof (#322) 2020-12-27 14:45:14 +08:00
Kevin Wan
8df7de94e3 add more tests for zrpc (#321) 2020-12-27 14:08:24 +08:00
Kevin Wan
bf21203297 add more tests (#320) 2020-12-27 12:26:31 +08:00
Kevin Wan
ae98375194 add more tests (#319) 2020-12-26 20:30:02 +08:00
Kevin Wan
82d1ccf376 fixes #286 (#315) 2020-12-25 19:47:27 +08:00
Kevin Wan
bb6d49c17e add go report card back (#313)
* add go report card back

* avoid test failure, run tests sequentially
2020-12-25 12:09:59 +08:00
Kevin Wan
ed735ec47c Update codeql-analysis.yml
disable python code analysis, python code is in examples.
2020-12-25 12:09:43 +08:00
Kevin Wan
ba4bac3a03 format code (#312) 2020-12-25 11:53:37 +08:00
FengZhang
08433d7e04 add config load support env var (#309) 2020-12-25 11:42:19 +08:00
anqiansong
a3b525b50d feature model fix (#296)
* add raw stirng quote for sql field

* remove unused code
2020-12-21 09:43:32 +08:00
Kevin Wan
097f6886f2 Update readme.md 2020-12-15 23:47:41 +08:00
Kevin Wan
07a1549634 add wechat micro practice qrcode image (#289) 2020-12-14 17:49:58 +08:00
Kevin Wan
befca26c58 Update readme.md
add goproxy.cn download badge
2020-12-13 00:02:32 +08:00
Kevin Wan
3556a2eef4 Update readme-en.md
goreportcard is not working, submitted an issue to them.
2020-12-12 23:40:26 +08:00
Kevin Wan
807765f77e Update readme.md
goreportcard is not working, submitted a issue to them.
2020-12-12 23:39:28 +08:00
Kevin Wan
e44584e549 Create codeql-analysis.yml 2020-12-12 23:01:15 +08:00
Kevin Wan
acd48f0abb optimize dockerfile generation (#284) 2020-12-12 16:53:06 +08:00
kingxt
f919bc6713 refactor (#283) 2020-12-12 11:18:22 +08:00
Kevin Wan
a0030b8f45 format dockerfile on non-chinese mode (#282) 2020-12-12 10:13:33 +08:00
Kevin Wan
a5f0cce1b1 Update readme-en.md 2020-12-12 09:06:09 +08:00
Kevin Wan
4d13dda605 add EXPOSE in dockerfile generation (#281) 2020-12-12 08:18:01 +08:00
songmeizi
b56cc8e459 optimize test case of TestRpcGenerate (#279)
Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2020-12-11 21:57:04 +08:00
Kevin Wan
c435811479 fix gocyclo warnings (#278) 2020-12-11 20:57:48 +08:00
Kevin Wan
c686c93fb5 fix dockerfile generation bug (#277) 2020-12-11 20:31:31 +08:00
Kevin Wan
da8f76e6bd add category docker & kube (#276) 2020-12-11 18:53:40 +08:00
Kevin Wan
99596a4149 fix issue #266 (#275)
* optimize dockerfile

* fix issue #266
2020-12-11 16:12:33 +08:00
wayne
ec2a9f2c57 fix tracelogger_test TestTraceLog (#271) 2020-12-10 17:04:57 +08:00
Kevin Wan
fd73ced6dc optimize dockerfile (#272) 2020-12-10 16:21:06 +08:00
Kevin Wan
5071736ab4 fmt code (#270) 2020-12-10 15:16:13 +08:00
Kevin Wan
0d7f1d23b4 require go 1.14 (#263)
* refactor & format code

* optimized parse tag (#256)

* feature plugin custom flag (#251)

* support plugin custom flags

* add short name

* remove log

* remove log

* require go 1.14

Co-authored-by: kingxt <kingxt4job@gmail.com>
Co-authored-by: songmeizi <anqiansong@xiaoheiban.cn>
2020-12-09 22:43:42 +08:00
songmeizi
84ab11ac09 feature plugin custom flag (#251)
* support plugin custom flags

* add short name

* remove log

* remove log
2020-12-09 18:08:17 +08:00
kingxt
67804a6bb2 optimized parse tag (#256) 2020-12-09 11:16:38 +08:00
Kevin Wan
65ee877236 refactor & format code (#255) 2020-12-08 23:01:25 +08:00
songmeizi
b060867009 Feature bookstore update (#253)
* update bookstore

* update bookstore
2020-12-08 22:36:48 +08:00
songmeizi
4d53045c6b improve data type conversion (#236)
* improve data type conversion

* update doc
2020-12-08 18:06:15 +08:00
kingxt
cecd4b1b75 goctl add plugin support (#243)
* add plugin support

* add plugin support

* add plugin support

* add plugin support

* add plugin support

* add plugin support

* add plugin support

* add plugin support

* add plugin support

* add plugin support

* add plugin support

* remove no need

* add plugin support

* rename

* rename

* add plugin support

* refactor

* update plugin

* refactor

* refactor

* refactor

* update plugin

* newline

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2020-12-07 14:55:10 +08:00
Kevin Wan
7cd0463953 fix lint errors (#249)
* simplify code, format makefile

* simplify code

* some optimize by kevwan and benying (#240)

Co-authored-by: 杨志泉 <zhiquan.yang@yiducloud.cn>

* optimization (#241)

* optimize docker file generation, make docker build faster

* support k8s deployment yaml generation

* fix lint errors

Co-authored-by: benying <31179034+benyingY@users.noreply.github.com>
Co-authored-by: 杨志泉 <zhiquan.yang@yiducloud.cn>
Co-authored-by: bittoy <bittoy@qq.com>
2020-12-07 11:12:02 +08:00
Kevin Wan
7a82cf80ce support k8s deployment yaml generation (#247)
* simplify code, format makefile

* simplify code

* some optimize by kevwan and benying (#240)

Co-authored-by: 杨志泉 <zhiquan.yang@yiducloud.cn>

* optimization (#241)

* optimize docker file generation, make docker build faster

* support k8s deployment yaml generation

Co-authored-by: benying <31179034+benyingY@users.noreply.github.com>
Co-authored-by: 杨志泉 <zhiquan.yang@yiducloud.cn>
Co-authored-by: bittoy <bittoy@qq.com>
2020-12-07 00:07:50 +08:00
Kevin Wan
f997aee3ba optimize docker file generation, make docker build faster (#244)
* simplify code, format makefile

* simplify code

* some optimize by kevwan and benying (#240)

Co-authored-by: 杨志泉 <zhiquan.yang@yiducloud.cn>

* optimization (#241)

* optimize docker file generation, make docker build faster

Co-authored-by: benying <31179034+benyingY@users.noreply.github.com>
Co-authored-by: 杨志泉 <zhiquan.yang@yiducloud.cn>
Co-authored-by: bittoy <bittoy@qq.com>
2020-12-05 21:48:09 +08:00
bittoy
88ec89bdbd optimization (#241) 2020-12-02 15:00:07 +08:00
benying
7d1b43780a some optimize by kevwan and benying (#240)
Co-authored-by: 杨志泉 <zhiquan.yang@yiducloud.cn>
2020-12-01 06:44:32 +08:00
Kevin Wan
4b5c2de376 simplify code (#234)
* simplify code, format makefile

* simplify code
2020-11-29 12:41:42 +08:00
Kevin Wan
e5c560e8ba simplify code, format makefile (#233) 2020-11-28 22:27:58 +08:00
xuezonggui
bed494d904 optimization (#221) 2020-11-28 19:43:39 +08:00
Keson
2dfecda465 modify the service name from proto (#230) 2020-11-28 11:48:44 +08:00
voidint
3ebb1e0221 Improve Makefile robustness (#224) 2020-11-27 23:40:07 +08:00
kingxt
348184904c set default handler value (#228)
* set default value

* set default value
2020-11-26 11:57:02 +08:00
Keson
7a27fa50a1 update version (#226) 2020-11-25 12:04:22 +08:00
Kevin Wan
8d4951c990 check go.mod before build docker image (#225) 2020-11-24 23:19:31 +08:00
Keson
6e57f6c527 feature model interface (#222)
* make variable declaration more concise

* add model interface

* optimize interface methods

* fix: go test failed

* warp returns

* optimize
2020-11-24 22:36:23 +08:00
kingxt
b9ac51b6c3 feature: file namestyle (#223)
* add api filename style

* new feature: config.yaml

* optimize

* optimize logic generation

* check hanlder valid

* optimize

* reactor naming style

* optimize

* optimize test

* optimize gen middleware

* format

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-24 15:11:18 +08:00
kevin
702e8d79ce fix doc errors 2020-11-24 10:39:38 +08:00
kevin
95a9dabf8b format import 2020-11-23 16:35:39 +08:00
Chris
bae66c49c2 1.use local variable i; 2.make sure limiter larger than timer period (#218)
Co-authored-by: chris <feilee1987@163.com>
2020-11-23 16:34:51 +08:00
kingxt
e0afe0b4bb optimize api new (#216) 2020-11-19 16:48:48 +08:00
Keson
24fb29a356 patch model&rpc (#207)
* change column to read from information_schema

* reactor generate mode from datasource

* reactor generate mode from datasource

* add primary key check logic

* resolve rebase conflicts

* add naming style

* add filename test case

* resolve rebase conflicts

* reactor test

* add test case

* change shell script to makefile

* update rpc new

* update gen_test.go

* format code

* format code

* update test

* generates alias
2020-11-18 15:32:53 +08:00
kevin
71083b5e64 update readme 2020-11-17 19:01:14 +08:00
kingxt
1174f17bd9 modify image url (#213) 2020-11-17 18:50:22 +08:00
kingxt
d6d8fc21d8 type should not define nested (#212)
* nest type should not supported

* nest type should not supported

* nest type should not supported

* nest type should not supported

* new test

* new test
2020-11-17 18:08:55 +08:00
kevin
9592639cb4 add error handle tests 2020-11-17 18:04:48 +08:00
kevin
abcb28e506 support error customization 2020-11-17 17:11:06 +08:00
kingxt
a92f65580c support type def without struct token (#210)
* add comment support

* add comment support

* 1. group support multi level folder
2. remove force flag

* bug fix

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* support type def without struct token

* support type def without struct token

* support type def without struct token

* support type def without struct token

* support type def without struct token

* support type def without struct token

* support type def without struct token

* optimized

* optimized

* optimized

Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-17 15:25:13 +08:00
bittoy
3819f67cf4 add redis geospatial (#209)
* add redis geospatial

* fix go test error
2020-11-16 19:45:43 +08:00
kevin
295c8d2934 fix issue #205 2020-11-16 19:23:24 +08:00
kingxt
88da8685dd optimize parser (#206)
* add comment support

* add comment support

* 1. group support multi level folder
2. remove force flag

* bug fix

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* optimized parser

Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-16 10:08:28 +08:00
kevin
c7831ac96d update goctl readme 2020-11-15 21:18:02 +08:00
kevin
e898761762 update example 2020-11-15 21:15:29 +08:00
kevin
13d1c5cd00 update example 2020-11-14 22:01:35 +08:00
kingxt
16bfb1b7be refactor parser and remove deprecated code (#204)
* add comment support

* add comment support

* 1. group support multi level folder
2. remove force flag

* bug fix

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-13 23:01:19 +08:00
kingxt
ef4d4968d6 1. group support multi level folder 2. remove force flag (#203)
* add comment support

* add comment support

* 1. group support multi level folder
2. remove force flag

* bug fix

Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-12 19:47:32 +08:00
kingxt
7b4a5e3ec6 api support for comment double slash // (#201)
* add comment support

* add comment support

Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-12 16:57:28 +08:00
kevin
e6df21e0d2 format code 2020-11-11 17:20:56 +08:00
SunJun
0a2c2d1eca change grpc interceptor to chain interceptor (#200)
* change grpc interceptor to chain interceptor

* change server rpc interceptors, del testing code
2020-11-11 17:15:22 +08:00
kevin
a5fb29a6f0 update etcd yaml to avoid no such nost resolve problem 2020-11-11 11:06:23 +08:00
zhoushuguang
f8da301e57 no default metric (#199)
Co-authored-by: zhoushuguang <zhoushuguang@xiaoheiban.cn>
2020-11-10 11:47:08 +08:00
kevin
cb9075b737 add dockerfile into template 2020-11-09 18:02:16 +08:00
kingxt
3f389a55c2 format service and add test (#197)
Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-09 17:41:07 +08:00
kevin
afbd565d87 rename postgres 2020-11-09 17:22:51 +08:00
zhoushuguang
d629acc2b7 default metric host (#196)
Co-authored-by: zhoushuguang <zhoushuguang@xiaoheiban.cn>
2020-11-09 16:03:07 +08:00
kingxt
f32c6a9b28 rewrite (#194)
Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-09 10:06:45 +08:00
kevin
95aa65efb9 add dockerfile generator 2020-11-08 21:28:58 +08:00
kevin
3806e66cf1 simplify http server starter 2020-11-08 13:17:14 +08:00
kevin
bd430baf52 graceful shutdown refined 2020-11-08 13:08:00 +08:00
Keson
48f4154ea8 update doc (#193) 2020-11-08 13:02:48 +08:00
super_mario
2599e0d28d Close the process when shutdown is finished (#157)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2020-11-08 12:50:58 +08:00
kingxt
12327fa07d break generator when happen error (#192)
Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-07 21:25:52 +08:00
kevin
57079bf4a4 update cli package 2020-11-07 20:01:25 +08:00
kingxt
7f6eceb5a3 add more test (#189)
* new test

* import bug when with quotation

* new test

* add test condition

* rpc template command use -o param

Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-07 17:13:40 +08:00
kevin
7d7cb836af fix issue #186 2020-11-06 12:25:48 +08:00
kevin
f87d9d1dda refine code style 2020-11-06 12:13:28 +08:00
Keson
856b5aadb1 rpc generation fix (#184)
* reactor alert

* optimize

* add test case

* update the target directory in case proto contains option

* fix missing comments and format code
2020-11-05 19:08:34 +08:00
Keson
f7d778e0ed fix duplicate alias (#183) 2020-11-05 18:12:23 +08:00
kevin
88333ee77f faster the tests 2020-11-05 16:04:00 +08:00
Keson
e76f44a35b reactor rpc (#179)
* reactor rpc generation

* update flag

* update command

* update command

* update unit test

* delete test file

* optimize code

* update doc

* update gen pb

* rename target dir

* update mysql data type convert rule

* add done flag

* optimize req/reply parameter

* optimize req/reply parameter

* remove waste code

* remove duplicate parameter

* format code

* format code

* optimize naming

* reactor rpcv2 to rpc

* remove new line

* format code

* rename underline to snake

* reactor getParentPackage

* remove debug log

* reactor background
2020-11-05 14:12:47 +08:00
kevin
c9ec22d5f4 add https listen and serve 2020-11-05 11:56:40 +08:00
Dashuang Li
afffc1048b fix url 404 (#180) 2020-11-04 12:03:07 +08:00
kevin
d0b76b1d9a move redistest into redis package 2020-11-03 16:35:34 +08:00
kevin
b004b070d7 refine tests 2020-11-02 17:51:33 +08:00
kevin
677d581bd1 update doc 2020-11-02 17:05:09 +08:00
kingxt
b776468e69 route support no request and response (#178)
* add more test and support no request and response

* fix slash when run on windows

* optimize test
2020-11-02 13:48:16 +08:00
kevin
4c9315e984 add more tests 2020-10-31 22:10:11 +08:00
kevin
668a7011c4 add more tests 2020-10-31 20:11:12 +08:00
吴亲库里
cc07a1d69b Update sharedcalls.go (#174)
Removes unused parameters
2020-10-31 19:40:07 +08:00
kevin
7f99a3baa8 add gitee url 2020-10-31 13:58:33 +08:00
kevin
9504418462 update doc 2020-10-31 12:41:29 +08:00
kevin
b144a2335c update bookstore example for generation prototype 2020-10-31 11:42:44 +08:00
kevin
7b9ed7a313 update doc 2020-10-30 15:20:19 +08:00
kevin
3d2e9fcb84 remove wechat image 2020-10-30 11:57:32 +08:00
kevin
2b993424c1 update wechat qrcode 2020-10-30 11:54:06 +08:00
kevin
5e87b33b23 support https in rest 2020-10-29 17:44:51 +08:00
kevin
9b7cc43dcb update wechat qrcode 2020-10-29 15:32:08 +08:00
kevin
000b28cf84 update readme 2020-10-29 11:31:35 +08:00
kevin
9fd16cd278 add images back because of gitee not showing 2020-10-29 11:27:40 +08:00
kevin
b71429e16b add images back because of gitee not showing 2020-10-29 11:26:10 +08:00
288 changed files with 9080 additions and 5939 deletions

67
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '18 19 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

2
.gitignore vendored
View File

@@ -4,6 +4,7 @@
# Unignore all with extensions
!*.*
!**/Dockerfile
!**/Makefile
# Unignore all dirs
!*/
@@ -12,7 +13,6 @@
.idea
**/.DS_Store
**/logs
!Makefile
# gitlab ci
.cache

View File

@@ -2,20 +2,16 @@ package bloom
import (
"testing"
"time"
"github.com/alicebob/miniredis"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/lang"
"github.com/tal-tech/go-zero/core/stores/redis"
"github.com/tal-tech/go-zero/core/stores/redis/redistest"
)
func TestRedisBitSet_New_Set_Test(t *testing.T) {
s, clean, err := createMiniRedis()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
store := redis.NewRedis(s.Addr(), redis.NodeType)
bitSet := newRedisBitSet(store, "test_key", 1024)
isSetBefore, err := bitSet.check([]uint{0})
if err != nil {
@@ -46,11 +42,10 @@ func TestRedisBitSet_New_Set_Test(t *testing.T) {
}
func TestRedisBitSet_Add(t *testing.T) {
s, clean, err := createMiniRedis()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
store := redis.NewRedis(s.Addr(), redis.NodeType)
filter := New(store, "test_key", 64)
assert.Nil(t, filter.Add([]byte("hello")))
assert.Nil(t, filter.Add([]byte("world")))
@@ -58,22 +53,3 @@ func TestRedisBitSet_Add(t *testing.T) {
assert.Nil(t, err)
assert.True(t, ok)
}
func createMiniRedis() (r *miniredis.Miniredis, clean func(), err error) {
r, err = miniredis.Run()
if err != nil {
return nil, nil, err
}
return r, func() {
ch := make(chan lang.PlaceholderType)
go func() {
r.Close()
close(ch)
}()
select {
case <-ch:
case <-time.After(time.Second):
}
}, nil
}

View File

@@ -8,8 +8,10 @@ import (
)
type (
// RollingWindowOption let callers customize the RollingWindow.
RollingWindowOption func(rollingWindow *RollingWindow)
// RollingWindow defines a rolling window to calculate the events in buckets with time interval.
RollingWindow struct {
lock sync.RWMutex
size int
@@ -17,10 +19,12 @@ type (
interval time.Duration
offset int
ignoreCurrent bool
lastTime time.Duration
lastTime time.Duration // start time of the last bucket
}
)
// NewRollingWindow returns a RollingWindow that with size buckets and time interval,
// use opts to customize the RollingWindow.
func NewRollingWindow(size int, interval time.Duration, opts ...RollingWindowOption) *RollingWindow {
if size < 1 {
panic("size must be greater than 0")
@@ -38,6 +42,7 @@ func NewRollingWindow(size int, interval time.Duration, opts ...RollingWindowOpt
return w
}
// Add adds value to current bucket.
func (rw *RollingWindow) Add(v float64) {
rw.lock.Lock()
defer rw.lock.Unlock()
@@ -45,6 +50,7 @@ func (rw *RollingWindow) Add(v float64) {
rw.win.add(rw.offset, v)
}
// Reduce runs fn on all buckets, ignore current bucket if ignoreCurrent was set.
func (rw *RollingWindow) Reduce(fn func(b *Bucket)) {
rw.lock.RLock()
defer rw.lock.RUnlock()
@@ -74,29 +80,23 @@ func (rw *RollingWindow) span() int {
func (rw *RollingWindow) updateOffset() {
span := rw.span()
if span > 0 {
offset := rw.offset
// reset expired buckets
start := offset + 1
steps := start + span
var remainder int
if steps > rw.size {
remainder = steps - rw.size
steps = rw.size
}
for i := start; i < steps; i++ {
rw.win.resetBucket(i)
offset = i
}
for i := 0; i < remainder; i++ {
rw.win.resetBucket(i)
offset = i
}
rw.offset = offset
rw.lastTime = timex.Now()
if span <= 0 {
return
}
offset := rw.offset
// reset expired buckets
for i := 0; i < span; i++ {
rw.win.resetBucket((offset + i + 1) % rw.size)
}
rw.offset = (offset + span) % rw.size
now := timex.Now()
// align to interval time boundary
rw.lastTime = now - (now-rw.lastTime)%rw.interval
}
// Bucket defines the bucket that holds sum and num of additions.
type Bucket struct {
Sum float64
Count int64
@@ -118,9 +118,9 @@ type window struct {
}
func newWindow(size int) *window {
var buckets []*Bucket
buckets := make([]*Bucket, size)
for i := 0; i < size; i++ {
buckets = append(buckets, new(Bucket))
buckets[i] = new(Bucket)
}
return &window{
buckets: buckets,
@@ -134,14 +134,15 @@ func (w *window) add(offset int, v float64) {
func (w *window) reduce(start, count int, fn func(b *Bucket)) {
for i := 0; i < count; i++ {
fn(w.buckets[(start+i)%len(w.buckets)])
fn(w.buckets[(start+i)%w.size])
}
}
func (w *window) resetBucket(offset int) {
w.buckets[offset].reset()
w.buckets[offset%w.size].reset()
}
// IgnoreCurrentBucket lets the Reduce call ignore current bucket.
func IgnoreCurrentBucket() RollingWindowOption {
return func(w *RollingWindow) {
w.ignoreCurrent = true

View File

@@ -105,6 +105,37 @@ func TestRollingWindowReduce(t *testing.T) {
}
}
func TestRollingWindowBucketTimeBoundary(t *testing.T) {
const size = 3
interval := time.Millisecond * 30
r := NewRollingWindow(size, interval)
listBuckets := func() []float64 {
var buckets []float64
r.Reduce(func(b *Bucket) {
buckets = append(buckets, b.Sum)
})
return buckets
}
assert.Equal(t, []float64{0, 0, 0}, listBuckets())
r.Add(1)
assert.Equal(t, []float64{0, 0, 1}, listBuckets())
time.Sleep(time.Millisecond * 45)
r.Add(2)
r.Add(3)
assert.Equal(t, []float64{0, 1, 5}, listBuckets())
// sleep time should be less than interval, and make the bucket change happen
time.Sleep(time.Millisecond * 20)
r.Add(4)
r.Add(5)
r.Add(6)
assert.Equal(t, []float64{1, 5, 15}, listBuckets())
time.Sleep(time.Millisecond * 100)
r.Add(7)
r.Add(8)
r.Add(9)
assert.Equal(t, []float64{0, 0, 24}, listBuckets())
}
func TestRollingWindowDataRace(t *testing.T) {
const size = 3
r := NewRollingWindow(size, duration)

View File

@@ -204,6 +204,7 @@ func (tw *TimingWheel) removeTask(key interface{}) {
timer := val.(*positionEntry)
timer.item.removed = true
tw.timers.Del(key)
}
func (tw *TimingWheel) run() {
@@ -248,7 +249,6 @@ func (tw *TimingWheel) scanAndRunTasks(l *list.List) {
if task.removed {
next := e.Next()
l.Remove(e)
tw.timers.Del(task.key)
e = next
continue
} else if task.circle > 0 {
@@ -301,6 +301,7 @@ func (tw *TimingWheel) setTask(task *timingEntry) {
func (tw *TimingWheel) setTimerPosition(pos int, task *timingEntry) {
if val, ok := tw.timers.Get(task.key); ok {
timer := val.(*positionEntry)
timer.item = task
timer.pos = pos
} else {
tw.timers.Set(task.key, &positionEntry{

View File

@@ -594,6 +594,31 @@ func TestTimingWheel_ElapsedAndSetThenMove(t *testing.T) {
}
}
func TestMoveAndRemoveTask(t *testing.T) {
ticker := timex.NewFakeTicker()
tick := func(v int) {
for i := 0; i < v; i++ {
ticker.Tick()
}
}
var keys []int
tw, _ := newTimingWheelWithClock(testStep, 10, func(k, v interface{}) {
assert.Equal(t, "any", k)
assert.Equal(t, 3, v.(int))
keys = append(keys, v.(int))
ticker.Done()
}, ticker)
defer tw.Stop()
tw.SetTimer("any", 3, testStep*8)
tick(6)
tw.MoveTimer("any", testStep*7)
tick(3)
tw.RemoveTimer("any")
tick(30)
time.Sleep(time.Millisecond)
assert.Equal(t, 0, len(keys))
}
func BenchmarkTimingWheel(b *testing.B) {
b.ReportAllocs()

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"io/ioutil"
"log"
"os"
"path"
"github.com/tal-tech/go-zero/core/mapping"
@@ -19,7 +20,7 @@ func LoadConfig(file string, v interface{}) error {
if content, err := ioutil.ReadFile(file); err != nil {
return err
} else if loader, ok := loaders[path.Ext(file)]; ok {
return loader(content, v)
return loader([]byte(os.ExpandEnv(string(content))), v)
} else {
return fmt.Errorf("unrecoginized file type: %s", file)
}

View File

@@ -17,13 +17,14 @@ func TestConfigJson(t *testing.T) {
}
text := `{
"a": "foo",
"b": 1
"b": 1,
"c": "${FOO}"
}`
for _, test := range tests {
test := test
t.Run(test, func(t *testing.T) {
t.Parallel()
os.Setenv("FOO", "2")
defer os.Unsetenv("FOO")
tmpfile, err := createTempFile(test, text)
assert.Nil(t, err)
defer os.Remove(tmpfile)
@@ -31,10 +32,12 @@ func TestConfigJson(t *testing.T) {
var val struct {
A string `json:"a"`
B int `json:"b"`
C string `json:"c"`
}
MustLoad(tmpfile, &val)
assert.Equal(t, "foo", val.A)
assert.Equal(t, 1, val.B)
assert.Equal(t, "2", val.C)
})
}
}

View File

@@ -35,7 +35,7 @@ spec:
- --listen-client-urls
- http://0.0.0.0:2379
- --advertise-client-urls
- http://etcd0:2379
- http://etcd0.discov:2379
- --initial-cluster
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
- --initial-cluster-state
@@ -107,7 +107,7 @@ spec:
- --listen-client-urls
- http://0.0.0.0:2379
- --advertise-client-urls
- http://etcd1:2379
- http://etcd1.discov:2379
- --initial-cluster
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
- --initial-cluster-state
@@ -179,7 +179,7 @@ spec:
- --listen-client-urls
- http://0.0.0.0:2379
- --advertise-client-urls
- http://etcd2:2379
- http://etcd2.discov:2379
- --initial-cluster
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
- --initial-cluster-state
@@ -251,7 +251,7 @@ spec:
- --listen-client-urls
- http://0.0.0.0:2379
- --advertise-client-urls
- http://etcd3:2379
- http://etcd3.discov:2379
- --initial-cluster
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
- --initial-cluster-state
@@ -323,7 +323,7 @@ spec:
- --listen-client-urls
- http://0.0.0.0:2379
- --advertise-client-urls
- http://etcd4:2379
- http://etcd4.discov:2379
- --initial-cluster
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
- --initial-cluster-state

View File

@@ -3,6 +3,7 @@ package executors
import (
"reflect"
"sync"
"sync/atomic"
"time"
"github.com/tal-tech/go-zero/core/lang"
@@ -35,6 +36,7 @@ type (
// avoid race condition on waitGroup when calling wg.Add/Done/Wait(...)
wgBarrier syncx.Barrier
confirmChan chan lang.PlaceholderType
inflight int32
guarded bool
newTicker func(duration time.Duration) timex.Ticker
lock sync.Mutex
@@ -82,6 +84,7 @@ func (pe *PeriodicalExecutor) Sync(fn func()) {
}
func (pe *PeriodicalExecutor) Wait() {
pe.Flush()
pe.wgBarrier.Guard(func() {
pe.waitGroup.Wait()
})
@@ -90,18 +93,16 @@ func (pe *PeriodicalExecutor) Wait() {
func (pe *PeriodicalExecutor) addAndCheck(task interface{}) (interface{}, bool) {
pe.lock.Lock()
defer func() {
var start bool
if !pe.guarded {
pe.guarded = true
start = true
// defer to unlock quickly
defer pe.backgroundFlush()
}
pe.lock.Unlock()
if start {
pe.backgroundFlush()
}
}()
if pe.container.AddTask(task) {
atomic.AddInt32(&pe.inflight, 1)
return pe.container.RemoveAll(), true
}
@@ -110,6 +111,9 @@ func (pe *PeriodicalExecutor) addAndCheck(task interface{}) (interface{}, bool)
func (pe *PeriodicalExecutor) backgroundFlush() {
threading.GoSafe(func() {
// flush before quit goroutine to avoid missing tasks
defer pe.Flush()
ticker := pe.newTicker(pe.interval)
defer ticker.Stop()
@@ -119,6 +123,7 @@ func (pe *PeriodicalExecutor) backgroundFlush() {
select {
case vals := <-pe.commander:
commanded = true
atomic.AddInt32(&pe.inflight, -1)
pe.enterExecution()
pe.confirmChan <- lang.Placeholder
pe.executeTasks(vals)
@@ -128,13 +133,7 @@ func (pe *PeriodicalExecutor) backgroundFlush() {
commanded = false
} else if pe.Flush() {
last = timex.Now()
} else if timex.Since(last) > pe.interval*idleRound {
pe.lock.Lock()
pe.guarded = false
pe.lock.Unlock()
// flush again to avoid missing tasks
pe.Flush()
} else if pe.shallQuit(last) {
return
}
}
@@ -177,3 +176,19 @@ func (pe *PeriodicalExecutor) hasTasks(tasks interface{}) bool {
return true
}
}
func (pe *PeriodicalExecutor) shallQuit(last time.Duration) (stop bool) {
if timex.Since(last) <= pe.interval*idleRound {
return
}
// checking pe.inflight and setting pe.guarded should be locked together
pe.lock.Lock()
if atomic.LoadInt32(&pe.inflight) == 0 {
pe.guarded = false
stop = true
}
pe.lock.Unlock()
return
}

View File

@@ -140,6 +140,26 @@ func TestPeriodicalExecutor_WaitFast(t *testing.T) {
assert.Equal(t, total, cnt)
}
func TestPeriodicalExecutor_Deadlock(t *testing.T) {
executor := NewBulkExecutor(func(tasks []interface{}) {
}, WithBulkTasks(1), WithBulkInterval(time.Millisecond))
for i := 0; i < 1e5; i++ {
executor.Add(1)
}
}
func TestPeriodicalExecutor_hasTasks(t *testing.T) {
ticker := timex.NewFakeTicker()
defer ticker.Stop()
exec := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, nil))
exec.newTicker = func(d time.Duration) timex.Ticker {
return ticker
}
assert.False(t, exec.hasTasks(nil))
assert.True(t, exec.hasTasks(1))
}
// go test -benchtime 10s -bench .
func BenchmarkExecutor(b *testing.B) {
b.ReportAllocs()

View File

@@ -3,9 +3,10 @@ package limit
import (
"testing"
"github.com/alicebob/miniredis"
"github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/stores/redis"
"github.com/tal-tech/go-zero/core/stores/redis/redistest"
)
func TestPeriodLimit_Take(t *testing.T) {
@@ -33,16 +34,16 @@ func TestPeriodLimit_RedisUnavailable(t *testing.T) {
}
func testPeriodLimit(t *testing.T, opts ...LimitOption) {
s, err := miniredis.Run()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer s.Close()
defer clean()
const (
seconds = 1
total = 100
quota = 5
)
l := NewPeriodLimit(seconds, quota, redis.NewRedis(s.Addr(), redis.NodeType), "periodlimit", opts...)
l := NewPeriodLimit(seconds, quota, store, "periodlimit", opts...)
var allowed, hitQuota, overQuota int
for i := 0; i < total; i++ {
val, err := l.Take("first")

View File

@@ -4,10 +4,11 @@ import (
"testing"
"time"
"github.com/alicebob/miniredis"
"github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stores/redis"
"github.com/tal-tech/go-zero/core/stores/redis/redistest"
)
func init() {
@@ -44,16 +45,16 @@ func TestTokenLimit_Rescue(t *testing.T) {
}
func TestTokenLimit_Take(t *testing.T) {
s, err := miniredis.Run()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer s.Close()
defer clean()
const (
total = 100
rate = 5
burst = 10
)
l := NewTokenLimiter(rate, burst, redis.NewRedis(s.Addr(), redis.NodeType), "tokenlimit")
l := NewTokenLimiter(rate, burst, store, "tokenlimit")
var allowed int
for i := 0; i < total; i++ {
time.Sleep(time.Second / time.Duration(total))
@@ -66,16 +67,16 @@ func TestTokenLimit_Take(t *testing.T) {
}
func TestTokenLimit_TakeBurst(t *testing.T) {
s, err := miniredis.Run()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer s.Close()
defer clean()
const (
total = 100
rate = 5
burst = 10
)
l := NewTokenLimiter(rate, burst, redis.NewRedis(s.Addr(), redis.NodeType), "tokenlimit")
l := NewTokenLimiter(rate, burst, store, "tokenlimit")
var allowed int
for i := 0; i < total; i++ {
if l.Allow() {

View File

@@ -21,6 +21,7 @@ var mock tracespec.Trace = new(mockTrace)
func TestTraceLog(t *testing.T) {
var buf mockWriter
atomic.StoreUint32(&initialized, 1)
ctx := context.WithValue(context.Background(), tracespec.TracingKey, mock)
WithContext(ctx).(*traceLogger).write(&buf, levelInfo, testlog)
assert.True(t, strings.Contains(buf.String(), mockTraceId))

View File

@@ -153,58 +153,57 @@ func doParseKeyAndOptions(field reflect.StructField, value string) (string, *fie
key := strings.TrimSpace(segments[0])
options := segments[1:]
if len(options) > 0 {
var fieldOpts fieldOptions
for _, segment := range options {
option := strings.TrimSpace(segment)
switch {
case option == stringOption:
fieldOpts.FromString = true
case strings.HasPrefix(option, optionalOption):
segs := strings.Split(option, equalToken)
switch len(segs) {
case 1:
fieldOpts.Optional = true
case 2:
fieldOpts.Optional = true
fieldOpts.OptionalDep = segs[1]
default:
return "", nil, fmt.Errorf("field %s has wrong optional", field.Name)
}
case option == optionalOption:
fieldOpts.Optional = true
case strings.HasPrefix(option, optionsOption):
segs := strings.Split(option, equalToken)
if len(segs) != 2 {
return "", nil, fmt.Errorf("field %s has wrong options", field.Name)
} else {
fieldOpts.Options = strings.Split(segs[1], optionSeparator)
}
case strings.HasPrefix(option, defaultOption):
segs := strings.Split(option, equalToken)
if len(segs) != 2 {
return "", nil, fmt.Errorf("field %s has wrong default option", field.Name)
} else {
fieldOpts.Default = strings.TrimSpace(segs[1])
}
case strings.HasPrefix(option, rangeOption):
segs := strings.Split(option, equalToken)
if len(segs) != 2 {
return "", nil, fmt.Errorf("field %s has wrong range", field.Name)
}
if nr, err := parseNumberRange(segs[1]); err != nil {
return "", nil, err
} else {
fieldOpts.Range = nr
}
}
}
return key, &fieldOpts, nil
if len(options) == 0 {
return key, nil, nil
}
return key, nil, nil
var fieldOpts fieldOptions
for _, segment := range options {
option := strings.TrimSpace(segment)
switch {
case option == stringOption:
fieldOpts.FromString = true
case strings.HasPrefix(option, optionalOption):
segs := strings.Split(option, equalToken)
switch len(segs) {
case 1:
fieldOpts.Optional = true
case 2:
fieldOpts.Optional = true
fieldOpts.OptionalDep = segs[1]
default:
return "", nil, fmt.Errorf("field %s has wrong optional", field.Name)
}
case option == optionalOption:
fieldOpts.Optional = true
case strings.HasPrefix(option, optionsOption):
segs := strings.Split(option, equalToken)
if len(segs) != 2 {
return "", nil, fmt.Errorf("field %s has wrong options", field.Name)
} else {
fieldOpts.Options = strings.Split(segs[1], optionSeparator)
}
case strings.HasPrefix(option, defaultOption):
segs := strings.Split(option, equalToken)
if len(segs) != 2 {
return "", nil, fmt.Errorf("field %s has wrong default option", field.Name)
} else {
fieldOpts.Default = strings.TrimSpace(segs[1])
}
case strings.HasPrefix(option, rangeOption):
segs := strings.Split(option, equalToken)
if len(segs) != 2 {
return "", nil, fmt.Errorf("field %s has wrong range", field.Name)
}
if nr, err := parseNumberRange(segs[1]); err != nil {
return "", nil, err
} else {
fieldOpts.Range = nr
}
}
}
return key, &fieldOpts, nil
}
func implicitValueRequiredStruct(tag string, tp reflect.Type) (bool, error) {

View File

@@ -0,0 +1,16 @@
package prof
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestReport(t *testing.T) {
once.Do(func() {})
assert.NotContains(t, generateReport(), "foo")
report("foo", time.Second)
assert.Contains(t, generateReport(), "foo")
report("foo", time.Second)
}

View File

@@ -0,0 +1,23 @@
package prof
import (
"testing"
"github.com/tal-tech/go-zero/core/utils"
)
func TestProfiler(t *testing.T) {
EnableProfiling()
Start()
Report("foo", ProfilePoint{
ElapsedTimer: utils.NewElapsedTimer(),
})
}
func TestNullProfiler(t *testing.T) {
p := newNullProfiler()
p.Start()
p.Report("foo", ProfilePoint{
ElapsedTimer: utils.NewElapsedTimer(),
})
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/tal-tech/go-zero/core/errorx"
"github.com/tal-tech/go-zero/core/hash"
"github.com/tal-tech/go-zero/core/stores/redis"
"github.com/tal-tech/go-zero/core/stores/redis/redistest"
"github.com/tal-tech/go-zero/core/syncx"
)
@@ -75,23 +76,23 @@ func (mc *mockedNode) TakeWithExpire(v interface{}, key string, query func(v int
func TestCache_SetDel(t *testing.T) {
const total = 1000
r1, clean1, err := createMiniRedis()
r1, clean1, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean1()
r2, clean2, err := createMiniRedis()
r2, clean2, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean2()
conf := ClusterConf{
{
RedisConf: redis.RedisConf{
Host: r1.Addr(),
Host: r1.Addr,
Type: redis.NodeType,
},
Weight: 100,
},
{
RedisConf: redis.RedisConf{
Host: r2.Addr(),
Host: r2.Addr,
Type: redis.NodeType,
},
Weight: 100,
@@ -123,13 +124,13 @@ func TestCache_SetDel(t *testing.T) {
func TestCache_OneNode(t *testing.T) {
const total = 1000
r, clean, err := createMiniRedis()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
conf := ClusterConf{
{
RedisConf: redis.RedisConf{
Host: r.Addr(),
Host: r.Addr,
Type: redis.NodeType,
},
Weight: 100,

View File

@@ -175,12 +175,12 @@ func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) e
}
if fresh {
return nil
} else {
// got the result from previous ongoing query
c.stat.IncrementTotal()
c.stat.IncrementHit()
}
// got the result from previous ongoing query
c.stat.IncrementTotal()
c.stat.IncrementHit()
return jsonx.Unmarshal(val.([]byte), v)
}

View File

@@ -9,12 +9,13 @@ import (
"testing"
"time"
"github.com/alicebob/miniredis"
"github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/mathx"
"github.com/tal-tech/go-zero/core/stat"
"github.com/tal-tech/go-zero/core/stores/redis"
"github.com/tal-tech/go-zero/core/stores/redis/redistest"
"github.com/tal-tech/go-zero/core/syncx"
)
@@ -26,12 +27,12 @@ func init() {
}
func TestCacheNode_DelCache(t *testing.T) {
s, clean, err := createMiniRedis()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
cn := cacheNode{
rds: redis.NewRedis(s.Addr(), redis.NodeType),
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
lock: new(sync.Mutex),
unstableExpiry: mathx.NewUnstable(expiryDeviation),
@@ -49,9 +50,9 @@ func TestCacheNode_DelCache(t *testing.T) {
}
func TestCacheNode_InvalidCache(t *testing.T) {
s, clean, err := createMiniRedis()
s, err := miniredis.Run()
assert.Nil(t, err)
defer clean()
defer s.Close()
cn := cacheNode{
rds: redis.NewRedis(s.Addr(), redis.NodeType),
@@ -70,12 +71,12 @@ func TestCacheNode_InvalidCache(t *testing.T) {
}
func TestCacheNode_Take(t *testing.T) {
s, clean, err := createMiniRedis()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
cn := cacheNode{
rds: redis.NewRedis(s.Addr(), redis.NodeType),
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
barrier: syncx.NewSharedCalls(),
lock: new(sync.Mutex),
@@ -91,18 +92,18 @@ func TestCacheNode_Take(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, "value", str)
assert.Nil(t, cn.GetCache("any", &str))
val, err := s.Get("any")
val, err := store.Get("any")
assert.Nil(t, err)
assert.Equal(t, `"value"`, val)
}
func TestCacheNode_TakeNotFound(t *testing.T) {
s, clean, err := createMiniRedis()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
cn := cacheNode{
rds: redis.NewRedis(s.Addr(), redis.NodeType),
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
barrier: syncx.NewSharedCalls(),
lock: new(sync.Mutex),
@@ -116,18 +117,18 @@ func TestCacheNode_TakeNotFound(t *testing.T) {
})
assert.Equal(t, errTestNotFound, err)
assert.Equal(t, errTestNotFound, cn.GetCache("any", &str))
val, err := s.Get("any")
val, err := store.Get("any")
assert.Nil(t, err)
assert.Equal(t, `*`, val)
s.Set("any", "*")
store.Set("any", "*")
err = cn.Take(&str, "any", func(v interface{}) error {
return nil
})
assert.Equal(t, errTestNotFound, err)
assert.Equal(t, errTestNotFound, cn.GetCache("any", &str))
s.Del("any")
store.Del("any")
var errDummy = errors.New("dummy")
err = cn.Take(&str, "any", func(v interface{}) error {
return errDummy
@@ -136,12 +137,12 @@ func TestCacheNode_TakeNotFound(t *testing.T) {
}
func TestCacheNode_TakeWithExpire(t *testing.T) {
s, clean, err := createMiniRedis()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
cn := cacheNode{
rds: redis.NewRedis(s.Addr(), redis.NodeType),
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
barrier: syncx.NewSharedCalls(),
lock: new(sync.Mutex),
@@ -157,18 +158,18 @@ func TestCacheNode_TakeWithExpire(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, "value", str)
assert.Nil(t, cn.GetCache("any", &str))
val, err := s.Get("any")
val, err := store.Get("any")
assert.Nil(t, err)
assert.Equal(t, `"value"`, val)
}
func TestCacheNode_String(t *testing.T) {
s, clean, err := createMiniRedis()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
cn := cacheNode{
rds: redis.NewRedis(s.Addr(), redis.NodeType),
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
barrier: syncx.NewSharedCalls(),
lock: new(sync.Mutex),
@@ -176,16 +177,16 @@ func TestCacheNode_String(t *testing.T) {
stat: NewCacheStat("any"),
errNotFound: errors.New("any"),
}
assert.Equal(t, s.Addr(), cn.String())
assert.Equal(t, store.Addr, cn.String())
}
func TestCacheValueWithBigInt(t *testing.T) {
s, clean, err := createMiniRedis()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
cn := cacheNode{
rds: redis.NewRedis(s.Addr(), redis.NodeType),
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
barrier: syncx.NewSharedCalls(),
lock: new(sync.Mutex),

View File

@@ -2,11 +2,8 @@ package cache
import (
"testing"
"time"
"github.com/alicebob/miniredis"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/lang"
)
func TestFormatKeys(t *testing.T) {
@@ -27,22 +24,3 @@ func TestTotalWeights(t *testing.T) {
})
assert.Equal(t, 1, val)
}
func createMiniRedis() (r *miniredis.Miniredis, clean func(), err error) {
r, err = miniredis.Run()
if err != nil {
return nil, nil, err
}
return r, func() {
ch := make(chan lang.PlaceholderType)
go func() {
r.Close()
close(ch)
}()
select {
case <-ch:
case <-time.After(time.Second):
}
}, nil
}

View File

@@ -4,7 +4,7 @@ import (
"testing"
"time"
"github.com/alicebob/miniredis"
"github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/hash"
"github.com/tal-tech/go-zero/core/stores/cache"

View File

@@ -11,7 +11,6 @@ import (
"testing"
"time"
"github.com/alicebob/miniredis"
"github.com/globalsign/mgo"
"github.com/globalsign/mgo/bson"
"github.com/stretchr/testify/assert"
@@ -19,6 +18,7 @@ import (
"github.com/tal-tech/go-zero/core/stores/cache"
"github.com/tal-tech/go-zero/core/stores/mongo"
"github.com/tal-tech/go-zero/core/stores/redis"
"github.com/tal-tech/go-zero/core/stores/redis/redistest"
)
func init() {
@@ -27,12 +27,10 @@ func init() {
func TestStat(t *testing.T) {
resetStats()
s, err := miniredis.Run()
if err != nil {
t.Error(err)
}
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
r := redis.NewRedis(s.Addr(), redis.NodeType)
cach := cache.NewCacheNode(r, sharedCalls, stats, mgo.ErrNotFound)
c := newCollection(dummyConn{}, cach)
@@ -73,12 +71,10 @@ func TestStatCacheFails(t *testing.T) {
func TestStatDbFails(t *testing.T) {
resetStats()
s, err := miniredis.Run()
if err != nil {
t.Error(err)
}
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
r := redis.NewRedis(s.Addr(), redis.NodeType)
cach := cache.NewCacheNode(r, sharedCalls, stats, mgo.ErrNotFound)
c := newCollection(dummyConn{}, cach)
@@ -97,12 +93,10 @@ func TestStatDbFails(t *testing.T) {
func TestStatFromMemory(t *testing.T) {
resetStats()
s, err := miniredis.Run()
if err != nil {
t.Error(err)
}
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
r := redis.NewRedis(s.Addr(), redis.NodeType)
cach := cache.NewCacheNode(r, sharedCalls, stats, mgo.ErrNotFound)
c := newCollection(dummyConn{}, cach)

View File

@@ -5,8 +5,8 @@ import (
"github.com/tal-tech/go-zero/core/stores/sqlx"
)
const postgreDriverName = "postgres"
const postgresDriverName = "postgres"
func NewPostgre(datasource string, opts ...sqlx.SqlOption) sqlx.SqlConn {
return sqlx.NewSqlConn(postgreDriverName, datasource, opts...)
func NewPostgres(datasource string, opts ...sqlx.SqlOption) sqlx.SqlConn {
return sqlx.NewSqlConn(postgresDriverName, datasource, opts...)
}

View File

@@ -42,6 +42,12 @@ type (
red.Cmdable
}
// GeoLocation is used with GeoAdd to add geospatial location.
GeoLocation = red.GeoLocation
// GeoRadiusQuery is used with GeoRadius to query geospatial index.
GeoRadiusQuery = red.GeoRadiusQuery
GeoPos = red.GeoPos
Pipeliner = red.Pipeliner
// Z represents sorted set member.
@@ -173,6 +179,107 @@ func (s *Redis) Expireat(key string, expireTime int64) error {
}, acceptable)
}
func (s *Redis) GeoAdd(key string, geoLocation ...*GeoLocation) (val int64, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
return err
}
if v, err := conn.GeoAdd(key, geoLocation...).Result(); err != nil {
return err
} else {
val = v
return nil
}
}, acceptable)
return
}
func (s *Redis) GeoDist(key string, member1, member2, unit string) (val float64, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
return err
}
if v, err := conn.GeoDist(key, member1, member2, unit).Result(); err != nil {
return err
} else {
val = v
return nil
}
}, acceptable)
return
}
func (s *Redis) GeoHash(key string, members ...string) (val []string, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
return err
}
if v, err := conn.GeoHash(key, members...).Result(); err != nil {
return err
} else {
val = v
return nil
}
}, acceptable)
return
}
func (s *Redis) GeoRadius(key string, longitude, latitude float64, query *GeoRadiusQuery) (val []GeoLocation, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
return err
}
if v, err := conn.GeoRadius(key, longitude, latitude, query).Result(); err != nil {
return err
} else {
val = v
return nil
}
}, acceptable)
return
}
func (s *Redis) GeoRadiusByMember(key, member string, query *GeoRadiusQuery) (val []GeoLocation, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
return err
}
if v, err := conn.GeoRadiusByMember(key, member, query).Result(); err != nil {
return err
} else {
val = v
return nil
}
}, acceptable)
return
}
func (s *Redis) GeoPos(key string, members ...string) (val []*GeoPos, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
return err
}
if v, err := conn.GeoPos(key, members...).Result(); err != nil {
return err
} else {
val = v
return nil
}
}, acceptable)
return
}
func (s *Redis) Get(key string) (val string, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)

View File

@@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/alicebob/miniredis"
"github.com/alicebob/miniredis/v2"
red "github.com/go-redis/redis"
"github.com/stretchr/testify/assert"
)
@@ -556,7 +556,7 @@ func TestRedis_SortedSet(t *testing.T) {
val, err = client.Zscore("key", "value1")
assert.Nil(t, err)
assert.Equal(t, int64(5), val)
val, err = NewRedis(client.Addr, "").Zadds("key")
_, err = NewRedis(client.Addr, "").Zadds("key")
assert.NotNil(t, err)
val, err = client.Zadds("key", Pair{
Key: "value2",
@@ -567,9 +567,9 @@ func TestRedis_SortedSet(t *testing.T) {
})
assert.Nil(t, err)
assert.Equal(t, int64(2), val)
pairs, err := NewRedis(client.Addr, "").ZRevRangeWithScores("key", 1, 3)
_, err = NewRedis(client.Addr, "").ZRevRangeWithScores("key", 1, 3)
assert.NotNil(t, err)
pairs, err = client.ZRevRangeWithScores("key", 1, 3)
pairs, err := client.ZRevRangeWithScores("key", 1, 3)
assert.Nil(t, err)
assert.EqualValues(t, []Pair{
{
@@ -816,6 +816,38 @@ func TestRedisBlpopEx(t *testing.T) {
})
}
func TestRedisGeo(t *testing.T) {
runOnRedis(t, func(client *Redis) {
client.Ping()
var geoLocation = []*GeoLocation{{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"}, {Longitude: 15.087269, Latitude: 37.502669, Name: "Catania"}}
v, err := client.GeoAdd("sicily", geoLocation...)
assert.Nil(t, err)
assert.Equal(t, int64(2), v)
v2, err := client.GeoDist("sicily", "Palermo", "Catania", "m")
assert.Nil(t, err)
assert.Equal(t, 166274, int(v2))
// GeoHash not support
v3, err := client.GeoPos("sicily", "Palermo", "Catania")
assert.Nil(t, err)
assert.Equal(t, int64(v3[0].Longitude), int64(13))
assert.Equal(t, int64(v3[0].Latitude), int64(38))
assert.Equal(t, int64(v3[1].Longitude), int64(15))
assert.Equal(t, int64(v3[1].Latitude), int64(37))
v4, err := client.GeoRadius("sicily", 15, 37, &red.GeoRadiusQuery{WithDist: true, Unit: "km", Radius: 200})
assert.Nil(t, err)
assert.Equal(t, int64(v4[0].Dist), int64(190))
assert.Equal(t, int64(v4[1].Dist), int64(56))
var geoLocation2 = []*GeoLocation{{Longitude: 13.583333, Latitude: 37.316667, Name: "Agrigento"}}
v5, err := client.GeoAdd("sicily", geoLocation2...)
assert.Nil(t, err)
assert.Equal(t, int64(1), v5)
v6, err := client.GeoRadiusByMember("sicily", "Agrigento", &red.GeoRadiusQuery{Unit: "km", Radius: 100})
assert.Nil(t, err)
assert.Equal(t, v6[0].Name, "Agrigento")
assert.Equal(t, v6[1].Name, "Palermo")
})
}
func runOnRedis(t *testing.T, fn func(client *Redis)) {
s, err := miniredis.Run()
assert.Nil(t, err)

View File

@@ -0,0 +1,28 @@
package redistest
import (
"time"
"github.com/alicebob/miniredis/v2"
"github.com/tal-tech/go-zero/core/lang"
"github.com/tal-tech/go-zero/core/stores/redis"
)
func CreateRedis() (r *redis.Redis, clean func(), err error) {
mr, err := miniredis.Run()
if err != nil {
return nil, nil, err
}
return redis.NewRedis(mr.Addr(), redis.NodeType), func() {
ch := make(chan lang.PlaceholderType)
go func() {
mr.Close()
close(ch)
}()
select {
case <-ch:
case <-time.After(time.Second):
}
}, nil
}

View File

@@ -14,13 +14,14 @@ import (
"testing"
"time"
"github.com/alicebob/miniredis"
"github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/lang"
"github.com/tal-tech/go-zero/core/fx"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stat"
"github.com/tal-tech/go-zero/core/stores/cache"
"github.com/tal-tech/go-zero/core/stores/redis"
"github.com/tal-tech/go-zero/core/stores/redis/redistest"
"github.com/tal-tech/go-zero/core/stores/sqlx"
)
@@ -31,16 +32,15 @@ func init() {
func TestCachedConn_GetCache(t *testing.T) {
resetStats()
s, clean, err := createMiniRedis()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(dummySqlConn{}, r, cache.WithExpiry(time.Second*10))
var value string
err = c.GetCache("any", &value)
assert.Equal(t, ErrNotFound, err)
s.Set("any", `"value"`)
r.Set("any", `"value"`)
err = c.GetCache("any", &value)
assert.Nil(t, err)
assert.Equal(t, "value", value)
@@ -48,11 +48,10 @@ func TestCachedConn_GetCache(t *testing.T) {
func TestStat(t *testing.T) {
resetStats()
s, clean, err := createMiniRedis()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(dummySqlConn{}, r, cache.WithExpiry(time.Second*10))
for i := 0; i < 10; i++ {
@@ -72,15 +71,14 @@ func TestStat(t *testing.T) {
func TestCachedConn_QueryRowIndex_NoCache(t *testing.T) {
resetStats()
s, clean, err := createMiniRedis()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewConn(dummySqlConn{}, cache.CacheConf{
{
RedisConf: redis.RedisConf{
Host: s.Addr(),
Host: r.Addr,
Type: redis.NodeType,
},
Weight: 100,
@@ -122,11 +120,10 @@ func TestCachedConn_QueryRowIndex_NoCache(t *testing.T) {
func TestCachedConn_QueryRowIndex_HasCache(t *testing.T) {
resetStats()
s, clean, err := createMiniRedis()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(dummySqlConn{}, r, cache.WithExpiry(time.Second*10),
cache.WithNotFoundExpiry(time.Second))
@@ -210,16 +207,13 @@ func TestCachedConn_QueryRowIndex_HasCache_IntPrimary(t *testing.T) {
},
}
s, clean, err := createMiniRedis()
assert.Nil(t, err)
defer clean()
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
resetStats()
s.FlushAll()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(dummySqlConn{}, r, cache.WithExpiry(time.Second*10),
cache.WithNotFoundExpiry(time.Second))
@@ -256,11 +250,10 @@ func TestCachedConn_QueryRowIndex_HasWrongCache(t *testing.T) {
for k, v := range caches {
t.Run(k+"/"+v, func(t *testing.T) {
resetStats()
s, clean, err := createMiniRedis()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(dummySqlConn{}, r, cache.WithExpiry(time.Second*10),
cache.WithNotFoundExpiry(time.Second))
@@ -312,11 +305,10 @@ func TestStatCacheFails(t *testing.T) {
func TestStatDbFails(t *testing.T) {
resetStats()
s, clean, err := createMiniRedis()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(dummySqlConn{}, r, cache.WithExpiry(time.Second*10))
for i := 0; i < 20; i++ {
@@ -334,11 +326,10 @@ func TestStatDbFails(t *testing.T) {
func TestStatFromMemory(t *testing.T) {
resetStats()
s, clean, err := createMiniRedis()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(dummySqlConn{}, r, cache.WithExpiry(time.Second*10))
var all sync.WaitGroup
@@ -393,7 +384,7 @@ func TestStatFromMemory(t *testing.T) {
}
func TestCachedConnQueryRow(t *testing.T) {
s, clean, err := createMiniRedis()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
@@ -404,7 +395,6 @@ func TestCachedConnQueryRow(t *testing.T) {
var conn trackedConn
var user string
var ran bool
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(&conn, r, cache.WithExpiry(time.Second*30))
err = c.QueryRow(&user, key, func(conn sqlx.SqlConn, v interface{}) error {
ran = true
@@ -412,7 +402,7 @@ func TestCachedConnQueryRow(t *testing.T) {
return nil
})
assert.Nil(t, err)
actualValue, err := s.Get(key)
actualValue, err := r.Get(key)
assert.Nil(t, err)
var actual string
assert.Nil(t, json.Unmarshal([]byte(actualValue), &actual))
@@ -422,7 +412,7 @@ func TestCachedConnQueryRow(t *testing.T) {
}
func TestCachedConnQueryRowFromCache(t *testing.T) {
s, clean, err := createMiniRedis()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
@@ -433,7 +423,6 @@ func TestCachedConnQueryRowFromCache(t *testing.T) {
var conn trackedConn
var user string
var ran bool
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(&conn, r, cache.WithExpiry(time.Second*30))
assert.Nil(t, c.SetCache(key, value))
err = c.QueryRow(&user, key, func(conn sqlx.SqlConn, v interface{}) error {
@@ -442,7 +431,7 @@ func TestCachedConnQueryRowFromCache(t *testing.T) {
return nil
})
assert.Nil(t, err)
actualValue, err := s.Get(key)
actualValue, err := r.Get(key)
assert.Nil(t, err)
var actual string
assert.Nil(t, json.Unmarshal([]byte(actualValue), &actual))
@@ -452,7 +441,7 @@ func TestCachedConnQueryRowFromCache(t *testing.T) {
}
func TestQueryRowNotFound(t *testing.T) {
s, clean, err := createMiniRedis()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
@@ -460,7 +449,6 @@ func TestQueryRowNotFound(t *testing.T) {
var conn trackedConn
var user string
var ran int
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(&conn, r, cache.WithExpiry(time.Second*30))
for i := 0; i < 20; i++ {
err = c.QueryRow(&user, key, func(conn sqlx.SqlConn, v interface{}) error {
@@ -473,12 +461,11 @@ func TestQueryRowNotFound(t *testing.T) {
}
func TestCachedConnExec(t *testing.T) {
s, clean, err := createMiniRedis()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
var conn trackedConn
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(&conn, r, cache.WithExpiry(time.Second*10))
_, err = c.ExecNoCache("delete from user_table where id='kevin'")
assert.Nil(t, err)
@@ -486,24 +473,26 @@ func TestCachedConnExec(t *testing.T) {
}
func TestCachedConnExecDropCache(t *testing.T) {
s, clean, err := createMiniRedis()
r, err := miniredis.Run()
assert.Nil(t, err)
defer clean()
defer fx.DoWithTimeout(func() error {
r.Close()
return nil
}, time.Second)
const (
key = "user"
value = "any"
)
var conn trackedConn
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(&conn, r, cache.WithExpiry(time.Second*30))
c := NewNodeConn(&conn, redis.NewRedis(r.Addr(), redis.NodeType), cache.WithExpiry(time.Second*30))
assert.Nil(t, c.SetCache(key, value))
_, err = c.Exec(func(conn sqlx.SqlConn) (result sql.Result, e error) {
return conn.Exec("delete from user_table where id='kevin'")
}, key)
assert.Nil(t, err)
assert.True(t, conn.execValue)
_, err = s.Get(key)
_, err = r.Get(key)
assert.Exactly(t, miniredis.ErrKeyNotFound, err)
_, err = c.Exec(func(conn sqlx.SqlConn) (result sql.Result, e error) {
return nil, errors.New("foo")
@@ -524,12 +513,11 @@ func TestCachedConnExecDropCacheFailed(t *testing.T) {
}
func TestCachedConnQueryRows(t *testing.T) {
s, clean, err := createMiniRedis()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
var conn trackedConn
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(&conn, r, cache.WithExpiry(time.Second*10))
var users []string
err = c.QueryRowsNoCache(&users, "select user from user_table where id='kevin'")
@@ -538,12 +526,11 @@ func TestCachedConnQueryRows(t *testing.T) {
}
func TestCachedConnTransact(t *testing.T) {
s, clean, err := createMiniRedis()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
var conn trackedConn
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(&conn, r, cache.WithExpiry(time.Second*10))
err = c.Transact(func(session sqlx.Session) error {
return nil
@@ -553,7 +540,7 @@ func TestCachedConnTransact(t *testing.T) {
}
func TestQueryRowNoCache(t *testing.T) {
s, clean, err := createMiniRedis()
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
@@ -563,7 +550,6 @@ func TestQueryRowNoCache(t *testing.T) {
)
var user string
var ran bool
r := redis.NewRedis(s.Addr(), redis.NodeType)
conn := dummySqlConn{queryRow: func(v interface{}, q string, args ...interface{}) error {
user = value
ran = true
@@ -639,22 +625,3 @@ func (c *trackedConn) Transact(fn func(session sqlx.Session) error) error {
c.transactValue = true
return c.dummySqlConn.Transact(fn)
}
func createMiniRedis() (r *miniredis.Miniredis, clean func(), err error) {
r, err = miniredis.Run()
if err != nil {
return nil, nil, err
}
return r, func() {
ch := make(chan lang.PlaceholderType)
go func() {
r.Close()
close(ch)
}()
select {
case <-ch:
case <-time.After(time.Second):
}
}, nil
}

View File

@@ -33,7 +33,7 @@ func NewSharedCalls() SharedCalls {
}
func (g *sharedGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
c, done := g.createCall(key, fn)
c, done := g.createCall(key)
if done {
return c.val, c.err
}
@@ -43,7 +43,7 @@ func (g *sharedGroup) Do(key string, fn func() (interface{}, error)) (interface{
}
func (g *sharedGroup) DoEx(key string, fn func() (interface{}, error)) (val interface{}, fresh bool, err error) {
c, done := g.createCall(key, fn)
c, done := g.createCall(key)
if done {
return c.val, false, c.err
}
@@ -52,7 +52,7 @@ func (g *sharedGroup) DoEx(key string, fn func() (interface{}, error)) (val inte
return c.val, true, c.err
}
func (g *sharedGroup) createCall(key string, fn func() (interface{}, error)) (c *call, done bool) {
func (g *sharedGroup) createCall(key string) (c *call, done bool) {
g.lock.Lock()
if c, ok := g.calls[key]; ok {
g.lock.Unlock()
@@ -70,8 +70,6 @@ func (g *sharedGroup) createCall(key string, fn func() (interface{}, error)) (c
func (g *sharedGroup) makeCall(c *call, key string, fn func() (interface{}, error)) {
defer func() {
// delete key first, done later. can't reverse the order, because if reverse,
// another Do call might wg.Wait() without get notified with wg.Done()
g.lock.Lock()
delete(g.calls, key)
g.lock.Unlock()

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

BIN
doc/images/architecture.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

BIN
doc/images/benchmark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
doc/images/go-zero.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

BIN
doc/images/resilience.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

View File

@@ -1,33 +1,29 @@
type (
addReq struct {
book string `form:"book"`
price int64 `form:"price"`
}
addResp struct {
ok bool `json:"ok"`
}
addReq {
book string `form:"book"`
price int64 `form:"price"`
}
addResp {
ok bool `json:"ok"`
}
)
type (
checkReq struct {
book string `form:"book"`
}
checkResp struct {
found bool `json:"found"`
price int64 `json:"price"`
}
checkReq {
book string `form:"book"`
}
checkResp {
found bool `json:"found"`
price int64 `json:"price"`
}
)
service bookstore-api {
@server(
handler: AddHandler
)
get /add (addReq) returns (addResp)
@server(
handler: CheckHandler
)
get /check (checkReq) returns (checkResp)
@handler AddHandler
get /add (addReq) returns (addResp)
@handler CheckHandler
get /check (checkReq) returns (checkResp)
}

View File

@@ -10,7 +10,7 @@ import (
"github.com/tal-tech/go-zero/rest/httpx"
)
func addHandler(ctx *svc.ServiceContext) http.HandlerFunc {
func AddHandler(ctx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AddReq
if err := httpx.Parse(r, &req); err != nil {

View File

@@ -10,7 +10,7 @@ import (
"github.com/tal-tech/go-zero/rest/httpx"
)
func checkHandler(ctx *svc.ServiceContext) http.HandlerFunc {
func CheckHandler(ctx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CheckReq
if err := httpx.Parse(r, &req); err != nil {

View File

@@ -10,16 +10,18 @@ import (
)
func RegisterHandlers(engine *rest.Server, serverCtx *svc.ServiceContext) {
engine.AddRoutes([]rest.Route{
{
Method: http.MethodGet,
Path: "/add",
Handler: addHandler(serverCtx),
engine.AddRoutes(
[]rest.Route{
{
Method: http.MethodGet,
Path: "/add",
Handler: AddHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/check",
Handler: CheckHandler(serverCtx),
},
},
{
Method: http.MethodGet,
Path: "/check",
Handler: checkHandler(serverCtx),
},
})
)
}

View File

@@ -29,7 +29,8 @@ func (l *CheckLogic) Check(req types.CheckReq) (*types.CheckResp, error) {
Book: req.Book,
})
if err != nil {
return nil, err
logx.Error(err)
return &types.CheckResp{}, err
}
return &types.CheckResp{

View File

@@ -5,7 +5,8 @@ go 1.15
require (
github.com/golang/mock v1.4.3
github.com/golang/protobuf v1.4.2
github.com/tal-tech/go-zero v1.0.16
github.com/tal-tech/go-zero v1.0.27
golang.org/x/net v0.0.0-20200707034311-ab3426394381
google.golang.org/grpc v1.29.1
google.golang.org/protobuf v1.25.0
)

View File

@@ -36,6 +36,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28=
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -46,6 +47,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
github.com/dsymonds/gotoc v0.0.0-20160928043926-5aebcfc91819/go.mod h1:MvzMVHq8BH2Ji/o8TGDocVA70byvLrAgFTxkEnmjO4Y=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/emicklei/proto v1.9.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -122,9 +124,11 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY=
github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
@@ -175,6 +179,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -210,6 +215,7 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@@ -232,11 +238,14 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tal-tech/go-zero v1.0.16 h1:oT7sOFftEUdD/XcXF0xEugX9yhnw4DcQkeMNFLi5KO8=
github.com/tal-tech/go-zero v1.0.16/go.mod h1:y2wBHTkxNJw79K9/wCSeDKzv2pCT6x45oOmXEsJdQK8=
github.com/tal-tech/go-zero v1.0.27 h1:QMIbaTxibMc/OsO5RTAuKZ8ndbl2dGN6pITQEtp2x/A=
github.com/tal-tech/go-zero v1.0.27/go.mod h1:JtNXlsh/CgeIHyQnt5C5M2IcSevW7V0NAnqO93TQgm8=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
@@ -382,6 +391,7 @@ gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@@ -393,6 +403,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

View File

@@ -7,10 +7,10 @@ import (
"flag"
"fmt"
"bookstore/rpc/add/add"
"bookstore/rpc/add/internal/config"
"bookstore/rpc/add/internal/server"
"bookstore/rpc/add/internal/svc"
add "bookstore/rpc/add/pb"
"github.com/tal-tech/go-zero/core/conf"
"github.com/tal-tech/go-zero/core/logx"

View File

@@ -0,0 +1,305 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: add.proto
package add
import (
context "context"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type AddReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Book string `protobuf:"bytes,1,opt,name=book,proto3" json:"book,omitempty"`
Price int64 `protobuf:"varint,2,opt,name=price,proto3" json:"price,omitempty"`
}
func (x *AddReq) Reset() {
*x = AddReq{}
if protoimpl.UnsafeEnabled {
mi := &file_add_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AddReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AddReq) ProtoMessage() {}
func (x *AddReq) ProtoReflect() protoreflect.Message {
mi := &file_add_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AddReq.ProtoReflect.Descriptor instead.
func (*AddReq) Descriptor() ([]byte, []int) {
return file_add_proto_rawDescGZIP(), []int{0}
}
func (x *AddReq) GetBook() string {
if x != nil {
return x.Book
}
return ""
}
func (x *AddReq) GetPrice() int64 {
if x != nil {
return x.Price
}
return 0
}
type AddResp struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Ok bool `protobuf:"varint,1,opt,name=ok,proto3" json:"ok,omitempty"`
}
func (x *AddResp) Reset() {
*x = AddResp{}
if protoimpl.UnsafeEnabled {
mi := &file_add_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AddResp) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AddResp) ProtoMessage() {}
func (x *AddResp) ProtoReflect() protoreflect.Message {
mi := &file_add_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AddResp.ProtoReflect.Descriptor instead.
func (*AddResp) Descriptor() ([]byte, []int) {
return file_add_proto_rawDescGZIP(), []int{1}
}
func (x *AddResp) GetOk() bool {
if x != nil {
return x.Ok
}
return false
}
var File_add_proto protoreflect.FileDescriptor
var file_add_proto_rawDesc = []byte{
0x0a, 0x09, 0x61, 0x64, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x64, 0x64,
0x22, 0x32, 0x0a, 0x06, 0x61, 0x64, 0x64, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f,
0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x14,
0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x70,
0x72, 0x69, 0x63, 0x65, 0x22, 0x19, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12,
0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x32,
0x29, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x03, 0x61, 0x64, 0x64, 0x12,
0x0b, 0x2e, 0x61, 0x64, 0x64, 0x2e, 0x61, 0x64, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x61,
0x64, 0x64, 0x2e, 0x61, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (
file_add_proto_rawDescOnce sync.Once
file_add_proto_rawDescData = file_add_proto_rawDesc
)
func file_add_proto_rawDescGZIP() []byte {
file_add_proto_rawDescOnce.Do(func() {
file_add_proto_rawDescData = protoimpl.X.CompressGZIP(file_add_proto_rawDescData)
})
return file_add_proto_rawDescData
}
var file_add_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_add_proto_goTypes = []interface{}{
(*AddReq)(nil), // 0: add.addReq
(*AddResp)(nil), // 1: add.addResp
}
var file_add_proto_depIdxs = []int32{
0, // 0: add.adder.add:input_type -> add.addReq
1, // 1: add.adder.add:output_type -> add.addResp
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_add_proto_init() }
func file_add_proto_init() {
if File_add_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_add_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AddReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_add_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AddResp); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_add_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_add_proto_goTypes,
DependencyIndexes: file_add_proto_depIdxs,
MessageInfos: file_add_proto_msgTypes,
}.Build()
File_add_proto = out.File
file_add_proto_rawDesc = nil
file_add_proto_goTypes = nil
file_add_proto_depIdxs = nil
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConnInterface
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion6
// AdderClient is the client API for Adder service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type AdderClient interface {
Add(ctx context.Context, in *AddReq, opts ...grpc.CallOption) (*AddResp, error)
}
type adderClient struct {
cc grpc.ClientConnInterface
}
func NewAdderClient(cc grpc.ClientConnInterface) AdderClient {
return &adderClient{cc}
}
func (c *adderClient) Add(ctx context.Context, in *AddReq, opts ...grpc.CallOption) (*AddResp, error) {
out := new(AddResp)
err := c.cc.Invoke(ctx, "/add.adder/add", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// AdderServer is the server API for Adder service.
type AdderServer interface {
Add(context.Context, *AddReq) (*AddResp, error)
}
// UnimplementedAdderServer can be embedded to have forward compatible implementations.
type UnimplementedAdderServer struct {
}
func (*UnimplementedAdderServer) Add(context.Context, *AddReq) (*AddResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method Add not implemented")
}
func RegisterAdderServer(s *grpc.Server, srv AdderServer) {
s.RegisterService(&_Adder_serviceDesc, srv)
}
func _Adder_Add_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AdderServer).Add(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/add.adder/Add",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AdderServer).Add(ctx, req.(*AddReq))
}
return interceptor(ctx, in, info, handler)
}
var _Adder_serviceDesc = grpc.ServiceDesc{
ServiceName: "add.adder",
HandlerType: (*AdderServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "add",
Handler: _Adder_Add_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "add.proto",
}

View File

@@ -8,13 +8,15 @@ package adder
import (
"context"
add "bookstore/rpc/add/pb"
"bookstore/rpc/add/add"
"github.com/tal-tech/go-zero/core/jsonx"
"github.com/tal-tech/go-zero/zrpc"
)
type (
AddReq = add.AddReq
AddResp = add.AddResp
Adder interface {
Add(ctx context.Context, in *AddReq) (*AddResp, error)
}
@@ -31,33 +33,6 @@ func NewAdder(cli zrpc.Client) Adder {
}
func (m *defaultAdder) Add(ctx context.Context, in *AddReq) (*AddResp, error) {
var request add.AddReq
bts, err := jsonx.Marshal(in)
if err != nil {
return nil, errJsonConvert
}
err = jsonx.Unmarshal(bts, &request)
if err != nil {
return nil, errJsonConvert
}
client := add.NewAdderClient(m.cli.Conn())
resp, err := client.Add(ctx, &request)
if err != nil {
return nil, err
}
var ret AddResp
bts, err = jsonx.Marshal(resp)
if err != nil {
return nil, errJsonConvert
}
err = jsonx.Unmarshal(bts, &ret)
if err != nil {
return nil, errJsonConvert
}
return &ret, nil
return client.Add(ctx, in)
}

View File

@@ -1,49 +0,0 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: adder.go
// Package adder is a generated GoMock package.
package adder
import (
context "context"
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)
// MockAdder is a mock of Adder interface
type MockAdder struct {
ctrl *gomock.Controller
recorder *MockAdderMockRecorder
}
// MockAdderMockRecorder is the mock recorder for MockAdder
type MockAdderMockRecorder struct {
mock *MockAdder
}
// NewMockAdder creates a new mock instance
func NewMockAdder(ctrl *gomock.Controller) *MockAdder {
mock := &MockAdder{ctrl: ctrl}
mock.recorder = &MockAdderMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockAdder) EXPECT() *MockAdderMockRecorder {
return m.recorder
}
// Add mocks base method
func (m *MockAdder) Add(ctx context.Context, in *AddReq) (*AddResp, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Add", ctx, in)
ret0, _ := ret[0].(*AddResp)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Add indicates an expected call of Add
func (mr *MockAdderMockRecorder) Add(ctx, in interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockAdder)(nil).Add), ctx, in)
}

View File

@@ -1,19 +0,0 @@
// Code generated by goctl. DO NOT EDIT!
// Source: add.proto
package adder
import "errors"
var errJsonConvert = errors.New("json convert error")
type (
AddReq struct {
Book string `json:"book,omitempty"`
Price int64 `json:"price,omitempty"`
}
AddResp struct {
Ok bool `json:"ok,omitempty"`
}
)

View File

@@ -8,6 +8,5 @@ import (
type Config struct {
zrpc.RpcServerConf
DataSource string
Table string
Cache cache.CacheConf
}

View File

@@ -3,8 +3,8 @@ package logic
import (
"context"
add "bookstore/rpc/add/adder"
"bookstore/rpc/add/internal/svc"
add "bookstore/rpc/add/pb"
"bookstore/rpc/model"
"github.com/tal-tech/go-zero/core/logx"

View File

@@ -6,9 +6,9 @@ package server
import (
"context"
"bookstore/rpc/add/add"
"bookstore/rpc/add/internal/logic"
"bookstore/rpc/add/internal/svc"
add "bookstore/rpc/add/pb"
)
type AdderServer struct {

View File

@@ -9,12 +9,12 @@ import (
type ServiceContext struct {
c config.Config
Model *model.BookModel
Model model.BookModel
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
c: c,
Model: model.NewBookModel(sqlx.NewMysql(c.DataSource), c.Cache, c.Table),
c: c,
Model: model.NewBookModel(sqlx.NewMysql(c.DataSource), c.Cache),
}
}
}

View File

@@ -1,167 +0,0 @@
// Code generated by protoc-gen-go.
// source: add.proto
// DO NOT EDIT!
/*
Package add is a generated protocol buffer package.
It is generated from these files:
add.proto
It has these top-level messages:
AddReq
AddResp
*/
package add
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type AddReq struct {
Book string `protobuf:"bytes,1,opt,name=book" json:"book,omitempty"`
Price int64 `protobuf:"varint,2,opt,name=price" json:"price,omitempty"`
}
func (m *AddReq) Reset() { *m = AddReq{} }
func (m *AddReq) String() string { return proto.CompactTextString(m) }
func (*AddReq) ProtoMessage() {}
func (*AddReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *AddReq) GetBook() string {
if m != nil {
return m.Book
}
return ""
}
func (m *AddReq) GetPrice() int64 {
if m != nil {
return m.Price
}
return 0
}
type AddResp struct {
Ok bool `protobuf:"varint,1,opt,name=ok" json:"ok,omitempty"`
}
func (m *AddResp) Reset() { *m = AddResp{} }
func (m *AddResp) String() string { return proto.CompactTextString(m) }
func (*AddResp) ProtoMessage() {}
func (*AddResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *AddResp) GetOk() bool {
if m != nil {
return m.Ok
}
return false
}
func init() {
proto.RegisterType((*AddReq)(nil), "add.addReq")
proto.RegisterType((*AddResp)(nil), "add.addResp")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for Adder service
type AdderClient interface {
Add(ctx context.Context, in *AddReq, opts ...grpc.CallOption) (*AddResp, error)
}
type adderClient struct {
cc *grpc.ClientConn
}
func NewAdderClient(cc *grpc.ClientConn) AdderClient {
return &adderClient{cc}
}
func (c *adderClient) Add(ctx context.Context, in *AddReq, opts ...grpc.CallOption) (*AddResp, error) {
out := new(AddResp)
err := grpc.Invoke(ctx, "/add.adder/add", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Adder service
type AdderServer interface {
Add(context.Context, *AddReq) (*AddResp, error)
}
func RegisterAdderServer(s *grpc.Server, srv AdderServer) {
s.RegisterService(&_Adder_serviceDesc, srv)
}
func _Adder_Add_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AdderServer).Add(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/add.adder/Add",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AdderServer).Add(ctx, req.(*AddReq))
}
return interceptor(ctx, in, info, handler)
}
var _Adder_serviceDesc = grpc.ServiceDesc{
ServiceName: "add.adder",
HandlerType: (*AdderServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "add",
Handler: _Adder_Add_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "add.proto",
}
func init() { proto.RegisterFile("add.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 136 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x4c, 0x4c, 0x49, 0xd1,
0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x4e, 0x4c, 0x49, 0x51, 0x32, 0xe2, 0x62, 0x4b, 0x4c,
0x49, 0x09, 0x4a, 0x2d, 0x14, 0x12, 0xe2, 0x62, 0x49, 0xca, 0xcf, 0xcf, 0x96, 0x60, 0x54, 0x60,
0xd4, 0xe0, 0x0c, 0x02, 0xb3, 0x85, 0x44, 0xb8, 0x58, 0x0b, 0x8a, 0x32, 0x93, 0x53, 0x25, 0x98,
0x14, 0x18, 0x35, 0x98, 0x83, 0x20, 0x1c, 0x25, 0x49, 0x2e, 0x76, 0xb0, 0x9e, 0xe2, 0x02, 0x21,
0x3e, 0x2e, 0x26, 0xa8, 0x16, 0x8e, 0x20, 0xa6, 0xfc, 0x6c, 0x23, 0x4d, 0x2e, 0xd6, 0xc4, 0x94,
0x94, 0xd4, 0x22, 0x21, 0x05, 0x2e, 0x90, 0xf1, 0x42, 0xdc, 0x7a, 0x20, 0xfb, 0x20, 0x36, 0x48,
0xf1, 0x20, 0x38, 0xc5, 0x05, 0x49, 0x6c, 0x60, 0x57, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff,
0xe2, 0x6d, 0xb5, 0x91, 0x92, 0x00, 0x00, 0x00,
}

View File

@@ -7,10 +7,10 @@ import (
"flag"
"fmt"
"bookstore/rpc/check/check"
"bookstore/rpc/check/internal/config"
"bookstore/rpc/check/internal/server"
"bookstore/rpc/check/internal/svc"
check "bookstore/rpc/check/pb"
"github.com/tal-tech/go-zero/core/conf"
"github.com/tal-tech/go-zero/core/logx"

View File

@@ -0,0 +1,306 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: check.proto
package check
import (
context "context"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type CheckReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Book string `protobuf:"bytes,1,opt,name=book,proto3" json:"book,omitempty"`
}
func (x *CheckReq) Reset() {
*x = CheckReq{}
if protoimpl.UnsafeEnabled {
mi := &file_check_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CheckReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CheckReq) ProtoMessage() {}
func (x *CheckReq) ProtoReflect() protoreflect.Message {
mi := &file_check_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CheckReq.ProtoReflect.Descriptor instead.
func (*CheckReq) Descriptor() ([]byte, []int) {
return file_check_proto_rawDescGZIP(), []int{0}
}
func (x *CheckReq) GetBook() string {
if x != nil {
return x.Book
}
return ""
}
type CheckResp struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Found bool `protobuf:"varint,1,opt,name=found,proto3" json:"found,omitempty"`
Price int64 `protobuf:"varint,2,opt,name=price,proto3" json:"price,omitempty"`
}
func (x *CheckResp) Reset() {
*x = CheckResp{}
if protoimpl.UnsafeEnabled {
mi := &file_check_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CheckResp) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CheckResp) ProtoMessage() {}
func (x *CheckResp) ProtoReflect() protoreflect.Message {
mi := &file_check_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CheckResp.ProtoReflect.Descriptor instead.
func (*CheckResp) Descriptor() ([]byte, []int) {
return file_check_proto_rawDescGZIP(), []int{1}
}
func (x *CheckResp) GetFound() bool {
if x != nil {
return x.Found
}
return false
}
func (x *CheckResp) GetPrice() int64 {
if x != nil {
return x.Price
}
return 0
}
var File_check_proto protoreflect.FileDescriptor
var file_check_proto_rawDesc = []byte{
0x0a, 0x0b, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x63,
0x68, 0x65, 0x63, 0x6b, 0x22, 0x1e, 0x0a, 0x08, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71,
0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x62, 0x6f, 0x6f, 0x6b, 0x22, 0x37, 0x0a, 0x09, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73,
0x70, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x32, 0x35, 0x0a,
0x07, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x05, 0x63, 0x68, 0x65, 0x63,
0x6b, 0x12, 0x0f, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52,
0x65, 0x71, 0x1a, 0x10, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b,
0x52, 0x65, 0x73, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_check_proto_rawDescOnce sync.Once
file_check_proto_rawDescData = file_check_proto_rawDesc
)
func file_check_proto_rawDescGZIP() []byte {
file_check_proto_rawDescOnce.Do(func() {
file_check_proto_rawDescData = protoimpl.X.CompressGZIP(file_check_proto_rawDescData)
})
return file_check_proto_rawDescData
}
var file_check_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_check_proto_goTypes = []interface{}{
(*CheckReq)(nil), // 0: check.checkReq
(*CheckResp)(nil), // 1: check.checkResp
}
var file_check_proto_depIdxs = []int32{
0, // 0: check.checker.check:input_type -> check.checkReq
1, // 1: check.checker.check:output_type -> check.checkResp
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_check_proto_init() }
func file_check_proto_init() {
if File_check_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_check_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CheckReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_check_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CheckResp); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_check_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_check_proto_goTypes,
DependencyIndexes: file_check_proto_depIdxs,
MessageInfos: file_check_proto_msgTypes,
}.Build()
File_check_proto = out.File
file_check_proto_rawDesc = nil
file_check_proto_goTypes = nil
file_check_proto_depIdxs = nil
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConnInterface
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion6
// CheckerClient is the client API for Checker service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type CheckerClient interface {
Check(ctx context.Context, in *CheckReq, opts ...grpc.CallOption) (*CheckResp, error)
}
type checkerClient struct {
cc grpc.ClientConnInterface
}
func NewCheckerClient(cc grpc.ClientConnInterface) CheckerClient {
return &checkerClient{cc}
}
func (c *checkerClient) Check(ctx context.Context, in *CheckReq, opts ...grpc.CallOption) (*CheckResp, error) {
out := new(CheckResp)
err := c.cc.Invoke(ctx, "/check.checker/check", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// CheckerServer is the server API for Checker service.
type CheckerServer interface {
Check(context.Context, *CheckReq) (*CheckResp, error)
}
// UnimplementedCheckerServer can be embedded to have forward compatible implementations.
type UnimplementedCheckerServer struct {
}
func (*UnimplementedCheckerServer) Check(context.Context, *CheckReq) (*CheckResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method Check not implemented")
}
func RegisterCheckerServer(s *grpc.Server, srv CheckerServer) {
s.RegisterService(&_Checker_serviceDesc, srv)
}
func _Checker_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CheckReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CheckerServer).Check(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/check.checker/Check",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CheckerServer).Check(ctx, req.(*CheckReq))
}
return interceptor(ctx, in, info, handler)
}
var _Checker_serviceDesc = grpc.ServiceDesc{
ServiceName: "check.checker",
HandlerType: (*CheckerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "check",
Handler: _Checker_Check_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "check.proto",
}

View File

@@ -8,13 +8,15 @@ package checker
import (
"context"
check "bookstore/rpc/check/pb"
"bookstore/rpc/check/check"
"github.com/tal-tech/go-zero/core/jsonx"
"github.com/tal-tech/go-zero/zrpc"
)
type (
CheckReq = check.CheckReq
CheckResp = check.CheckResp
Checker interface {
Check(ctx context.Context, in *CheckReq) (*CheckResp, error)
}
@@ -31,33 +33,6 @@ func NewChecker(cli zrpc.Client) Checker {
}
func (m *defaultChecker) Check(ctx context.Context, in *CheckReq) (*CheckResp, error) {
var request check.CheckReq
bts, err := jsonx.Marshal(in)
if err != nil {
return nil, errJsonConvert
}
err = jsonx.Unmarshal(bts, &request)
if err != nil {
return nil, errJsonConvert
}
client := check.NewCheckerClient(m.cli.Conn())
resp, err := client.Check(ctx, &request)
if err != nil {
return nil, err
}
var ret CheckResp
bts, err = jsonx.Marshal(resp)
if err != nil {
return nil, errJsonConvert
}
err = jsonx.Unmarshal(bts, &ret)
if err != nil {
return nil, errJsonConvert
}
return &ret, nil
return client.Check(ctx, in)
}

View File

@@ -1,49 +0,0 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: checker.go
// Package checker is a generated GoMock package.
package checker
import (
context "context"
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)
// MockChecker is a mock of Checker interface
type MockChecker struct {
ctrl *gomock.Controller
recorder *MockCheckerMockRecorder
}
// MockCheckerMockRecorder is the mock recorder for MockChecker
type MockCheckerMockRecorder struct {
mock *MockChecker
}
// NewMockChecker creates a new mock instance
func NewMockChecker(ctrl *gomock.Controller) *MockChecker {
mock := &MockChecker{ctrl: ctrl}
mock.recorder = &MockCheckerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockChecker) EXPECT() *MockCheckerMockRecorder {
return m.recorder
}
// Check mocks base method
func (m *MockChecker) Check(ctx context.Context, in *CheckReq) (*CheckResp, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Check", ctx, in)
ret0, _ := ret[0].(*CheckResp)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Check indicates an expected call of Check
func (mr *MockCheckerMockRecorder) Check(ctx, in interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Check", reflect.TypeOf((*MockChecker)(nil).Check), ctx, in)
}

View File

@@ -1,19 +0,0 @@
// Code generated by goctl. DO NOT EDIT!
// Source: check.proto
package checker
import "errors"
var errJsonConvert = errors.New("json convert error")
type (
CheckReq struct {
Book string `json:"book,omitempty"`
}
CheckResp struct {
Found bool `json:"found,omitempty"`
Price int64 `json:"price,omitempty"`
}
)

View File

@@ -8,6 +8,5 @@ import (
type Config struct {
zrpc.RpcServerConf
DataSource string
Table string
Cache cache.CacheConf
}

View File

@@ -3,8 +3,8 @@ package logic
import (
"context"
check "bookstore/rpc/check/checker"
"bookstore/rpc/check/internal/svc"
check "bookstore/rpc/check/pb"
"github.com/tal-tech/go-zero/core/logx"
)

View File

@@ -6,9 +6,9 @@ package server
import (
"context"
"bookstore/rpc/check/check"
"bookstore/rpc/check/internal/logic"
"bookstore/rpc/check/internal/svc"
check "bookstore/rpc/check/pb"
)
type CheckerServer struct {

View File

@@ -9,12 +9,12 @@ import (
type ServiceContext struct {
c config.Config
Model *model.BookModel
Model model.BookModel
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
c: c,
Model: model.NewBookModel(sqlx.NewMysql(c.DataSource), c.Cache, c.Table),
c: c,
Model: model.NewBookModel(sqlx.NewMysql(c.DataSource), c.Cache),
}
}
}

View File

@@ -1,167 +0,0 @@
// Code generated by protoc-gen-go.
// source: check.proto
// DO NOT EDIT!
/*
Package check is a generated protocol buffer package.
It is generated from these files:
check.proto
It has these top-level messages:
CheckReq
CheckResp
*/
package check
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type CheckReq struct {
Book string `protobuf:"bytes,1,opt,name=book" json:"book,omitempty"`
}
func (m *CheckReq) Reset() { *m = CheckReq{} }
func (m *CheckReq) String() string { return proto.CompactTextString(m) }
func (*CheckReq) ProtoMessage() {}
func (*CheckReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *CheckReq) GetBook() string {
if m != nil {
return m.Book
}
return ""
}
type CheckResp struct {
Found bool `protobuf:"varint,1,opt,name=found" json:"found,omitempty"`
Price int64 `protobuf:"varint,2,opt,name=price" json:"price,omitempty"`
}
func (m *CheckResp) Reset() { *m = CheckResp{} }
func (m *CheckResp) String() string { return proto.CompactTextString(m) }
func (*CheckResp) ProtoMessage() {}
func (*CheckResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *CheckResp) GetFound() bool {
if m != nil {
return m.Found
}
return false
}
func (m *CheckResp) GetPrice() int64 {
if m != nil {
return m.Price
}
return 0
}
func init() {
proto.RegisterType((*CheckReq)(nil), "check.checkReq")
proto.RegisterType((*CheckResp)(nil), "check.checkResp")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for Checker service
type CheckerClient interface {
Check(ctx context.Context, in *CheckReq, opts ...grpc.CallOption) (*CheckResp, error)
}
type checkerClient struct {
cc *grpc.ClientConn
}
func NewCheckerClient(cc *grpc.ClientConn) CheckerClient {
return &checkerClient{cc}
}
func (c *checkerClient) Check(ctx context.Context, in *CheckReq, opts ...grpc.CallOption) (*CheckResp, error) {
out := new(CheckResp)
err := grpc.Invoke(ctx, "/check.checker/check", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Checker service
type CheckerServer interface {
Check(context.Context, *CheckReq) (*CheckResp, error)
}
func RegisterCheckerServer(s *grpc.Server, srv CheckerServer) {
s.RegisterService(&_Checker_serviceDesc, srv)
}
func _Checker_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CheckReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CheckerServer).Check(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/check.checker/Check",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CheckerServer).Check(ctx, req.(*CheckReq))
}
return interceptor(ctx, in, info, handler)
}
var _Checker_serviceDesc = grpc.ServiceDesc{
ServiceName: "check.checker",
HandlerType: (*CheckerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "check",
Handler: _Checker_Check_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "check.proto",
}
func init() { proto.RegisterFile("check.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 136 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x4e, 0xce, 0x48, 0x4d,
0xce, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x73, 0x94, 0xe4, 0xb8, 0x38, 0xc0,
0x8c, 0xa0, 0xd4, 0x42, 0x21, 0x21, 0x2e, 0x96, 0xa4, 0xfc, 0xfc, 0x6c, 0x09, 0x46, 0x05, 0x46,
0x0d, 0xce, 0x20, 0x30, 0x5b, 0xc9, 0x9c, 0x8b, 0x13, 0x2a, 0x5f, 0x5c, 0x20, 0x24, 0xc2, 0xc5,
0x9a, 0x96, 0x5f, 0x9a, 0x97, 0x02, 0x56, 0xc1, 0x11, 0x04, 0xe1, 0x80, 0x44, 0x0b, 0x8a, 0x32,
0x93, 0x53, 0x25, 0x98, 0x14, 0x18, 0x35, 0x98, 0x83, 0x20, 0x1c, 0x23, 0x53, 0x2e, 0x76, 0xb0,
0xc6, 0xd4, 0x22, 0x21, 0x2d, 0x2e, 0x88, 0x65, 0x42, 0xfc, 0x7a, 0x10, 0x17, 0xc0, 0x6c, 0x94,
0x12, 0x40, 0x15, 0x28, 0x2e, 0x48, 0x62, 0x03, 0xbb, 0xce, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff,
0x6e, 0x6f, 0xa7, 0x1d, 0xac, 0x00, 0x00, 0x00,
}

View File

@@ -18,11 +18,18 @@ var (
bookRowsExpectAutoSet = strings.Join(stringx.Remove(bookFieldNames, "create_time", "update_time"), ",")
bookRowsWithPlaceHolder = strings.Join(stringx.Remove(bookFieldNames, "book", "create_time", "update_time"), "=?,") + "=?"
cacheBookBookPrefix = "cache#Book#book#"
cacheBookPrefix = "cache#Book#book#"
)
type (
BookModel struct {
BookModel interface {
Insert(data Book) (sql.Result, error)
FindOne(book string) (*Book, error)
Update(data Book) error
Delete(book string) error
}
defaultBookModel struct {
sqlc.CachedConn
table string
}
@@ -33,23 +40,25 @@ type (
}
)
func NewBookModel(conn sqlx.SqlConn, c cache.CacheConf, table string) *BookModel {
return &BookModel{
func NewBookModel(conn sqlx.SqlConn, c cache.CacheConf) BookModel {
return &defaultBookModel{
CachedConn: sqlc.NewConn(conn, c),
table: table,
table: "book",
}
}
func (m *BookModel) Insert(data Book) (sql.Result, error) {
query := `insert into ` + m.table + ` (` + bookRowsExpectAutoSet + `) values (?, ?)`
return m.ExecNoCache(query, data.Book, data.Price)
func (m *defaultBookModel) Insert(data Book) (sql.Result, error) {
query := fmt.Sprintf("insert into %s (%s) values (?, ?)", m.table, bookRowsExpectAutoSet)
ret, err := m.ExecNoCache(query, data.Book, data.Price)
return ret, err
}
func (m *BookModel) FindOne(book string) (*Book, error) {
bookBookKey := fmt.Sprintf("%s%v", cacheBookBookPrefix, book)
func (m *defaultBookModel) FindOne(book string) (*Book, error) {
bookKey := fmt.Sprintf("%s%v", cacheBookPrefix, book)
var resp Book
err := m.QueryRow(&resp, bookBookKey, func(conn sqlx.SqlConn, v interface{}) error {
query := `select ` + bookRows + ` from ` + m.table + ` where book = ? limit 1`
err := m.QueryRow(&resp, bookKey, func(conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where book = ? limit 1", bookRows, m.table)
return conn.QueryRow(v, query, book)
})
switch err {
@@ -62,21 +71,30 @@ func (m *BookModel) FindOne(book string) (*Book, error) {
}
}
func (m *BookModel) Update(data Book) error {
bookBookKey := fmt.Sprintf("%s%v", cacheBookBookPrefix, data.Book)
func (m *defaultBookModel) Update(data Book) error {
bookKey := fmt.Sprintf("%s%v", cacheBookPrefix, data.Book)
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := `update ` + m.table + ` set ` + bookRowsWithPlaceHolder + ` where book = ?`
query := fmt.Sprintf("update %s set %s where book = ?", m.table, bookRowsWithPlaceHolder)
return conn.Exec(query, data.Price, data.Book)
}, bookBookKey)
}, bookKey)
return err
}
func (m *BookModel) Delete(book string) error {
func (m *defaultBookModel) Delete(book string) error {
bookBookKey := fmt.Sprintf("%s%v", cacheBookBookPrefix, book)
bookKey := fmt.Sprintf("%s%v", cacheBookPrefix, book)
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := `delete from ` + m.table + ` where book = ?`
query := fmt.Sprintf("delete from %s where book = ?", m.table)
return conn.Exec(query, book)
}, bookBookKey)
}, bookKey)
return err
}
func (m *defaultBookModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheBookPrefix, primary)
}
func (m *defaultBookModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where book = ? limit 1", bookRows, m.table)
return conn.QueryRow(v, query, primary)
}

0
example/bookstore/rpc/model/vars.go Executable file → Normal file
View File

View File

@@ -41,6 +41,7 @@ func main() {
var allowed, denied int32
var wait sync.WaitGroup
for i := 0; i < *threads; i++ {
i := i
wait.Add(1)
go func() {
for {

View File

@@ -5,7 +5,7 @@ go 1.15
require (
github.com/golang/mock v1.4.3
github.com/golang/protobuf v1.4.2
github.com/tal-tech/go-zero v1.0.16
github.com/tal-tech/go-zero v1.0.27
golang.org/x/net v0.0.0-20200707034311-ab3426394381
google.golang.org/grpc v1.29.1
)

View File

@@ -37,6 +37,8 @@ github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQa
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -48,6 +50,8 @@ github.com/dsymonds/gotoc v0.0.0-20160928043926-5aebcfc91819 h1:9778zj477h/VauD8
github.com/dsymonds/gotoc v0.0.0-20160928043926-5aebcfc91819/go.mod h1:MvzMVHq8BH2Ji/o8TGDocVA70byvLrAgFTxkEnmjO4Y=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/emicklei/proto v1.9.0 h1:l0QiNT6Qs7Yj0Mb4X6dnWBQer4ebei2BFcgQLbGqUDc=
github.com/emicklei/proto v1.9.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -124,10 +128,13 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY=
github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8=
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/iancoleman/strcase v0.1.2 h1:gnomlvw9tnV3ITTAxzKSgTF+8kFWcU/f+TgttpXGz1U=
github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
@@ -179,6 +186,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -215,6 +223,8 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
@@ -238,12 +248,16 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tal-tech/go-zero v1.0.16 h1:oT7sOFftEUdD/XcXF0xEugX9yhnw4DcQkeMNFLi5KO8=
github.com/tal-tech/go-zero v1.0.16/go.mod h1:y2wBHTkxNJw79K9/wCSeDKzv2pCT6x45oOmXEsJdQK8=
github.com/tal-tech/go-zero v1.0.27 h1:QMIbaTxibMc/OsO5RTAuKZ8ndbl2dGN6pITQEtp2x/A=
github.com/tal-tech/go-zero v1.0.27/go.mod h1:JtNXlsh/CgeIHyQnt5C5M2IcSevW7V0NAnqO93TQgm8=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
@@ -390,6 +404,7 @@ gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@@ -401,6 +416,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

18
go.mod
View File

@@ -1,16 +1,17 @@
module github.com/tal-tech/go-zero
go 1.13
go 1.14
require (
github.com/ClickHouse/clickhouse-go v1.4.3
github.com/DATA-DOG/go-sqlmock v1.4.1
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 // indirect
github.com/alicebob/miniredis v2.5.0+incompatible
github.com/alicebob/miniredis/v2 v2.14.1
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/dchest/siphash v1.2.1
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/emicklei/proto v1.9.0
github.com/fatih/color v1.9.0 // indirect
github.com/fatih/structtag v1.2.0
github.com/frankban/quicktest v1.7.2 // indirect
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
github.com/go-redis/redis v6.15.7+incompatible
@@ -20,12 +21,11 @@ require (
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/mock v1.4.3
github.com/golang/protobuf v1.4.2
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/google/gops v0.3.7
github.com/google/uuid v1.1.1
github.com/gorilla/websocket v1.4.2 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.14.3 // indirect
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334
github.com/iancoleman/strcase v0.1.2
github.com/justinas/alice v1.2.0
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
@@ -40,12 +40,12 @@ require (
github.com/pierrec/lz4 v2.5.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.5.1
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spaolacci/murmur3 v1.1.0
github.com/stretchr/testify v1.5.1
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect
github.com/urfave/cli v1.22.4
github.com/urfave/cli v1.22.5
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb // indirect
go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698
go.uber.org/automaxprocs v1.3.0
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
@@ -56,10 +56,10 @@ require (
golang.org/x/tools v0.0.0-20200410132612-ae9902aceb98 // indirect
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f // indirect
google.golang.org/grpc v1.29.1
google.golang.org/protobuf v1.25.0
google.golang.org/protobuf v1.25.0 // indirect
gopkg.in/cheggaaa/pb.v1 v1.0.28
gopkg.in/h2non/gock.v1 v1.0.15
gopkg.in/yaml.v2 v2.2.8
gopkg.in/yaml.v2 v2.4.0
honnef.co/go/tools v0.0.1-2020.1.4 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
)

28
go.sum
View File

@@ -11,10 +11,10 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMwWcqkLzDAQugVEwedisr5nRJ1r+7LYnv0U=
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI=
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis/v2 v2.14.1 h1:GjlbSeoJ24bzdLRs13HoMEeaRZx9kg5nHoRW7QV/nCs=
github.com/alicebob/miniredis/v2 v2.14.1/go.mod h1:uS970Sw5Gs9/iK3yBg0l9Uj9s25wXxSpQUE9EaJ/Blg=
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -42,6 +42,8 @@ github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQa
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -61,6 +63,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/frankban/quicktest v1.7.2 h1:2QxQoC1TS09S7fhCPsrvqYdvP1H5M1P1ih5ABm3BTYk=
github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
@@ -112,8 +116,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -145,8 +147,8 @@ github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslC
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8=
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/iancoleman/strcase v0.1.2 h1:gnomlvw9tnV3ITTAxzKSgTF+8kFWcU/f+TgttpXGz1U=
github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
@@ -207,6 +209,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
@@ -248,6 +251,8 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
@@ -275,8 +280,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6 h1:YdYsPAZ2pC6Tow/nPZOPQ96O3hm/ToAkGsPLzedXERk=
@@ -417,6 +422,7 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
@@ -446,6 +452,8 @@ gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=

View File

@@ -1,4 +1,4 @@
<img align="right" width="150px" src="https://raw.githubusercontent.com/tal-tech/zero-doc/main/doc/images/go-zero.png">
<img align="right" width="150px" src="doc/images/go-zero.png">
# go-zero
@@ -25,7 +25,7 @@ Advantages of go-zero:
* auto validate the request parameters from clients
* plenty of builtin microservice management and concurrent toolkits
<img src="https://raw.githubusercontent.com/tal-tech/zero-doc/main/doc/images/architecture-en.png" alt="Architecture" width="1500" />
<img src="doc/images/architecture-en.png" alt="Architecture" width="1500" />
## 1. Backgrounds of go-zero
@@ -76,7 +76,7 @@ go-zero is a web and rpc framework that integrates lots of engineering practices
As below, go-zero protects the system with couple layers and mechanisms:
![Resilience](https://raw.githubusercontent.com/tal-tech/zero-doc/main/doc/images/resilience-en.png)
![Resilience](doc/images/resilience-en.png)
## 4. Future development plans of go-zero
@@ -121,19 +121,17 @@ go get -u github.com/tal-tech/go-zero
}
service greet-api {
@server(
handler: GreetHandler
)
@handler GreetHandler
get /greet/from/:name(Request) returns (Response);
}
```
the .api files also can be generate by goctl, like below:
```shell
goctl api -o greet.api
```
3. generate the go server side code
```shell
@@ -176,7 +174,7 @@ go get -u github.com/tal-tech/go-zero
you can check it by curl:
```shell
curl -i http://localhost:8888/greet/from/you
curl -i http://localhost:8888/from/you
```
the response looks like:
@@ -202,7 +200,7 @@ go get -u github.com/tal-tech/go-zero
## 7. Benchmark
![benchmark](https://raw.githubusercontent.com/tal-tech/zero-doc/main/doc/images/benchmark.png)
![benchmark](doc/images/benchmark.png)
[Checkout the test code](https://github.com/smallnest/go-web-framework-benchmark)
@@ -210,3 +208,7 @@ go get -u github.com/tal-tech/go-zero
* [Rapid development of microservice systems](https://github.com/tal-tech/zero-doc/blob/main/doc/shorturl-en.md)
* [Rapid development of microservice systems - multiple RPCs](https://github.com/tal-tech/zero-doc/blob/main/doc/bookstore-en.md)
## 9. Chat group
Join the chat via https://discord.gg/4JQvC5A4Fe

View File

@@ -1,12 +1,13 @@
<img align="right" width="150px" src="https://raw.githubusercontent.com/tal-tech/zero-doc/main/doc/images/go-zero.png">
<img align="right" width="150px" src="https://gitee.com/kevwan/static/raw/master/doc/images/go-zero.png">
# go-zero
[English](readme-en.md) | 简体中文
[![Go](https://github.com/tal-tech/go-zero/workflows/Go/badge.svg?branch=master)](https://github.com/tal-tech/go-zero/actions)
[![codecov](https://codecov.io/gh/tal-tech/go-zero/branch/master/graph/badge.svg)](https://codecov.io/gh/tal-tech/go-zero)
[![Go Report Card](https://goreportcard.com/badge/github.com/tal-tech/go-zero)](https://goreportcard.com/report/github.com/tal-tech/go-zero)
[![goproxy](https://goproxy.cn/stats/github.com/tal-tech/go-zero/badges/download-count.svg)](https://goproxy.cn/stats/github.com/tal-tech/go-zero/badges/download-count.svg)
[![codecov](https://codecov.io/gh/tal-tech/go-zero/branch/master/graph/badge.svg)](https://codecov.io/gh/tal-tech/go-zero)
[![Release](https://img.shields.io/github/v/release/tal-tech/go-zero.svg?style=flat-square)](https://github.com/tal-tech/go-zero)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -25,7 +26,7 @@ go-zero 包含极简的 API 定义和生成工具 goctl可以根据定义的
* 自动校验客户端请求参数合法性
* 大量微服务治理和并发工具包
<img src="https://raw.githubusercontent.com/tal-tech/zero-doc/main/doc/images/architecture.png" alt="架构图" width="1500" />
<img src="https://gitee.com/kevwan/static/raw/master/doc/images/architecture.png" alt="架构图" width="1500" />
## 1. go-zero 框架背景
@@ -77,7 +78,9 @@ go-zero 是一个集成了各种工程实践的包含 web 和 rpc 框架,有
如下图,我们从多个层面保障了整体服务的高可用:
![弹性设计](https://raw.githubusercontent.com/tal-tech/zero-doc/main/doc/images/resilience.jpg)
![弹性设计](https://gitee.com/kevwan/static/raw/master/doc/images/resilience.jpg)
觉得不错的话,别忘 **star** 👏
## 4. Installation
@@ -93,7 +96,7 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/
[快速构建高并发微服务](https://github.com/tal-tech/zero-doc/blob/main/doc/shorturl.md)
[快速构建高并发微服务 - 多 RPC 版](https://github.com/tal-tech/zero-doc/blob/main/doc/bookstore.md)
[快速构建高并发微服务 - 多 RPC 版](https://github.com/tal-tech/zero-doc/blob/main/docs/zero/bookstore.md)
1. 安装 goctl 工具
@@ -118,7 +121,7 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/
默认侦听在 8888 端口(可以在配置文件里修改),可以通过 curl 请求:
```shell
curl -i http://localhost:8888/greet/from/you
curl -i http://localhost:8888/from/you
```
返回如下:
@@ -148,7 +151,7 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/
## 6. Benchmark
![benchmark](https://raw.githubusercontent.com/tal-tech/zero-doc/main/doc/images/benchmark.png)
![benchmark](https://gitee.com/kevwan/static/raw/master/doc/images/benchmark.png)
[测试代码见这里](https://github.com/smallnest/go-web-framework-benchmark)
@@ -160,7 +163,7 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/
* awesome 系列
* [快速构建高并发微服务](https://github.com/tal-tech/zero-doc/blob/main/doc/shorturl.md)
* [快速构建高并发微服务 - 多 RPC 版](https://github.com/tal-tech/zero-doc/blob/main/doc/bookstore.md)
* [快速构建高并发微服务 - 多 RPC 版](https://github.com/tal-tech/zero-doc/blob/main/docs/zero/bookstore.md)
* [goctl 使用帮助](https://github.com/tal-tech/zero-doc/blob/main/doc/goctl.md)
* [通过 MapReduce 降低服务响应时间](https://github.com/tal-tech/zero-doc/blob/main/doc/mapreduce.md)
* [关键字替换和敏感词过滤工具](https://github.com/tal-tech/zero-doc/blob/main/doc/keywords.md)
@@ -170,18 +173,26 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/
* [文本序列化和反序列化](https://github.com/tal-tech/zero-doc/blob/main/doc/mapping.md)
* [快速构建 jwt 鉴权认证](https://github.com/tal-tech/zero-doc/blob/main/doc/jwt.md)
## 9. 微信交流群
## 8. 微信公众号
加群之前有劳给一个 star一个小小的 star 是作者们回答海量问题的动力。
`go-zero` 相关文章都会在 `微服务实践` 公众号整理呈现,欢迎扫码关注,也可以通过公众号私信我 👏
<img src="https://gitee.com/kevwan/static/raw/master/images/wechat-micro.jpg" alt="wechat" width="300" />
## 9. 微信交流群
如果文档中未能覆盖的任何疑问,欢迎您在群里提出,我们会尽快答复。
您可以在群内提出使用中需要改进的地方,我们会考虑合理性并尽快修改。
如果您发现 bug 请及时提 issue我们会尽快确认并修改。
如果您发现 ***bug*** 请及时提 ***issue***,我们会尽快确认并修改。
<!-- 扫码后请加群主,便于我邀请您进讨论群,并请退出扫码网关群,谢!-->
为了防止广告用户、识别技术同行,请 ***star*** 后加我时注明 **github** 当前 ***star*** 数,我再拉进 **go-zero** 群,谢!
开源中国年度评选给go-zero投上一票[https://www.oschina.net/p/go-zero](https://www.oschina.net/p/go-zero)
加我之前有劳点一下 ***star***,一个小小的 ***star*** 是作者们回答海量问题的动力🤝
<img src="https://raw.githubusercontent.com/tal-tech/zero-doc/main/doc/images/wechat.jpg" alt="wechat" width="300" />
<img src="https://gitee.com/kevwan/static/raw/master/images/wechat.jpg" alt="wechat" width="300" />
项目地址:[https://github.com/tal-tech/go-zero](https://github.com/tal-tech/go-zero)
码云地址:[https://gitee.com/kevwan/go-zero](https://gitee.com/kevwan/go-zero) (国内用户可访问gitee每日自动从github同步代码)

View File

@@ -18,7 +18,7 @@ type (
PrivateKeys []PrivateKeyConf
}
// why not name it as Conf, because we need to consider usage like:
// Why not name it as Conf, because we need to consider usage like:
// type Config struct {
// zrpc.RpcConf
// rest.RestConf
@@ -28,9 +28,11 @@ type (
service.ServiceConf
Host string `json:",default=0.0.0.0"`
Port int
Verbose bool `json:",optional"`
MaxConns int `json:",default=10000"`
MaxBytes int64 `json:",default=1048576,range=[0:8388608]"`
CertFile string `json:",optional"`
KeyFile string `json:",optional"`
Verbose bool `json:",optional"`
MaxConns int `json:",default=10000"`
MaxBytes int64 `json:",default=1048576,range=[0:8388608]"`
// milliseconds
Timeout int64 `json:",default=3000"`
CpuThreshold int64 `json:",default=900,range=[0:1000]"`

View File

@@ -65,7 +65,11 @@ func (s *engine) StartWithRouter(router httpx.Router) error {
return err
}
return internal.StartHttp(s.conf.Host, s.conf.Port, router)
if len(s.conf.CertFile) == 0 && len(s.conf.KeyFile) == 0 {
return internal.StartHttp(s.conf.Host, s.conf.Port, router)
}
return internal.StartHttps(s.conf.Host, s.conf.Port, s.conf.CertFile, s.conf.KeyFile, router)
}
func (s *engine) appendAuthHandler(fr featuredRoutes, chain alice.Chain,

171
rest/engine_test.go Normal file
View File

@@ -0,0 +1,171 @@
package rest
import (
"errors"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/conf"
)
func TestNewEngine(t *testing.T) {
yamls := []string{
`Name: foo
Port: 54321
`,
`Name: foo
Port: 54321
CpuThreshold: 500
`,
`Name: foo
Port: 54321
CpuThreshold: 500
Verbose: true
`,
}
routes := []featuredRoutes{
{
jwt: jwtSetting{},
signature: signatureSetting{},
routes: []Route{{
Method: http.MethodGet,
Path: "/",
Handler: func(w http.ResponseWriter, r *http.Request) {},
}},
},
{
priority: true,
jwt: jwtSetting{},
signature: signatureSetting{},
routes: []Route{{
Method: http.MethodGet,
Path: "/",
Handler: func(w http.ResponseWriter, r *http.Request) {},
}},
},
{
priority: true,
jwt: jwtSetting{
enabled: true,
},
signature: signatureSetting{},
routes: []Route{{
Method: http.MethodGet,
Path: "/",
Handler: func(w http.ResponseWriter, r *http.Request) {},
}},
},
{
priority: true,
jwt: jwtSetting{
enabled: true,
prevSecret: "thesecret",
},
signature: signatureSetting{},
routes: []Route{{
Method: http.MethodGet,
Path: "/",
Handler: func(w http.ResponseWriter, r *http.Request) {},
}},
},
{
priority: true,
jwt: jwtSetting{
enabled: true,
},
signature: signatureSetting{},
routes: []Route{{
Method: http.MethodGet,
Path: "/",
Handler: func(w http.ResponseWriter, r *http.Request) {},
}},
},
{
priority: true,
jwt: jwtSetting{
enabled: true,
},
signature: signatureSetting{
enabled: true,
},
routes: []Route{{
Method: http.MethodGet,
Path: "/",
Handler: func(w http.ResponseWriter, r *http.Request) {},
}},
},
{
priority: true,
jwt: jwtSetting{
enabled: true,
},
signature: signatureSetting{
enabled: true,
SignatureConf: SignatureConf{
Strict: true,
},
},
routes: []Route{{
Method: http.MethodGet,
Path: "/",
Handler: func(w http.ResponseWriter, r *http.Request) {},
}},
},
{
priority: true,
jwt: jwtSetting{
enabled: true,
},
signature: signatureSetting{
enabled: true,
SignatureConf: SignatureConf{
Strict: true,
PrivateKeys: []PrivateKeyConf{
{
Fingerprint: "a",
KeyFile: "b",
},
},
},
},
routes: []Route{{
Method: http.MethodGet,
Path: "/",
Handler: func(w http.ResponseWriter, r *http.Request) {},
}},
},
}
for _, yaml := range yamls {
for _, route := range routes {
var cnf RestConf
assert.Nil(t, conf.LoadConfigFromYamlBytes([]byte(yaml), &cnf))
ng := newEngine(cnf)
ng.AddRoutes(route)
ng.use(func(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
}
})
assert.NotNil(t, ng.StartWithRouter(mockedRouter{}))
}
}
}
type mockedRouter struct {
}
func (m mockedRouter) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
}
func (m mockedRouter) Handle(method string, path string, handler http.Handler) error {
return errors.New("foo")
}
func (m mockedRouter) SetNotFoundHandler(handler http.Handler) {
}
func (m mockedRouter) SetNotAllowedHandler(handler http.Handler) {
}

View File

@@ -46,18 +46,18 @@ func Authorize(secret string, opts ...AuthorizeOption) func(http.Handler) http.H
parser := token.NewTokenParser()
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token, err := parser.ParseToken(r, secret, authOpts.PrevSecret)
tok, err := parser.ParseToken(r, secret, authOpts.PrevSecret)
if err != nil {
unauthorized(w, r, err, authOpts.Callback)
return
}
if !token.Valid {
if !tok.Valid {
unauthorized(w, r, errInvalidToken, authOpts.Callback)
return
}
claims, ok := token.Claims.(jwt.MapClaims)
claims, ok := tok.Claims.(jwt.MapClaims)
if !ok {
unauthorized(w, r, errNoClaims, authOpts.Callback)
return
@@ -122,6 +122,12 @@ func newGuardedResponseWriter(w http.ResponseWriter) *guardedResponseWriter {
}
}
func (grw *guardedResponseWriter) Flush() {
if flusher, ok := grw.writer.(http.Flusher); ok {
flusher.Flush()
}
}
func (grw *guardedResponseWriter) Header() http.Header {
return grw.writer.Header()
}

View File

@@ -41,6 +41,10 @@ func TestAuthHandler(t *testing.T) {
w.Header().Set("X-Test", "test")
_, err := w.Write([]byte("content"))
assert.Nil(t, err)
flusher, ok := w.(http.Flusher)
assert.True(t, ok)
flusher.Flush()
}))
resp := httptest.NewRecorder()

View File

@@ -26,11 +26,11 @@ func ContentSecurityHandler(decrypters map[string]codec.RsaDecrypter, tolerance
case http.MethodDelete, http.MethodGet, http.MethodPost, http.MethodPut:
header, err := security.ParseContentSecurity(decrypters, r)
if err != nil {
logx.Infof("Signature parse failed, X-Content-Security: %s, error: %s",
logx.Errorf("Signature parse failed, X-Content-Security: %s, error: %s",
r.Header.Get(contentSecurity), err.Error())
executeCallbacks(w, r, next, strict, httpx.CodeSignatureInvalidHeader, callbacks)
} else if code := security.VerifySignature(r, header, tolerance); code != httpx.CodeSignaturePass {
logx.Infof("Signature verification failed, X-Content-Security: %s",
logx.Errorf("Signature verification failed, X-Content-Security: %s",
r.Header.Get(contentSecurity))
executeCallbacks(w, r, next, strict, code, callbacks)
} else if r.ContentLength > 0 && header.Encrypted() {
@@ -54,7 +54,7 @@ func executeCallbacks(w http.ResponseWriter, r *http.Request, next http.Handler,
func handleVerificationFailure(w http.ResponseWriter, r *http.Request, next http.Handler, strict bool, code int) {
if strict {
w.WriteHeader(http.StatusUnauthorized)
w.WriteHeader(http.StatusForbidden)
} else {
next.ServeHTTP(w, r)
}

View File

@@ -113,7 +113,7 @@ func TestContentSecurityHandler(t *testing.T) {
strict: true,
crypt: true,
timestamp: time.Now().Add(timeDiff).Unix(),
statusCode: http.StatusUnauthorized,
statusCode: http.StatusForbidden,
},
{
method: http.MethodPost,
@@ -122,7 +122,7 @@ func TestContentSecurityHandler(t *testing.T) {
strict: true,
crypt: true,
timestamp: time.Now().Add(-timeDiff).Unix(),
statusCode: http.StatusUnauthorized,
statusCode: http.StatusForbidden,
},
{
method: http.MethodPost,
@@ -148,7 +148,7 @@ func TestContentSecurityHandler(t *testing.T) {
crypt: true,
timestamp: time.Now().Add(-timeDiff).Unix(),
fingerprint: "badone",
statusCode: http.StatusUnauthorized,
statusCode: http.StatusForbidden,
},
{
method: http.MethodPost,
@@ -157,7 +157,7 @@ func TestContentSecurityHandler(t *testing.T) {
strict: true,
crypt: true,
missHeader: true,
statusCode: http.StatusUnauthorized,
statusCode: http.StatusForbidden,
},
{
method: http.MethodHead,
@@ -171,7 +171,7 @@ func TestContentSecurityHandler(t *testing.T) {
strict: true,
crypt: false,
signature: "badone",
statusCode: http.StatusUnauthorized,
statusCode: http.StatusForbidden,
},
}

View File

@@ -83,6 +83,12 @@ func newCryptionResponseWriter(w http.ResponseWriter) *cryptionResponseWriter {
}
}
func (w *cryptionResponseWriter) Flush() {
if flusher, ok := w.ResponseWriter.(http.Flusher); ok {
flusher.Flush()
}
}
func (w *cryptionResponseWriter) Header() http.Header {
return w.ResponseWriter.Header()
}

View File

@@ -87,3 +87,19 @@ func TestCryptionHandlerWriteHeader(t *testing.T) {
handler.ServeHTTP(recorder, req)
assert.Equal(t, http.StatusServiceUnavailable, recorder.Code)
}
func TestCryptionHandlerFlush(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/any", nil)
handler := CryptionHandler(aesKey)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(respText))
flusher, ok := w.(http.Flusher)
assert.True(t, ok)
flusher.Flush()
}))
recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, req)
expect, err := codec.EcbEncrypt(aesKey, []byte(respText))
assert.Nil(t, err)
assert.Equal(t, base64.StdEncoding.EncodeToString(expect), recorder.Body.String())
}

View File

@@ -38,6 +38,12 @@ func (w *LoggedResponseWriter) WriteHeader(code int) {
w.code = code
}
func (w *LoggedResponseWriter) Flush() {
if flusher, ok := w.w.(http.Flusher); ok {
flusher.Flush()
}
}
func LogHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
timer := utils.NewElapsedTimer()
@@ -68,6 +74,10 @@ func newDetailLoggedResponseWriter(writer *LoggedResponseWriter, buf *bytes.Buff
}
}
func (w *DetailLoggedResponseWriter) Flush() {
w.writer.Flush()
}
func (w *DetailLoggedResponseWriter) Header() http.Header {
return w.writer.Header()
}

View File

@@ -30,6 +30,10 @@ func TestLogHandler(t *testing.T) {
w.WriteHeader(http.StatusServiceUnavailable)
_, err := w.Write([]byte("content"))
assert.Nil(t, err)
flusher, ok := w.(http.Flusher)
assert.True(t, ok)
flusher.Flush()
}))
resp := httptest.NewRecorder()

View File

@@ -10,7 +10,6 @@ import (
)
const (
multipartFormData = "multipart/form-data"
formKey = "form"
pathKey = "path"
emptyJson = "{}"
@@ -39,12 +38,12 @@ func Parse(r *http.Request, v interface{}) error {
// Parses the form request.
func ParseForm(r *http.Request, v interface{}) error {
if strings.Contains(r.Header.Get(ContentType), multipartFormData) {
if err := r.ParseMultipartForm(maxMemory); err != nil {
return err
}
} else {
if err := r.ParseForm(); err != nil {
if err := r.ParseForm(); err != nil {
return err
}
if err := r.ParseMultipartForm(maxMemory); err != nil {
if err != http.ErrNotMultipart {
return err
}
}

View File

@@ -19,9 +19,7 @@ func TestParseForm(t *testing.T) {
r, err := http.NewRequest(http.MethodGet, "http://hello.com/a?name=hello&age=18&percent=3.4", nil)
assert.Nil(t, err)
err = Parse(r, &v)
assert.Nil(t, err)
assert.Nil(t, Parse(r, &v))
assert.Equal(t, "hello", v.Name)
assert.Equal(t, 18, v.Age)
assert.Equal(t, 3.4, v.Percent)
@@ -97,8 +95,44 @@ Content-Disposition: form-data; name="age"
r := httptest.NewRequest(http.MethodPost, "http://localhost:3333/", strings.NewReader(body))
r.Header.Set(ContentType, "multipart/form-data; boundary=--------------------------220477612388154780019383")
err := Parse(r, &v)
assert.Nil(t, err)
assert.Nil(t, Parse(r, &v))
assert.Equal(t, "kevin", v.Name)
assert.Equal(t, 18, v.Age)
}
func TestParseMultipartFormWrongBoundary(t *testing.T) {
var v struct {
Name string `form:"name"`
Age int `form:"age"`
}
body := strings.Replace(`----------------------------22047761238815478001938
Content-Disposition: form-data; name="name"
kevin
----------------------------22047761238815478001938
Content-Disposition: form-data; name="age"
18
----------------------------22047761238815478001938--`, "\n", "\r\n", -1)
r := httptest.NewRequest(http.MethodPost, "http://localhost:3333/", strings.NewReader(body))
r.Header.Set(ContentType, "multipart/form-data; boundary=--------------------------220477612388154780019383")
assert.NotNil(t, Parse(r, &v))
}
func TestParseJsonBody(t *testing.T) {
var v struct {
Name string `json:"name"`
Age int `json:"age"`
}
body := `{"name":"kevin", "age": 18}`
r := httptest.NewRequest(http.MethodPost, "http://localhost:3333/", strings.NewReader(body))
r.Header.Set(ContentType, ApplicationJson)
assert.Nil(t, Parse(r, &v))
assert.Equal(t, "kevin", v.Name)
assert.Equal(t, 18, v.Age)
}
@@ -111,9 +145,7 @@ func TestParseRequired(t *testing.T) {
r, err := http.NewRequest(http.MethodGet, "http://hello.com/a?name=hello", nil)
assert.Nil(t, err)
err = Parse(r, &v)
assert.NotNil(t, err)
assert.NotNil(t, Parse(r, &v))
}
func TestParseOptions(t *testing.T) {
@@ -123,9 +155,7 @@ func TestParseOptions(t *testing.T) {
r, err := http.NewRequest(http.MethodGet, "http://hello.com/a?pos=4", nil)
assert.Nil(t, err)
err = Parse(r, &v)
assert.NotNil(t, err)
assert.NotNil(t, Parse(r, &v))
}
func BenchmarkParseRaw(b *testing.B) {

View File

@@ -3,12 +3,33 @@ package httpx
import (
"encoding/json"
"net/http"
"sync"
"github.com/tal-tech/go-zero/core/logx"
)
var (
errorHandler func(error) (int, interface{})
lock sync.RWMutex
)
func Error(w http.ResponseWriter, err error) {
http.Error(w, err.Error(), http.StatusBadRequest)
lock.RLock()
handler := errorHandler
lock.RUnlock()
if handler == nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
code, body := errorHandler(err)
e, ok := body.(error)
if ok {
http.Error(w, e.Error(), code)
} else {
WriteJson(w, code, body)
}
}
func Ok(w http.ResponseWriter) {
@@ -19,6 +40,12 @@ func OkJson(w http.ResponseWriter, v interface{}) {
WriteJson(w, http.StatusOK, v)
}
func SetErrorHandler(handler func(error) (int, interface{})) {
lock.Lock()
defer lock.Unlock()
errorHandler = handler
}
func WriteJson(w http.ResponseWriter, code int, v interface{}) {
w.Header().Set(ContentType, ApplicationJson)
w.WriteHeader(code)

View File

@@ -19,13 +19,65 @@ func init() {
}
func TestError(t *testing.T) {
const body = "foo"
w := tracedResponseWriter{
headers: make(map[string][]string),
const (
body = "foo"
wrappedBody = `"foo"`
)
tests := []struct {
name string
input string
errorHandler func(error) (int, interface{})
expectBody string
expectCode int
}{
{
name: "default error handler",
input: body,
expectBody: body,
expectCode: http.StatusBadRequest,
},
{
name: "customized error handler return string",
input: body,
errorHandler: func(err error) (int, interface{}) {
return http.StatusForbidden, err.Error()
},
expectBody: wrappedBody,
expectCode: http.StatusForbidden,
},
{
name: "customized error handler return error",
input: body,
errorHandler: func(err error) (int, interface{}) {
return http.StatusForbidden, err
},
expectBody: body,
expectCode: http.StatusForbidden,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
w := tracedResponseWriter{
headers: make(map[string][]string),
}
if test.errorHandler != nil {
lock.RLock()
prev := errorHandler
lock.RUnlock()
SetErrorHandler(test.errorHandler)
defer func() {
lock.Lock()
errorHandler = prev
lock.Unlock()
}()
}
Error(&w, errors.New(test.input))
assert.Equal(t, test.expectCode, w.code)
assert.Equal(t, test.expectBody, strings.TrimSpace(w.builder.String()))
})
}
Error(&w, errors.New(body))
assert.Equal(t, http.StatusBadRequest, w.code)
assert.Equal(t, body, strings.TrimSpace(w.builder.String()))
}
func TestOk(t *testing.T) {

View File

@@ -23,5 +23,5 @@ func WithPathVars(r *http.Request, params map[string]string) *http.Request {
type contextKey string
func (c contextKey) String() string {
return "rest/internal/context context key" + string(c)
return "rest/internal/context key: " + string(c)
}

View File

@@ -0,0 +1,32 @@
package context
import (
"context"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestVars(t *testing.T) {
expect := map[string]string{
"a": "1",
"b": "2",
}
r, err := http.NewRequest(http.MethodGet, "/", nil)
assert.Nil(t, err)
r = r.WithContext(context.WithValue(context.Background(), pathVars, expect))
assert.EqualValues(t, expect, Vars(r))
}
func TestVarsNil(t *testing.T) {
r, err := http.NewRequest(http.MethodGet, "/", nil)
assert.Nil(t, err)
assert.Nil(t, Vars(r))
}
func TestContextKey(t *testing.T) {
ck := contextKey("hello")
assert.True(t, strings.Contains(ck.String(), "hello"))
}

View File

@@ -7,6 +7,12 @@ type WithCodeResponseWriter struct {
Code int
}
func (w *WithCodeResponseWriter) Flush() {
if flusher, ok := w.Writer.(http.Flusher); ok {
flusher.Flush()
}
}
func (w *WithCodeResponseWriter) Header() http.Header {
return w.Writer.Header()
}

View File

@@ -0,0 +1,33 @@
package security
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestWithCodeResponseWriter(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "http://localhost", nil)
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
cw := &WithCodeResponseWriter{Writer: w}
cw.Header().Set("X-Test", "test")
cw.WriteHeader(http.StatusServiceUnavailable)
assert.Equal(t, cw.Code, http.StatusServiceUnavailable)
_, err := cw.Write([]byte("content"))
assert.Nil(t, err)
flusher, ok := http.ResponseWriter(cw).(http.Flusher)
assert.True(t, ok)
flusher.Flush()
})
resp := httptest.NewRecorder()
handler.ServeHTTP(resp, req)
assert.Equal(t, http.StatusServiceUnavailable, resp.Code)
assert.Equal(t, "test", resp.Header().Get("X-Test"))
assert.Equal(t, "content", resp.Body.String())
}

View File

@@ -2,7 +2,6 @@ package internal
import (
"context"
"crypto/tls"
"fmt"
"net/http"
@@ -10,42 +9,27 @@ import (
)
func StartHttp(host string, port int, handler http.Handler) error {
addr := fmt.Sprintf("%s:%d", host, port)
server := buildHttpServer(addr, handler)
return StartServer(server)
return start(host, port, handler, func(srv *http.Server) error {
return srv.ListenAndServe()
})
}
func StartHttps(host string, port int, certFile, keyFile string, handler http.Handler) error {
addr := fmt.Sprintf("%s:%d", host, port)
if server, err := buildHttpsServer(addr, handler, certFile, keyFile); err != nil {
return err
} else {
return StartServer(server)
}
}
func StartServer(srv *http.Server) error {
proc.AddWrapUpListener(func() {
srv.Shutdown(context.Background())
return start(host, port, handler, func(srv *http.Server) error {
// certFile and keyFile are set in buildHttpsServer
return srv.ListenAndServeTLS(certFile, keyFile)
})
return srv.ListenAndServe()
}
func buildHttpServer(addr string, handler http.Handler) *http.Server {
return &http.Server{Addr: addr, Handler: handler}
}
func buildHttpsServer(addr string, handler http.Handler, certFile, keyFile string) (*http.Server, error) {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
func start(host string, port int, handler http.Handler, run func(srv *http.Server) error) error {
server := &http.Server{
Addr: fmt.Sprintf("%s:%d", host, port),
Handler: handler,
}
waitForCalled := proc.AddWrapUpListener(func() {
server.Shutdown(context.Background())
})
defer waitForCalled()
config := tls.Config{Certificates: []tls.Certificate{cert}}
return &http.Server{
Addr: addr,
Handler: handler,
TLSConfig: &config,
}, nil
return run(server)
}

View File

@@ -64,7 +64,7 @@ func (pr *patRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
allow, ok := pr.methodNotAllowed(r.Method, reqPath)
allows, ok := pr.methodsAllowed(r.Method, reqPath)
if !ok {
pr.handleNotFound(w, r)
return
@@ -73,7 +73,7 @@ func (pr *patRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if pr.notAllowed != nil {
pr.notAllowed.ServeHTTP(w, r)
} else {
w.Header().Set(allowHeader, allow)
w.Header().Set(allowHeader, allows)
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
@@ -94,7 +94,7 @@ func (pr *patRouter) handleNotFound(w http.ResponseWriter, r *http.Request) {
}
}
func (pr *patRouter) methodNotAllowed(method, path string) (string, bool) {
func (pr *patRouter) methodsAllowed(method, path string) (string, bool) {
var allows []string
for treeMethod, tree := range pr.trees {

View File

@@ -1,7 +1,6 @@
package rest
import (
"errors"
"log"
"net/http"
@@ -24,6 +23,9 @@ type (
}
)
// MustNewServer returns a server with given config of c and options defined in opts.
// Be aware that later RunOption might overwrite previous one that write the same option.
// The process will exit if error occurs.
func MustNewServer(c RestConf, opts ...RunOption) *Server {
engine, err := NewServer(c, opts...)
if err != nil {
@@ -33,11 +35,9 @@ func MustNewServer(c RestConf, opts ...RunOption) *Server {
return engine
}
// NewServer returns a server with given config of c and options defined in opts.
// Be aware that later RunOption might overwrite previous one that write the same option.
func NewServer(c RestConf, opts ...RunOption) (*Server, error) {
if len(opts) > 1 {
return nil, errors.New("only one RunOption is allowed")
}
if err := c.SetUp(); err != nil {
return nil, err
}

View File

@@ -8,18 +8,84 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/conf"
"github.com/tal-tech/go-zero/rest/httpx"
"github.com/tal-tech/go-zero/rest/router"
)
func TestNewServer(t *testing.T) {
_, err := NewServer(RestConf{}, WithNotFoundHandler(nil), WithNotAllowedHandler(nil))
assert.NotNil(t, err)
const configYaml = `
Name: foo
Port: 54321
`
var cnf RestConf
assert.Nil(t, conf.LoadConfigFromYamlBytes([]byte(configYaml), &cnf))
failStart := func(server *Server) {
server.opts.start = func(e *engine) error {
return http.ErrServerClosed
}
}
tests := []struct {
c RestConf
opts []RunOption
fail bool
}{
{
c: RestConf{},
opts: []RunOption{failStart},
fail: true,
},
{
c: cnf,
opts: []RunOption{failStart},
},
{
c: cnf,
opts: []RunOption{WithNotAllowedHandler(nil), failStart},
},
{
c: cnf,
opts: []RunOption{WithNotFoundHandler(nil), failStart},
},
{
c: cnf,
opts: []RunOption{WithUnauthorizedCallback(nil), failStart},
},
{
c: cnf,
opts: []RunOption{WithUnsignedCallback(nil), failStart},
},
}
for _, test := range tests {
srv, err := NewServer(test.c, test.opts...)
if test.fail {
assert.NotNil(t, err)
}
if err != nil {
continue
}
srv.Use(ToMiddleware(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
})
}))
srv.AddRoute(Route{
Method: http.MethodGet,
Path: "/",
Handler: nil,
}, WithJwt("thesecret"), WithSignature(SignatureConf{}),
WithJwtTransition("preivous", "thenewone"))
srv.Start()
srv.Stop()
}
}
func TestWithMiddleware(t *testing.T) {
m := make(map[string]string)
router := router.NewRouter()
rt := router.NewRouter()
handler := func(w http.ResponseWriter, r *http.Request) {
var v struct {
Nickname string `form:"nickname"`
@@ -56,14 +122,14 @@ func TestWithMiddleware(t *testing.T) {
"http://hello.com/second/wan/2020?nickname=whatever&zipcode=200000",
}
for _, route := range rs {
assert.Nil(t, router.Handle(route.Method, route.Path, route.Handler))
assert.Nil(t, rt.Handle(route.Method, route.Path, route.Handler))
}
for _, url := range urls {
r, err := http.NewRequest(http.MethodGet, url, nil)
assert.Nil(t, err)
rr := httptest.NewRecorder()
router.ServeHTTP(rr, r)
rt.ServeHTTP(rr, r)
assert.Equal(t, "whatever:200000", rr.Body.String())
}
@@ -76,7 +142,7 @@ func TestWithMiddleware(t *testing.T) {
func TestMultiMiddlewares(t *testing.T) {
m := make(map[string]string)
router := router.NewRouter()
rt := router.NewRouter()
handler := func(w http.ResponseWriter, r *http.Request) {
var v struct {
Nickname string `form:"nickname"`
@@ -127,14 +193,14 @@ func TestMultiMiddlewares(t *testing.T) {
"http://hello.com/second/wan/2020?nickname=whatever&zipcode=200000",
}
for _, route := range rs {
assert.Nil(t, router.Handle(route.Method, route.Path, route.Handler))
assert.Nil(t, rt.Handle(route.Method, route.Path, route.Handler))
}
for _, url := range urls {
r, err := http.NewRequest(http.MethodGet, url, nil)
assert.Nil(t, err)
rr := httptest.NewRecorder()
router.ServeHTTP(rr, r)
rt.ServeHTTP(rr, r)
assert.Equal(t, "whatever:200000200000", rr.Body.String())
}

View File

@@ -2,13 +2,13 @@ version := $(shell /bin/date "+%Y-%m-%d %H:%M")
build:
go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" goctl.go
command -v upx &> /dev/null && upx goctl
$(if $(shell command -v upx), upx goctl)
mac:
GOOS=darwin go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl-darwin goctl.go
command -v upx &> /dev/null && upx goctl-darwin
$(if $(shell command -v upx), upx goctl-darwin)
win:
GOOS=windows go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl.exe goctl.go
command -v upx &> /dev/null && upx goctl.exe
$(if $(shell command -v upx), upx goctl.exe)
linux:
GOOS=linux go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl-linux goctl.go
command -v upx &> /dev/null && upx goctl-linux
$(if $(shell command -v upx), upx goctl-linux)

View File

@@ -28,10 +28,10 @@ type response struct {
}
service {{.serviceName}} {
@handler // TODO: set handler name and delete this comment
@handler GetUser // TODO: set handler name and delete this comment
get /users/id/:userId(request) returns(response)
@handler // TODO: set handler name and delete this comment
@handler CreateUser // TODO: set handler name and delete this comment
post /users/create(request)
}
`

View File

@@ -40,7 +40,7 @@ func genDoc(api *spec.ApiSpec, dir string, filename string) error {
defer fp.Close()
var builder strings.Builder
for index, route := range api.Service.Routes {
for index, route := range api.Service.Routes() {
routeComment, _ := util.GetAnnotationValue(route.Annotations, "doc", "summary")
if len(routeComment) == 0 {
routeComment = "N/A"

View File

@@ -1,6 +1,7 @@
package format
import (
"bufio"
"errors"
"fmt"
"go/format"
@@ -8,18 +9,20 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/tal-tech/go-zero/core/errorx"
"github.com/tal-tech/go-zero/tools/goctl/api/parser"
"github.com/tal-tech/go-zero/tools/goctl/api/util"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/urfave/cli"
)
var (
reg = regexp.MustCompile("type (?P<name>.*)[\\s]+{")
const (
leftParenthesis = "("
rightParenthesis = ")"
leftBrace = "{"
rightBrace = "}"
)
func GoFormatApi(c *cli.Context) error {
@@ -94,69 +97,116 @@ func ApiFormatByPath(apiFilePath string) error {
}
func apiFormat(data string) (string, error) {
r := reg.ReplaceAllStringFunc(data, func(m string) string {
parts := reg.FindStringSubmatch(m)
if len(parts) < 2 {
return m
}
if !strings.Contains(m, "struct") {
return "type " + parts[1] + " struct {"
}
return m
})
apiStruct, err := parser.ParseApi(r)
_, err := parser.ParseApi(data)
if err != nil {
return "", err
}
info := strings.TrimSpace(apiStruct.Info)
if len(apiStruct.Service) == 0 {
return data, nil
}
fs, err := format.Source([]byte(strings.TrimSpace(apiStruct.StructBody)))
if err != nil {
str := err.Error()
lineNumber := strings.Index(str, ":")
if lineNumber > 0 {
ln, err := strconv.ParseInt(str[:lineNumber], 10, 64)
var builder strings.Builder
s := bufio.NewScanner(strings.NewReader(data))
var tapCount = 0
var newLineCount = 0
var preLine string
for s.Scan() {
line := strings.TrimSpace(s.Text())
if len(line) == 0 {
if newLineCount > 0 {
continue
}
newLineCount++
} else {
if preLine == rightBrace {
builder.WriteString(ctlutil.NL)
}
newLineCount = 0
}
if tapCount == 0 {
format, err := formatGoTypeDef(line, s, &builder)
if err != nil {
return "", err
}
pn := 0
if len(info) > 0 {
pn = countRune(info, '\n') + 1
if format {
continue
}
number := int(ln) + pn + 1
return "", errors.New(fmt.Sprintf("line: %d, %s", number, str[lineNumber+1:]))
}
return "", err
}
var result string
if len(strings.TrimSpace(info)) > 0 {
result += strings.TrimSpace(info) + "\n\n"
noCommentLine := util.RemoveComment(line)
if noCommentLine == rightParenthesis || noCommentLine == rightBrace {
tapCount -= 1
}
if tapCount < 0 {
line := strings.TrimSuffix(noCommentLine, rightBrace)
line = strings.TrimSpace(line)
if strings.HasSuffix(line, leftBrace) {
tapCount += 1
}
}
util.WriteIndent(&builder, tapCount)
builder.WriteString(line + ctlutil.NL)
if strings.HasSuffix(noCommentLine, leftParenthesis) || strings.HasSuffix(noCommentLine, leftBrace) {
tapCount += 1
}
preLine = line
}
if len(strings.TrimSpace(apiStruct.Imports)) > 0 {
result += strings.TrimSpace(apiStruct.Imports) + "\n\n"
}
if len(strings.TrimSpace(string(fs))) > 0 {
result += strings.TrimSpace(string(fs)) + "\n\n"
}
if len(strings.TrimSpace(apiStruct.Service)) > 0 {
result += strings.TrimSpace(apiStruct.Service) + "\n\n"
}
return result, nil
return strings.TrimSpace(builder.String()), nil
}
func countRune(s string, r rune) int {
count := 0
for _, c := range s {
if c == r {
count++
func formatGoTypeDef(line string, scanner *bufio.Scanner, builder *strings.Builder) (bool, error) {
noCommentLine := util.RemoveComment(line)
tokenCount := 0
if strings.HasPrefix(noCommentLine, "type") && (strings.HasSuffix(noCommentLine, leftParenthesis) ||
strings.HasSuffix(noCommentLine, leftBrace)) {
var typeBuilder strings.Builder
typeBuilder.WriteString(mayInsertStructKeyword(line, &tokenCount) + ctlutil.NL)
for scanner.Scan() {
noCommentLine := util.RemoveComment(scanner.Text())
typeBuilder.WriteString(mayInsertStructKeyword(scanner.Text(), &tokenCount) + ctlutil.NL)
if noCommentLine == rightBrace || noCommentLine == rightParenthesis {
tokenCount--
}
if tokenCount == 0 {
ts, err := format.Source([]byte(typeBuilder.String()))
if err != nil {
return false, errors.New("error format \n" + typeBuilder.String())
}
result := strings.ReplaceAll(string(ts), " struct ", " ")
result = strings.ReplaceAll(result, "type ()", "")
builder.WriteString(result)
break
}
}
return true, nil
}
return false, nil
}
func mayInsertStructKeyword(line string, token *int) string {
insertStruct := func() string {
if strings.Contains(line, " struct") {
return line
}
index := strings.Index(line, leftBrace)
return line[:index] + " struct " + line[index:]
}
noCommentLine := util.RemoveComment(line)
if strings.HasSuffix(noCommentLine, leftBrace) {
*token++
return insertStruct()
}
if strings.HasSuffix(noCommentLine, rightBrace) {
noCommentLine = strings.TrimSuffix(noCommentLine, rightBrace)
noCommentLine = util.RemoveComment(noCommentLine)
if strings.HasSuffix(noCommentLine, leftBrace) {
return insertStruct()
}
}
return count
if strings.HasSuffix(noCommentLine, leftParenthesis) {
*token++
}
return line
}

View File

@@ -0,0 +1,47 @@
package format
import (
"testing"
"github.com/stretchr/testify/assert"
)
const (
notFormattedStr = `
type Request struct {
Name string
}
type Response struct {
Message string
}
service A-api {
@server(
handler: GreetHandler
)
get /greet/from/:name(Request) returns (Response)
}
`
formattedStr = `type Request {
Name string
}
type Response {
Message string
}
service A-api {
@server(
handler: GreetHandler
)
get /greet/from/:name(Request) returns (Response)
}`
)
func TestFormat(t *testing.T) {
r, err := apiFormat(notFormattedStr)
assert.Nil(t, err)
assert.Equal(t, r, formattedStr)
}

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