Compare commits

...

667 Commits

Author SHA1 Message Date
d0f9d44fe4 Coverage default.go (#330)
Some checks failed
Go / test (push) Failing after 11s
/ autoupdate (push) Failing after 1s
Coverage default.go

Added tests for default.go. The tests are successful

Co-authored-by: Aleksandr Tolstikhin <atolstikhin@mtsbank.ru>
Co-authored-by: Василий Толстов <v.tolstov@unistack.org>
Reviewed-on: #330
Reviewed-by: Василий Толстов <v.tolstov@unistack.org>
Co-authored-by: Александр Толстихин <tolstihin1996@mail.ru>
Co-committed-by: Александр Толстихин <tolstihin1996@mail.ru>
2024-09-20 17:55:24 +03:00
725ed992cc #335 caller skip count. (#338)
Some checks failed
Go / test (push) Has been cancelled
/ autoupdate (push) Failing after 5s
Co-authored-by: Gorbunov Kirill Andreevich <kgorbunov@mtsbank.ru>
Reviewed-on: #338
Co-authored-by: Кирилл Горбунов <kirya_gorbunov_2015@mail.ru>
Co-committed-by: Кирилл Горбунов <kirya_gorbunov_2015@mail.ru>
2024-04-15 13:31:14 +03:00
b8928d3da9 semconv: add cache metric names
Some checks failed
Go / test (push) Failing after 6m10s
/ autoupdate (push) Successful in 1m20s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-04-14 16:46:05 +03:00
76090f7569 util/xpool: package pool
Some checks failed
Go / test (push) Failing after 6m9s
/ autoupdate (push) Successful in 1m18s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-04-14 00:19:08 +03:00
f8c68a81f7 semconv: add broker group lag
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-04-13 02:11:05 +03:00
5d997f7654 Merge pull request 'add options in broker' (#333) from devstigneev/micro:master into master
Some checks failed
Go / test (push) Failing after 6m14s
/ autoupdate (push) Successful in 1m20s
Reviewed-on: #333
2024-04-08 23:17:49 +03:00
56d33ae823 rename path to sync
Some checks failed
Go / test (pull_request) Has been cancelled
lint / lint (pull_request) Has been cancelled
pr / test (pull_request) Has been cancelled
2024-04-07 21:17:42 +03:00
c3cabc1fe5 add options in broker
Some checks failed
Go / test (pull_request) Has been cancelled
lint / lint (pull_request) Has been cancelled
pr / test (pull_request) Has been cancelled
2024-04-07 20:49:07 +03:00
47497b49b3 util/reflect: detect json.Unmarshaler
Some checks failed
Go / test (push) Failing after 6m45s
/ autoupdate (push) Successful in 1m21s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-04-02 08:54:12 +03:00
e89cfdc80d move sort.Uniq to dedicated package
Some checks failed
/ autoupdate (push) Has been cancelled
Go / test (push) Has been cancelled
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-03-27 11:18:16 +03:00
f954afb236 logger: fixup Debugf => Debug
Some checks failed
/ autoupdate (push) Has been cancelled
Go / test (push) Has been cancelled
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-03-25 21:16:24 +03:00
fbfacc82cd update type for WaitGroup in server (#324)
Some checks failed
Go / test (push) Failing after 6m5s
/ autoupdate (push) Failing after 1m2s
## Pull Request template
Please, go through these steps before clicking submit on this PR.

1. Give a descriptive title to your PR.
2. Provide a description of your changes.
3. Make sure you have some relevant tests.
4. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if applicable).

**PLEASE REMOVE THIS TEMPLATE BEFORE SUBMITTING**

Reviewed-on: #324
Co-authored-by: Evstigneev Denis <danteevstigneev@yandex.ru>
Co-committed-by: Evstigneev Denis <danteevstigneev@yandex.ru>
2024-03-12 23:13:55 +03:00
3d6df76dd5 tracer: add Context init to NewOptions
Some checks failed
Go / test (push) Failing after 6m14s
/ autoupdate (push) Failing after 1m5s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-03-11 01:14:30 +03:00
2afcb51440 update workflow
Some checks failed
Go / test (push) Failing after 7m59s
/ autoupdate (push) Failing after 59s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-03-10 22:59:12 +03:00
eb534a46d0 metadata: add exclude to Copy func
Some checks failed
/ autoupdate (push) Failing after 1m2s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-03-09 23:53:04 +03:00
856aac4aa9 sync/waitgroup: initial sync.WaitGroup wrapper with context support (#319)
Some checks failed
/ autoupdate (push) Failing after 1m12s
Reviewed-on: #319
Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org>
Co-committed-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-03-09 23:35:13 +03:00
e416a5b38c Merge pull request 'rework metadata' (#318) from metav4 into master
Some checks failed
/ autoupdate (push) Failing after 1m40s
Reviewed-on: #318
2024-03-09 17:24:02 +03:00
2f8c0d9f9d rework metadata
All checks were successful
pr / test (pull_request) Successful in 2m10s
lint / lint (pull_request) Successful in 10m35s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-03-09 17:21:20 +03:00
4b261083d6 fix races
Some checks failed
/ autoupdate (push) Failing after 59s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-03-07 08:57:45 +03:00
4c3c4859fc logger: add TimeFunc option
Some checks failed
/ autoupdate (push) Failing after 1m3s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-03-07 00:08:34 +03:00
7ef5c5d804 backport from v3
Some checks failed
/ autoupdate (push) Failing after 1m3s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-03-06 01:17:19 +03:00
1cbab38d24 tracer and logger improvements
Some checks failed
/ autoupdate (push) Failing after 1m2s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-03-05 01:27:22 +03:00
c766477aaa backport from v3
Some checks failed
/ autoupdate (push) Failing after 1m2s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-03-05 00:03:37 +03:00
f28f8e13b3 logger/slog: add stacktrace support
Some checks failed
/ autoupdate (push) Failing after 1m22s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-03-04 23:42:15 +03:00
1cbc353479 logger: fixes
Some checks failed
/ autoupdate (push) Failing after 1m16s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-03-04 22:22:05 +03:00
7050313bc1 Merge pull request 'issue_294' (#301) from devstigneev/micro:issue_294 into master
Some checks failed
/ autoupdate (push) Failing after 1m3s
Reviewed-on: #301
2024-02-29 23:37:51 +03:00
3654961fde Merge pull request 'Обновить server/options.go' (#305) from vtolstov-patch-1 into master
Some checks failed
/ autoupdate (push) Failing after 2m27s
Reviewed-on: #305
2024-02-29 23:27:56 +03:00
fa2b7c924e Обновить server/options.go
Some checks failed
pr / test (pull_request) Failing after 3m15s
lint / lint (pull_request) Successful in 11m33s
Signed-off-by: Василий Толстов <v.tolstov@unistack.org>
2024-02-29 23:27:18 +03:00
de83f42149 Merge pull request 'add gracefultimeout in server' (#303) from devstigneev/micro:issue_293 into master
Some checks failed
/ autoupdate (push) Failing after 1m5s
Reviewed-on: #303
2024-02-29 22:31:49 +03:00
17ace07b1d skip test service register memory
Some checks failed
lint / lint (pull_request) Has been cancelled
pr / test (pull_request) Has been cancelled
2024-02-29 18:43:25 +03:00
e212e3dc50 move test
Some checks failed
lint / lint (pull_request) Has been cancelled
pr / test (pull_request) Has been cancelled
2024-02-29 18:02:12 +03:00
bf0c3016cb add gracefultimeout in server
Some checks failed
lint / lint (pull_request) Has been cancelled
pr / test (pull_request) Has been cancelled
2024-02-29 13:28:27 +03:00
4124b49481 Merge branch 'master' into issue_294
Some checks failed
lint / lint (pull_request) Has been cancelled
pr / test (pull_request) Has been cancelled
2024-02-29 13:07:26 +03:00
a0348a2664 implements register noop
Some checks failed
lint / lint (pull_request) Has been cancelled
pr / test (pull_request) Has been cancelled
2024-02-29 13:06:21 +03:00
f06e0b21e8 add variadic (#302)
Some checks failed
/ autoupdate (push) Failing after 1m0s
Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org>
Reviewed-on: #302
Co-authored-by: Evstigneev Denis <danteevstigneev@yandex.ru>
Co-committed-by: Evstigneev Denis <danteevstigneev@yandex.ru>
2024-02-29 01:27:33 +03:00
3be0566550 Merge pull request 'issue_296' (#300) from devstigneev/micro:issue_296 into master
Some checks failed
/ autoupdate (push) Failing after 1m21s
Reviewed-on: #300
2024-02-28 23:52:30 +03:00
249a64db74 add TODO
Some checks failed
lint / lint (pull_request) Has been cancelled
pr / test (pull_request) Has been cancelled
2024-02-28 23:51:25 +03:00
a428061bf3 fixed changex, have cycle imports 2024-02-28 23:51:05 +03:00
e6feca2fb1 fixup
Some checks failed
pr / test (pull_request) Failing after 2m44s
lint / lint (pull_request) Successful in 11m1s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-02-28 23:50:38 +03:00
37fa3d6696 Merge remote-tracking branch 'origin/issue_296' into issue_296
Some checks failed
lint / lint (pull_request) Has been cancelled
pr / test (pull_request) Has been cancelled
# Conflicts:
#	options/options.go
2024-02-28 17:02:17 +03:00
47940a5ca2 add option metadata 2024-02-28 17:01:59 +03:00
d667bbee0c add option metadata 2024-02-28 15:06:29 +03:00
96dd5d869a Обновить .gitea/workflows/pr.yml
Some checks failed
/ autoupdate (push) Failing after 1m3s
2024-02-28 00:24:02 +03:00
ea90948315 Обновить .gitea/workflows/autoupdate.yml
Some checks failed
/ autoupdate (push) Failing after 1m9s
2024-02-28 00:17:09 +03:00
a382ea7d45 Merge pull request '#97 add As for Broker.' (#298) from kgorbunov/micro:#97 into master
Some checks failed
/ autoupdate (push) Failing after 1m0s
Reviewed-on: #298
2024-02-27 22:49:03 +03:00
Gorbunov Kirill Andreevich
cdfeaa7e20 #97 add As for all interface.
Some checks failed
lint / lint (pull_request) Has been cancelled
pr / test (pull_request) Has been cancelled
2024-02-27 22:36:18 +03:00
Gorbunov Kirill Andreevich
c09674ae92 #97 add As for all interface.
Some checks failed
lint / lint (pull_request) Has been cancelled
pr / test (pull_request) Has been cancelled
2024-02-27 22:11:39 +03:00
Gorbunov Kirill Andreevich
0d497ca0df #97 add As for Broker.
Some checks failed
lint / lint (pull_request) Has been cancelled
pr / test (pull_request) Has been cancelled
2024-02-27 21:05:42 +03:00
bb0c415a77 Merge pull request 'config: add conditions' (#287) from cond-config into master
Some checks failed
/ autoupdate (push) Failing after 1m4s
Reviewed-on: #287
2024-01-15 00:51:30 +03:00
8182cb008a config: add conditions
Some checks failed
lint / lint (pull_request) Successful in 1m27s
pr / test (pull_request) Failing after 1m13s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-01-15 00:49:27 +03:00
0771fa0647 fixup multiple client handling (#281)
Some checks failed
/ autoupdate (push) Failing after 1m28s
Reviewed-on: #281
2023-11-13 08:23:22 +03:00
b1fd82adf8 Merge pull request 'database: add FormatDSN' (#277) from database-new into master
Some checks failed
/ autoupdate (push) Failing after 1m26s
Reviewed-on: #277
2023-11-02 01:35:12 +03:00
4bc0e25017 database: add FormatDSN
Some checks failed
lint / lint (pull_request) Failing after 1m28s
pr / test (pull_request) Failing after 2m37s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-11-02 01:31:26 +03:00
aec552aa0b Merge pull request 'database: initial import for dsn parsing' (#275) from database into master
Some checks failed
/ autoupdate (push) Failing after 1m28s
Reviewed-on: #275
2023-11-01 23:44:01 +03:00
cc81bed81d cleanup
Some checks failed
lint / lint (pull_request) Failing after 1m29s
pr / test (pull_request) Failing after 2m37s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-11-01 23:42:07 +03:00
7fde39fba5 database: initial import for dsn parsing
Some checks failed
lint / lint (pull_request) Failing after 1m29s
pr / test (pull_request) Failing after 2m40s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-11-01 23:40:39 +03:00
79438f11e0 logger: slog break import cycle
All checks were successful
/ autoupdate (push) Successful in 1m13s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-17 01:48:50 +03:00
8d19abfebd small fixes
Some checks are pending
/ autoupdate (push) Has started running
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-17 01:11:35 +03:00
77f3731329 change dep
Some checks are pending
/ autoupdate (push) Waiting to run
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-17 01:10:52 +03:00
dbcf6bb74a logger: use key names from opts (#271)
All checks were successful
/ autoupdate (push) Successful in 1m14s
Reviewed-on: #271
2023-10-17 01:05:55 +03:00
17698440ed Merge pull request 'logger: improvements' (#270) from logtrace into master
All checks were successful
/ autoupdate (push) Successful in 1m13s
Reviewed-on: #270
2023-10-17 01:00:20 +03:00
eb8851ab58 logger: improvements
Some checks failed
lint / lint (pull_request) Successful in 1m8s
pr / test (pull_request) Failing after 1m5s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-17 01:00:00 +03:00
69c6d78920 Merge pull request 'logger: add ability to fill fileds from context' (#269) from logger-slog into master
All checks were successful
/ autoupdate (push) Successful in 1m17s
Reviewed-on: #269
2023-10-16 23:44:23 +03:00
7568779db4 logger: add ability to fill fileds from context
Some checks failed
lint / lint (pull_request) Successful in 1m1s
pr / test (pull_request) Failing after 1m7s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-16 23:43:39 +03:00
5c3fa31ad2 logger: slog improve
All checks were successful
/ autoupdate (push) Successful in 1m16s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-16 19:37:38 +03:00
7b9a4924ad Merge pull request 'logger: slog logger' (#266) from slog into master
All checks were successful
/ autoupdate (push) Successful in 1m12s
Reviewed-on: #266
2023-10-14 18:05:46 +03:00
40939e56a3 fixup
All checks were successful
lint / lint (pull_request) Successful in 1m28s
pr / test (pull_request) Successful in 1m11s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-14 17:23:01 +03:00
0df97183f8 tracer: update wrapper
Some checks failed
lint / lint (pull_request) Successful in 1m13s
pr / test (pull_request) Failing after 59s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-14 16:58:40 +03:00
08c40ff7d5 update workflow
Some checks are pending
/ autoupdate (push) Has started running
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-03 13:58:39 +03:00
afc6f88852 update workflow
Some checks are pending
/ autoupdate (push) Has started running
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-03 13:49:04 +03:00
dbcc2bc262 update workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-03 12:07:51 +03:00
ccffa5cd72 Merge pull request 'Bump github.com/google/uuid from v1.3.0 to v1.3.1' (#265) from pkgdash/go_modules/github.com/google/uuid-v1.3.1 into master
Some checks are pending
/ autoupdate (push) Has started running
Reviewed-on: #265
2023-10-03 00:39:35 +03:00
gitea-actions
3612f639d6 2023-10-02 21:38:45 +00:00
32d0b13f91 update workflow
Some checks are pending
/ autoupdate (push) Has started running
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-03 00:32:44 +03:00
4ad61003b9 update workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-03 00:32:33 +03:00
7647a0d3b3 update workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-03 00:32:07 +03:00
c16961e852 update workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-03 00:24:17 +03:00
968feb931c update workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-03 00:15:55 +03:00
911a863313 fixup deps
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-02 02:14:34 +03:00
fcaf323b59 fixup deps
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-02 02:13:17 +03:00
52fecd1772 fixup deps
Some checks are pending
/ autoupdate (push) Has started running
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-02 01:19:37 +03:00
d5f35fe88a fixup deps
Some checks are pending
/ autoupdate (push) Has started running
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-02 01:16:31 +03:00
1952053c1b fixup deps
Some checks are pending
/ autoupdate (push) Has started running
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-02 00:46:29 +03:00
75b45d7313 fixup deps
All checks were successful
/ autoupdate (push) Successful in 1m10s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-02 00:35:21 +03:00
pkgdash
eec350c897 2023-10-01 18:38:57 +00:00
pkgdash
360feb0f41 2023-10-01 18:38:48 +00:00
e1975ab66a fixup deps
Some checks failed
/ autoupdate (push) Failing after 1m12s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-01 21:32:00 +03:00
2504345815 fixup deps
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-01 21:28:47 +03:00
9a95fda93f fixup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-01 16:55:47 +03:00
7ee309a4ae fixup
Some checks are pending
/ autoupdate (push) Has started running
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-01 16:46:09 +03:00
a47b6b92c4 fixup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-10-01 16:28:24 +03:00
8d46a6498d update
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-30 01:32:47 +03:00
65a1612b51 update
Some checks are pending
/ autoupdate (push) Has started running
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-30 00:55:27 +03:00
26783b341b update
Some checks failed
/ autoupdate (push) Failing after 37s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-30 00:53:53 +03:00
ec21d4f307 update
Some checks failed
/ autoupdate (push) Failing after 53s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-30 00:51:52 +03:00
a3ca5ebb97 update
Some checks failed
/ autoupdate (push) Failing after 54s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-30 00:49:32 +03:00
f67ee26042 update
Some checks failed
/ autoupdate (push) Failing after 38s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-30 00:47:29 +03:00
6b7bba709e fixup autoupdate
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-30 00:27:56 +03:00
8b082b90df disable workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-27 14:42:42 +03:00
b499fe99ca rename cli
All checks were successful
/ autoupdate (push) Successful in 40s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-27 14:27:21 +03:00
178e6e956d updates
Some checks failed
/ autoupdate (push) Failing after 38s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-23 23:43:16 +03:00
a52db1969b add autoupdate
Some checks failed
/ autoupdate (push) Failing after 36s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-23 13:35:04 +03:00
27bf1c3d84 add autoupdate
All checks were successful
/ autoupdate (push) Successful in 7s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-23 12:54:15 +03:00
58b687f134 add autoupdate
All checks were successful
/ test_schedule (push) Successful in 4s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-23 12:52:02 +03:00
8a354bd468 add autoupdate
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-22 23:31:14 +03:00
f600195ee2 go mod tidy
Some checks failed
lint / lint (pull_request) Has been cancelled
pr / test (pull_request) Has been cancelled
2023-09-18 10:59:46 +03:00
fd471a89e2 deleted unnecessary operations
Some checks failed
pr / test (pull_request) Has been cancelled
lint / lint (pull_request) Has been cancelled
2023-09-18 10:59:21 +03:00
c346ac43dd add google/slog logger
Some checks failed
lint / lint (pull_request) Has been cancelled
pr / test (pull_request) Has been cancelled
2023-09-16 14:20:02 +03:00
666176138b Merge pull request 'util/reflect: add Equal func with ability to skip some fields' (#243) from util-reflect into master
Reviewed-on: #243
2023-09-12 10:30:48 +03:00
884a96ad32 util/reflect: add Equal func with ability to skip some fields
Some checks failed
lint / lint (pull_request) Successful in 1m18s
pr / test (pull_request) Failing after 1m5s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-12 10:30:19 +03:00
65d715c9da Merge pull request 'tracer: improve' (#242) from tracer into master
Reviewed-on: #242
2023-09-08 14:01:01 +03:00
247c7f6fa2 tracer: improve
Some checks failed
pr / test (pull_request) Failing after 2m43s
lint / lint (pull_request) Failing after 1m28s
2023-09-08 13:58:11 +03:00
f890f1d8b4 tracer: add missing UniqLabels function
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-01 15:48:27 +03:00
54b13bbded Merge pull request 'tracer: improve tracing wrapper' (#240) from trracer into master
Reviewed-on: #240
2023-09-01 15:25:45 +03:00
d3f9078bec tracer: improve tracing wrapper
Some checks failed
lint / lint (pull_request) Failing after 1m28s
pr / test (pull_request) Failing after 2m40s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-01 15:25:25 +03:00
fcc29f9eac tracer: update wrapper
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-09-01 08:49:45 +03:00
e724425ce2 Merge pull request 'fix some options' (#237) from fixes into master
Reviewed-on: #237
2023-08-15 07:55:44 +03:00
bc55c2fa6f fix some options
Some checks failed
lint / lint (pull_request) Failing after 1m29s
pr / test (pull_request) Failing after 2m39s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-08-15 07:55:26 +03:00
fdd1589122 Merge pull request 'move options to dedicated package' (#234) from options_rewrite into master
Reviewed-on: #234
2023-07-29 00:47:28 +03:00
6f6f850af6 move options to dedicated package
Some checks failed
lint / lint (pull_request) Failing after 1m31s
pr / test (pull_request) Failing after 2m37s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-07-29 00:40:58 +03:00
b1dbd99ce2 Merge pull request 'config/default: add micro:generate uuid/id' (#231) from config-default-gen into master
Reviewed-on: #231
2023-07-13 20:11:12 +03:00
6e24807edf config/default: add micro:generate uuid/id
Some checks failed
lint / lint (pull_request) Failing after 1m28s
pr / test (pull_request) Failing after 2m43s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-07-13 20:10:47 +03:00
654d8fa7e4 Merge pull request 'tracer: add Flush method' (#224) from tracerimp into master
Reviewed-on: #224
2023-07-04 00:24:09 +03:00
dd1a9cd25a tracer: add Flush method
Some checks failed
lint / lint (pull_request) Failing after 1m29s
pr / test (pull_request) Failing after 2m37s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-07-04 00:23:50 +03:00
d463eb20cb Merge pull request 'util/time: ParseDuration fix' (#221) from timefix into master
Reviewed-on: #221
2023-05-29 13:59:59 +03:00
8d5e25f8cf util/time: ParseDuration fix
Some checks failed
lint / lint (pull_request) Successful in 1m8s
pr / test (pull_request) Failing after 1m6s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-05-29 13:59:30 +03:00
27e8043fed Merge pull request 'util/time: fix duration parsing' (#220) from mastertime into master
Reviewed-on: #220
2023-05-27 23:57:27 +03:00
4e86df1721 util/time: fix duration parsing
Some checks failed
pr / test (pull_request) Failing after 1m6s
lint / lint (pull_request) Successful in 1m10s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-05-27 23:56:51 +03:00
03de1ec38f Merge pull request 'cleanup interfaces for v4' (#217) from v4 into master
Reviewed-on: #217
2023-05-09 20:04:40 +03:00
819ad1117a cleanup interfaces for v4
Some checks failed
lint / lint (pull_request) Successful in 1m9s
pr / test (pull_request) Failing after 1m4s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-05-09 20:04:15 +03:00
a2a383606d Merge pull request 'util/test: update test cases code' (#216) from testcase into master
Reviewed-on: #216
2023-04-28 07:10:40 +03:00
55ce58617b util/test: update test cases code
Some checks failed
lint / lint (pull_request) Successful in 51s
pr / test (pull_request) Failing after 52s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-28 07:10:19 +03:00
0e587d923e Merge pull request 'meter: move metrics handling in broker implementations' (#215) from metrics into master
Reviewed-on: #215
2023-04-27 15:32:56 +03:00
fa0248c80c cleanup
All checks were successful
pr / test (pull_request) Successful in 50s
lint / lint (pull_request) Successful in 49s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-27 15:31:59 +03:00
054bd02b59 meter: move metrics handling in broker implementations
All checks were successful
lint / lint (pull_request) Successful in 1m4s
pr / test (pull_request) Successful in 50s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-27 15:30:55 +03:00
0cf246d2d6 Merge pull request 'util/io: add RedirectStderr' (#214) from io-redirect into master
Reviewed-on: #214
2023-04-24 12:59:31 +03:00
af278bd7d3 util/io: add RedirectStderr
All checks were successful
lint / lint (pull_request) Successful in 46s
pr / test (pull_request) Successful in 50s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-24 12:58:05 +03:00
814b90efe5 Merge pull request 'util/test: export GetCases func' (#213) from GetCases into master
Reviewed-on: #213
2023-04-19 01:23:53 +03:00
e403ae3d8e util/test: export GetCases func
All checks were successful
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-19 01:23:34 +03:00
c9816a3957 Merge pull request 'util/test: add helper funcs' (#212) from test into master
Reviewed-on: #212
2023-04-19 00:33:28 +03:00
5691238a6a util/test: add helper funcs
All checks were successful
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-18 23:47:12 +03:00
963a0fa7b7 Merge pull request 'gofmt -s code' (#209) from gofmt into master
Reviewed-on: #209
2023-04-11 23:34:41 +03:00
485257035c gofmt -s code
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-11 23:32:58 +03:00
ebd8ddf05b move to v4
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-11 22:21:25 +03:00
1d5e795443 Merge pull request 'move RawMessage to codec package' (#208) from rawmsg into v3
Reviewed-on: #208
2023-04-02 14:15:02 +03:00
a3a434d923 move RawMessage to codec package
All checks were successful
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-04-02 14:10:57 +03:00
bcc06054f1 Merge pull request 'updates' (#207) from updates into v3
Reviewed-on: #207
2023-03-24 00:29:33 +03:00
270d26f1ae test
All checks were successful
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-24 00:25:47 +03:00
646212cc08 retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 17:09:40 +03:00
00c2c749db retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:46:41 +03:00
2dbada0e94 retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:43:33 +03:00
7b8f4410fb retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:36:38 +03:00
45ebef5544 retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:31:42 +03:00
cf4cac0733 retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:29:58 +03:00
50d60b5825 retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:24:47 +03:00
46ef491764 retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:17:44 +03:00
a51b8b8102 retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:11:52 +03:00
15aac48f1e retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:08:51 +03:00
078069b2d7 retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 16:01:27 +03:00
258812304a retest
Some checks failed
autoapprove
automerge
test
lint
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 15:12:32 +03:00
da5d50db5b retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 14:48:27 +03:00
384e4d113d retest
All checks were successful
test
lint
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 14:43:23 +03:00
dfd1da7f0d retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 14:26:03 +03:00
8e5015e580 retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 13:41:59 +03:00
bd0c309b71 retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 13:38:39 +03:00
b4f0c3e29a retest
Some checks failed
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 13:29:21 +03:00
8fddaa0455 retest
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 13:26:42 +03:00
2710c269a8 actions
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 13:21:59 +03:00
70ea93e466 actions
Some checks failed
automerge
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 13:12:10 +03:00
a87d0ab1c1 update deps
Some checks failed
autoapprove
automerge
lint
test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-19 00:43:15 +03:00
2e5e102719 Merge pull request 'config: another fix for Default funcs' (#193) from defaults-fix into v3
Reviewed-on: #193
2023-03-16 07:14:41 +03:00
36e492314d config: another fix for Default funcs
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-16 07:14:21 +03:00
0c78873277 Merge pull request 'config: fix Default funcs' (#192) from config-fix into v3
Reviewed-on: #192
2023-03-15 22:53:21 +03:00
7f57dc09d3 config: fix Default funcs
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-15 22:51:40 +03:00
447206d256 Merge pull request 'config: proper handle AllowFail option' (#191) from config-fix into v3
Reviewed-on: #191
2023-03-14 13:24:37 +03:00
33a7feb970 config: proper handle AllowFail option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-14 13:23:41 +03:00
3950f2ed86 Merge pull request 'config: add Before/After Init funcs' (#190) from config-improve into v3
Reviewed-on: #190
2023-03-14 10:40:12 +03:00
68c1048a7d config: add Before/After Init funcs
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-03-14 10:38:24 +03:00
bff40bd317
Merge pull request #189 from unistack-org/meter-handler
meter/handler: move to go.unistack.org/micro-meter-handler/v3
2023-02-26 13:48:09 +03:00
2878d0a4ea server/health: move to go.unistack.org/micro-server-health/v3
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-26 13:46:10 +03:00
3138a9fded meter/handler: move to go.unistack.org/micro-meter-handler/v3
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-26 13:42:04 +03:00
742b99636a fix and regen
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-22 00:09:52 +03:00
34387f129d
Merge pull request #188 from unistack-org/proto
fix service names in proto
2023-02-21 13:28:13 +03:00
47075acb06 fix service names in proto
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-21 13:26:01 +03:00
09cb998ba4 fix service names in proto
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-21 13:18:03 +03:00
b9dbfb1cfc
Merge pull request #187 from unistack-org/api-REMOVE
api: remove and regen
2023-02-21 02:12:47 +03:00
56efccc4cf api: remove and regen
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-21 02:10:24 +03:00
ce9f896287
Merge pull request #186 from unistack-org/structfs
util/structfs: import https://github.com/unistack-org/go-structfs
2023-02-19 23:38:31 +03:00
83d87a40e4 util/structfs: import https://github.com/unistack-org/go-structfs
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-19 23:35:39 +03:00
75fd1e43b9
Merge pull request #185 from unistack-org/server
server: add server.SetHandlerOption helper
2023-02-13 23:33:57 +03:00
395a3eed3d server: add server.SetHandlerOption helper
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-13 23:31:38 +03:00
3ba8cb7f9e
Merge pull request #184 from unistack-org/duration
util/time: add Marshal/Unmarshal to own Duration
2023-02-13 14:05:16 +03:00
b07806b9a1 tmp
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-13 14:03:02 +03:00
0f583218d4 tmp
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-13 14:02:08 +03:00
f4d0237785
Merge pull request #183 from unistack-org/logger/unwrap
logger/unwrap: fix for nested tagged/untagged
2023-02-08 14:56:51 +03:00
0f343dad0b logger/unwrap: fix for nested tagged/untagged
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-08 14:54:37 +03:00
7c29afba0b
Merge pull request #182 from unistack-org/timeDuration
config/default: handle time.Duration
2023-02-07 06:50:42 +03:00
8159b9d233 config/default: handle time.Duration
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-07 06:48:36 +03:00
45cdac5c29 config/default: handle time.Duration
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-07 06:48:12 +03:00
98db0dc8bc config/default: handle time.Duration
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-07 06:47:46 +03:00
453d2232bd
Merge pull request #181 from unistack-org/unwrap
logger/unwrap: check nested in case of Tagged
2023-02-06 22:38:42 +03:00
9b387312da logger/unwrap: check nested in case of Tagged
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-06 22:36:24 +03:00
84024f7713
Merge pull request #180 from unistack-org/logger-wrapper
logger/unwrap: support sql and proto wrapper types
2023-02-06 18:55:39 +03:00
5a554f9f0c fixup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-06 18:53:27 +03:00
9c33cbc8e2 logger/unwrap: support sql and proto wrapper types
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-06 18:50:00 +03:00
848fe1c0d4
Merge pull request #179 from unistack-org/logger-unwrap
logger/unwrap: fix unwrap format
2023-02-03 23:36:47 +03:00
6cbf23fec5 logger/unwrap: fix unwrap format
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-02-03 23:33:24 +03:00
7462b0b53c
Merge pull request #178 from unistack-org/fsm
fsm: improve and convert to interface
2023-01-30 00:31:50 +03:00
cb743cee3f logger: remove wrappers support and OmitLogger
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-30 00:29:49 +03:00
d18952951c fsm: improve and convert to interface
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-30 00:17:29 +03:00
dependabot[bot]
f6b7f1b4bc
chore(deps): bump dependabot/fetch-metadata from 1.3.5 to 1.3.6 (#172)
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.3.5 to 1.3.6.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.3.5...v1.3.6)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 10:07:13 +03:00
dependabot[bot]
33fa702104
chore(deps): bump golangci/golangci-lint-action from 3.3.1 to 3.4.0 (#173)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.3.1 to 3.4.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.3.1...v3.4.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 09:54:07 +03:00
4debc392d1
Merge pull request #171 from unistack-org/fixspan
add span status method
2023-01-18 09:51:39 +03:00
7137d99102 add span status method
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-18 09:48:58 +03:00
f76b3171d9
Merge pull request #170 from unistack-org/tracer
tracer: add span kind
2023-01-18 00:28:02 +03:00
db3fc42204 tracer: add span kind
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-18 00:25:15 +03:00
f59023d741 tracer: add span kind
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-18 00:21:18 +03:00
6f17fd891a
Merge pull request #169 from unistack-org/tracer
tracer/wrapper: fix observers
2023-01-17 23:39:49 +03:00
fd93308e8e tracer/wrapper: fix observers
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-17 23:37:25 +03:00
2aaa0a74d8 tracer/wrapper: fix observers
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-17 23:31:27 +03:00
ccbf23688b
Merge pull request #168 from unistack-org/tracer
tracer: add context to Options
2023-01-17 08:33:35 +03:00
3bd6db79cb tracer: add context to Options
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-17 08:30:57 +03:00
9347bb0651
use no default content-type (#165)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-07 23:38:53 +03:00
0d63723ed3
Merge pull request #164 from unistack-org/logger-clone-fix
logger: fix Clone
2023-01-06 22:44:26 +03:00
a7f84e0baa logger: fix Clone
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2023-01-06 22:41:57 +03:00
c209892ce8
Merge pull request #163 from unistack-org/logger_unwrap
logger/unwrap: fix Tagged option
2022-12-29 23:19:57 +03:00
421842315f logger/unwrap: fix Tagged option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-12-29 23:16:58 +03:00
25350a6531
Merge pull request #162 from unistack-org/util_http_method_not_allowed
util/http: trie support method not allowed
2022-12-27 23:49:36 +03:00
5e47cc7e8c util/http: trie support method not allowed
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-12-27 23:47:11 +03:00
1687b98b11
Merge pull request #161 from unistack-org/tracer
tracer: add labels method
2022-12-24 19:22:48 +03:00
a81649d2a2 tracer: add labels method
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-12-24 19:20:22 +03:00
b48faa3b2b
Merge pull request #160 from unistack-org/tracer
tracer: fix span options
2022-12-24 18:20:59 +03:00
0be584ef0d fix wrapper
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-12-24 18:18:42 +03:00
26a2d18766 tracer: fix span options
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-12-24 18:09:48 +03:00
25a796fe4f
Merge pull request #158 from unistack-org/meter
meter/wrapper: fix naming
2022-11-28 14:39:45 +03:00
d23de14769 meter/wrapper: fix naming
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-11-28 14:36:54 +03:00
2fb108519c
Merge pull request #157 from unistack-org/fixups
fix wrappers
2022-11-27 00:45:44 +03:00
c7ce238da3 fix wrappers
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-11-27 00:43:30 +03:00
dependabot[bot]
67aa79f18a
chore(deps): bump hmarr/auto-approve-action from 2 to 3 (#155)
Bumps [hmarr/auto-approve-action](https://github.com/hmarr/auto-approve-action) from 2 to 3.
- [Release notes](https://github.com/hmarr/auto-approve-action/releases)
- [Commits](https://github.com/hmarr/auto-approve-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: hmarr/auto-approve-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-19 21:45:49 +03:00
e6c3d734a3
Merge pull request #156 from unistack-org/take_unwrap
logger/unwrap: add tagged option
2022-11-19 15:23:22 +03:00
1374e27531 logger/unwrap: add tagged option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-11-19 15:20:42 +03:00
1060f6a4c3
Merge pull request #154 from unistack-org/client_metadata
client: fix WithResponseMetadata CallOption
2022-11-14 16:13:22 +03:00
7d72ab05c6 client: fix WithResponseMetadata CallOption
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-11-14 16:10:54 +03:00
42864ff1c6
Merge pull request #153 from unistack-org/client_metadata
client: add req/rsp metadata to CallOptions
2022-11-14 15:31:58 +03:00
49978b75c0 client: add req/rsp metadata to CallOptions
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-11-14 15:29:45 +03:00
dependabot[bot]
20770b6e30
chore(deps): bump golangci/golangci-lint-action from 3.3.0 to 3.3.1 (#151)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.3.0 to 3.3.1.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.3.0...v3.3.1)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-14 13:43:44 +03:00
b38c6106b2
Merge pull request #152 from unistack-org/dependabot/go_modules/go.unistack.org/micro-proto/v3-3.3.1
chore(deps): bump go.unistack.org/micro-proto/v3 from 3.2.7 to 3.3.1
2022-11-14 09:05:23 +03:00
dependabot[bot]
138c4a0888
chore(deps): bump go.unistack.org/micro-proto/v3 from 3.2.7 to 3.3.1
Bumps [go.unistack.org/micro-proto/v3](https://github.com/unistack-org/micro-proto) from 3.2.7 to 3.3.1.
- [Release notes](https://github.com/unistack-org/micro-proto/releases)
- [Commits](https://github.com/unistack-org/micro-proto/compare/v3.2.7...v3.3.1)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro-proto/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-14 06:03:28 +00:00
dependabot[bot]
22f66fc258
chore(deps): bump dependabot/fetch-metadata from 1.3.4 to 1.3.5 (#148)
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.3.4 to 1.3.5.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.3.4...v1.3.5)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-12 18:22:11 +03:00
18fafbbbab
Merge pull request #150 from unistack-org/unwrap
logger/unwrap: intergate omit functionality
2022-11-07 16:00:45 +03:00
59c08c1d9a logger/unwrap: intergate omit functionality
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-11-07 15:56:09 +03:00
5fbb1a923e
Merge pull request #149 from unistack-org/improvements
minor improvements
2022-11-07 14:56:24 +03:00
396387d1e8 minor improvements
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-11-07 14:53:58 +03:00
dependabot[bot]
4c2f12a419
chore(deps): bump golangci/golangci-lint-action from 3.2.0 to 3.3.0 (#144)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.2.0...v3.3.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-26 19:34:23 +03:00
b2abb86971
Merge pull request #147 from unistack-org/client_context_dialer
client: add ContextDialer/WithContextDialer option
2022-10-26 19:23:01 +03:00
e546eef96c client: add ContextDialer/WithContextDialer option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-10-26 19:20:37 +03:00
91701e7a45
Merge pull request #146 from unistack-org/codec_noop_fix
codec: fix noop
2022-10-26 13:39:52 +03:00
817bf1f4d0 codec: fix noop
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-10-26 13:37:30 +03:00
4120f79b55
Merge pull request #145 from unistack-org/codec_fix
codec: add []byte support to noop codec
2022-10-26 08:28:36 +03:00
d659db69ff codec: add []byte support to noop codec
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-10-26 08:26:10 +03:00
416bb313fc
Merge pull request #143 from unistack-org/logger_unwrap
WIP: logger/unwrap: add unwrap method
2022-10-11 00:33:25 +03:00
ec43cfea6b logger/unwrap: add unwrap method
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-10-11 00:30:42 +03:00
dependabot[bot]
60194fb42e
chore(deps): bump codecov/codecov-action from 3.1.0 to 3.1.1 (#141)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v3.1.0...v3.1.1)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-06 17:34:40 +03:00
dependabot[bot]
945d9d16a5
chore(deps): bump dependabot/fetch-metadata from 1.3.3 to 1.3.4 (#142)
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.3.3 to 1.3.4.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.3.3...v1.3.4)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-06 17:34:25 +03:00
1c0e5e1a85
Merge pull request #139 from unistack-org/store
store: add Timeout option
2022-07-08 22:41:47 +03:00
33591e0bc9 fixup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-07-08 22:39:51 +03:00
dependabot[bot]
75cbaf2612
chore(deps): bump dependabot/fetch-metadata from 1.3.1 to 1.3.3 (#137)
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.3.1 to 1.3.3.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.3.1...v1.3.3)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-08 22:33:05 +03:00
f4aee3414b store: add Timeout option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-07-08 22:16:33 +03:00
9f7b61eb17 add test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-06-29 23:04:01 +03:00
5953b5aae6
Merge pull request #133 from unistack-org/mtls
WIP: initial mtls package
2022-06-27 00:22:10 +03:00
4a8f490e0c fixup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-06-27 00:20:04 +03:00
eb8c1332f0 fix test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-06-27 00:18:14 +03:00
c1c27b6d1d Merge branch 'v3' into mtls 2022-06-27 00:17:56 +03:00
dependabot[bot]
bb22b203cc
chore(deps): bump github.com/imdario/mergo from 0.3.12 to 0.3.13 (#134)
Bumps [github.com/imdario/mergo](https://github.com/imdario/mergo) from 0.3.12 to 0.3.13.
- [Release notes](https://github.com/imdario/mergo/releases)
- [Commits](https://github.com/imdario/mergo/compare/0.3.12...v0.3.13)

---
updated-dependencies:
- dependency-name: github.com/imdario/mergo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-25 23:28:02 +03:00
dependabot[bot]
4df2f3a5a1
chore(deps): bump github.com/golang-jwt/jwt/v4 from 4.4.1 to 4.4.2 (#135)
Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.4.1 to 4.4.2.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v4.4.1...v4.4.2)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-25 22:39:53 +03:00
b8ad19a5a2 WIP: initial mtls package
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-12 15:11:31 +03:00
dependabot[bot]
d32a97c846
chore(deps): bump golangci/golangci-lint-action from 3.1.0 to 3.2.0 (#132)
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.1.0...v3.2.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-11 11:28:38 +03:00
cfe0473ae0
Merge pull request #130 from unistack-org/improvements
Improvements
2022-05-03 16:08:50 +03:00
c26ad51e25 config: improve coverage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 16:05:56 +03:00
aefc398b71 flow: improve coverage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 16:05:51 +03:00
9af23e3e74 metadata: improve coverage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 16:05:42 +03:00
4ab7f19ef0 logger: improve coverage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 16:05:36 +03:00
d26e9d642b meter: improve coverage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 16:05:28 +03:00
f9ecb9b056 register: improve coverage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 16:05:18 +03:00
dbfcfcd288 router: improve coverage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 16:05:12 +03:00
8b6bdb857b store: improve coverage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 16:05:03 +03:00
1181e9dc5e tracer: improve coverage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 16:04:57 +03:00
6ac7b53d75 server: improve coverage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 16:04:48 +03:00
80d342a72a client: improve coverage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 15:59:38 +03:00
8ff312e71d broker: improve coverage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 15:51:08 +03:00
20e40ccdfd api: improve coverage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 15:41:30 +03:00
d4efbb9b22 metadata: allow to Set/Del multiple items
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 15:19:10 +03:00
b433cbcbb6
Merge pull request #129 from unistack-org/api_cleanup
api: cleanup
2022-05-03 14:48:23 +03:00
dae3c1170b api: cleanup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 14:46:03 +03:00
a10dd3d08a
Merge pull request #128 from unistack-org/big_remove
global cleanup
2022-05-03 14:40:55 +03:00
b075230ae5 global cleanup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 14:38:44 +03:00
289aadb28e
Merge pull request #127 from unistack-org/cover
add more cover stuff
2022-05-03 00:26:13 +03:00
9640cdae1a add more cover stuff
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-05-03 00:23:43 +03:00
dependabot[bot]
fb35e73731
chore(deps): bump github/codeql-action from 1 to 2 (#126)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1 to 2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-26 23:42:38 +03:00
f416cb3e0e
Merge pull request #125 from unistack-org/cover
add tests
2022-04-24 11:11:32 +03:00
57d06d5d27 add tests
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-24 11:08:38 +03:00
0628408c27
Merge pull request #124 from unistack-org/cover
util/reflect: improve test coverage
2022-04-22 09:27:03 +03:00
206cd8c3c9 util/reflect: improve test coverage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-22 09:24:34 +03:00
dependabot[bot]
b38db00ee5
chore(deps): bump codecov/codecov-action from 3.0.0 to 3.1.0 (#123)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v3.0.0...v3.1.0)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-22 06:14:04 +00:00
dependabot[bot]
0ca39a1477
chore(deps): bump dependabot/fetch-metadata from 1.3.0 to 1.3.1 (#122)
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.3.0...v1.3.1)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-21 12:47:39 +03:00
d9be99cfde
Merge pull request #121 from unistack-org/fsm
fsm: add state execution options
2022-04-19 18:45:14 +03:00
b37c6006c4 fsm: add state execution options
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-19 18:42:30 +03:00
12f188e3ad
Merge pull request #120 from unistack-org/fsm
fsm: run steps in order
2022-04-19 17:36:05 +03:00
08aaf14a79 fsm: run steps in order
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-19 17:33:33 +03:00
2ce1e94596
Merge pull request #119 from unistack-org/errors
errors: add CodeIn helper func
2022-04-19 17:15:03 +03:00
c5aeaf6db7 errors: add CodeIn helper func
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-19 17:12:25 +03:00
1db505decd
Merge pull request #118 from unistack-org/cover
improve coverage
2022-04-17 16:28:59 +03:00
8b1a579c9d add context tests
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-17 16:25:42 +03:00
11b614f2df client: add retry func tests
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-17 13:11:36 +03:00
fb4d747197 server: fix race
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-17 11:41:49 +03:00
00439e23f3 add client call options tests
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-16 16:36:43 +03:00
955953b519 client: fix lint
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-16 16:36:34 +03:00
aa2b5ddaad client: add backoff tests
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-16 16:36:15 +03:00
46da092899 client: implement Call and Stream methods for noop
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-16 16:35:52 +03:00
b871f64ba6
Merge pull request #117 from unistack-org/race
server: fix race in noop server
2022-04-15 15:53:18 +03:00
74db004f51 server: fix race in noop server
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-15 15:50:45 +03:00
f93ba9d977
Merge pull request #116 from unistack-org/fsm
fsm: initial import
2022-04-15 15:22:55 +03:00
c7da7d5bc8 fsm: initial import
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-15 15:20:17 +03:00
ed27647be5
Merge pull request #115 from unistack-org/dependabot/github_actions/actions/setup-go-3
chore(deps): bump actions/setup-go from 2 to 3
2022-04-11 23:01:22 +03:00
dependabot[bot]
db3b67267e
chore(deps): bump actions/setup-go from 2 to 3
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2 to 3.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-11 06:17:56 +00:00
9ee9cc2a4a
Merge pull request #114 from unistack-org/cover
fix coverage
2022-04-07 19:18:34 +03:00
0b41b4f9c5 fix 2022-04-07 19:15:55 +03:00
8d14753931 Merge branch 'v3' into cover 2022-04-07 19:13:56 +03:00
93fc17bad3 fix coverage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-07 19:12:49 +03:00
5a1cd12d3d
Merge pull request #113 from unistack-org/vtolstov-patch-1
Update README.md
2022-04-07 19:11:22 +03:00
5c00e6763f
Update README.md
fix link to godoc
2022-04-07 19:08:45 +03:00
497b82ac6c
Merge pull request #112 from unistack-org/codecov
add codecov
2022-04-07 19:07:17 +03:00
a8c6690af7 add codecov
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-07 19:04:04 +03:00
98d2264c2a add codecov
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-07 19:00:08 +03:00
63641b9840 add codecov
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-07 18:57:30 +03:00
2b28057918 add codecov
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-04-07 18:56:10 +03:00
25c551411b
Merge pull request #111 from unistack-org/config
service: fix ordering
2022-03-30 15:51:27 +03:00
35162a82a4 service: fix ordering
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-30 15:48:43 +03:00
0ce0855b6a
Merge pull request #109 from unistack-org/dependabot/go_modules/github.com/golang-jwt/jwt/v4-4.4.1
chore(deps): bump github.com/golang-jwt/jwt/v4 from 4.4.0 to 4.4.1
2022-03-30 15:42:23 +03:00
226ec43ecf
Merge pull request #110 from unistack-org/config
service: config load only on start, not init phase
2022-03-30 15:39:51 +03:00
575af66ddc service: config load only on start, not init phase
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-30 15:37:02 +03:00
dependabot[bot]
afb9e8c240
chore(deps): bump github.com/golang-jwt/jwt/v4 from 4.4.0 to 4.4.1
Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.4.0 to 4.4.1.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v4.4.0...v4.4.1)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-28 06:11:57 +00:00
c10f29ee74
Merge pull request #108 from unistack-org/improve
small improve
2022-03-27 01:39:33 +03:00
03410c4ab1 small improve
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-27 01:37:21 +03:00
3805d0f067
Merge pull request #107 from unistack-org/retries
client: determenistic retry backoff
2022-03-27 00:19:06 +03:00
680ac11ef9 client: determenistic retry backoff
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-27 00:16:22 +03:00
35ab6ae84e
Merge pull request #106 from unistack-org/jitter
jitter: add NewTickerContext
2022-03-26 18:01:31 +03:00
c6c2b0884e jitter: add NewTickerContext
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-26 17:59:19 +03:00
297a80da84
Merge pull request #105 from unistack-org/improve
small fixes
2022-03-25 14:27:29 +03:00
2d292db7bd small fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-25 14:24:20 +03:00
54c4287fab
Update README.md 2022-03-22 15:00:01 +03:00
9c074e5741
Merge pull request #103 from unistack-org/dependabot/github_actions/actions/cache-3
chore(deps): bump actions/cache from 2 to 3
2022-03-22 14:57:20 +03:00
290975eaf5
Merge pull request #104 from unistack-org/small_changes
config: add Validate func, small lint fixes
2022-03-22 14:57:03 +03:00
c64218d52c config: add Validate func, small lint fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-22 14:54:43 +03:00
dependabot[bot]
46c266a4a9
chore(deps): bump actions/cache from 2 to 3
Bumps [actions/cache](https://github.com/actions/cache) from 2 to 3.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](https://github.com/actions/cache/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-22 06:14:47 +00:00
5527b16cd8
Merge pull request #102 from unistack-org/cleanup
server: remove unparsed body from request and message
2022-03-21 15:26:20 +03:00
4904cad8ef server: remove unparsed body from request and message
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-21 15:23:41 +03:00
74633f4290
Merge pull request #101 from unistack-org/dependabot/go_modules/github.com/golang-jwt/jwt/v4-4.4.0
chore(deps): bump github.com/golang-jwt/jwt/v4 from 4.3.0 to 4.4.0
2022-03-18 16:29:26 +03:00
dependabot[bot]
c8ad4d772b
chore(deps): bump github.com/golang-jwt/jwt/v4 from 4.3.0 to 4.4.0
Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.3.0 to 4.4.0.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v4.3.0...v4.4.0)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-17 08:24:50 +00:00
91bd0f7efe Merge branch 'master' into v3 2022-03-17 11:23:08 +03:00
00dc7e1bb5 update go version
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-07 13:44:18 +03:00
5a5165a003
Merge pull request #99 from unistack-org/dependabot/go_modules/github.com/golang-jwt/jwt/v4-4.3.0
chore(deps): bump github.com/golang-jwt/jwt/v4 from 4.2.0 to 4.3.0
2022-03-07 13:16:48 +03:00
382e3d554b
Merge pull request #98 from unistack-org/dependabot/github_actions/golangci/golangci-lint-action-3.1.0
chore(deps): bump golangci/golangci-lint-action from 2 to 3.1.0
2022-03-07 13:16:37 +03:00
05a0c97fc6
Merge pull request #100 from unistack-org/dependabot/go_modules/go.unistack.org/micro-proto/v3-3.2.7
chore(deps): bump go.unistack.org/micro-proto/v3 from 3.2.1 to 3.2.7
2022-03-07 13:14:31 +03:00
dependabot[bot]
5e06ae1a42
chore(deps): bump go.unistack.org/micro-proto/v3 from 3.2.1 to 3.2.7
Bumps [go.unistack.org/micro-proto/v3](https://github.com/unistack-org/micro-proto) from 3.2.1 to 3.2.7.
- [Release notes](https://github.com/unistack-org/micro-proto/releases)
- [Commits](https://github.com/unistack-org/micro-proto/compare/v3.2.1...v3.2.7)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro-proto/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-07 10:11:59 +00:00
dependabot[bot]
7ac4ad4efa
chore(deps): bump github.com/golang-jwt/jwt/v4 from 4.2.0 to 4.3.0
Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v4.2.0...v4.3.0)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-07 10:11:54 +00:00
dependabot[bot]
01348bd9b2
chore(deps): bump golangci/golangci-lint-action from 2 to 3.1.0
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 2 to 3.1.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v2...v3.1.0)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-07 10:11:45 +00:00
2287c65118 update workflows
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-03-05 19:07:51 +03:00
b34bc7ffff
Merge pull request #97 from unistack-org/dependabot/go_modules/go.unistack.org/micro-proto/v3-3.2.7
chore(deps): bump go.unistack.org/micro-proto/v3 from 3.2.5 to 3.2.7
2022-03-02 09:42:24 +03:00
dependabot[bot]
2a0bf03d0a
chore(deps): bump go.unistack.org/micro-proto/v3 from 3.2.5 to 3.2.7
Bumps [go.unistack.org/micro-proto/v3](https://github.com/unistack-org/micro-proto) from 3.2.5 to 3.2.7.
- [Release notes](https://github.com/unistack-org/micro-proto/releases)
- [Commits](https://github.com/unistack-org/micro-proto/compare/v3.2.5...v3.2.7)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro-proto/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-02 06:42:04 +00:00
89114c291c
Merge pull request #94 from unistack-org/dependabot/go_modules/go.unistack.org/micro-proto/v3-3.2.5
chore(deps): bump go.unistack.org/micro-proto/v3 from 3.2.3 to 3.2.5
2022-02-28 09:12:18 +03:00
dependabot[bot]
b4b4320fac
chore(deps): bump go.unistack.org/micro-proto/v3 from 3.2.3 to 3.2.5
Bumps [go.unistack.org/micro-proto/v3](https://github.com/unistack-org/micro-proto) from 3.2.3 to 3.2.5.
- [Release notes](https://github.com/unistack-org/micro-proto/releases)
- [Commits](https://github.com/unistack-org/micro-proto/compare/v3.2.3...v3.2.5)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro-proto/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-28 06:12:01 +00:00
7b0d69115c
Merge pull request #90 from unistack-org/dependabot/go_modules/github.com/golang-jwt/jwt/v4-4.3.0
chore(deps): bump github.com/golang-jwt/jwt/v4 from 4.2.0 to 4.3.0
2022-02-25 10:00:00 +03:00
f054beb6e8
Merge pull request #92 from unistack-org/dependabot/github_actions/dependabot/fetch-metadata-1.2.1
chore(deps): bump dependabot/fetch-metadata from 1.1.1 to 1.2.1
2022-02-25 09:44:23 +03:00
9fb346594e
Merge pull request #93 from unistack-org/dependabot/github_actions/golangci/golangci-lint-action-3
chore(deps): bump golangci/golangci-lint-action from 2 to 3
2022-02-25 09:33:47 +03:00
dependabot[bot]
cbf6fbd780
chore(deps): bump golangci/golangci-lint-action from 2 to 3
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 2 to 3.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-25 06:18:58 +00:00
dependabot[bot]
0392bff282
chore(deps): bump dependabot/fetch-metadata from 1.1.1 to 1.2.1
Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.1.1 to 1.2.1.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.1.1...v1.2.1)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-23 06:14:10 +00:00
dependabot[bot]
75b1fe5dc6
chore(deps): bump github.com/golang-jwt/jwt/v4 from 4.2.0 to 4.3.0
Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v4.2.0...v4.3.0)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-10 06:31:09 +00:00
1f232ffba8
Merge pull request #89 from unistack-org/dependabot/go_modules/go.unistack.org/micro-proto/v3-3.2.3
chore(deps): bump go.unistack.org/micro-proto/v3 from 3.2.1 to 3.2.3
2022-02-02 09:15:27 +03:00
dependabot[bot]
7f43b64fc2
chore(deps): bump go.unistack.org/micro-proto/v3 from 3.2.1 to 3.2.3
Bumps [go.unistack.org/micro-proto/v3](https://github.com/unistack-org/micro-proto) from 3.2.1 to 3.2.3.
- [Release notes](https://github.com/unistack-org/micro-proto/releases)
- [Commits](https://github.com/unistack-org/micro-proto/compare/v3.2.1...v3.2.3)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro-proto/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-02 06:15:10 +00:00
d0d04a840a
Merge pull request #88 from unistack-org/master
merge master
2022-01-30 17:05:41 +03:00
1dda3f0dcc
Merge pull request #87 from unistack-org/register
register: DefaultDomain as var not as const
2022-01-30 17:05:19 +03:00
1abf5e7647 register: DefaultDomain as var not as const
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-01-30 17:04:52 +03:00
f06610c9c2
Merge pull request #86 from unistack-org/master
update micro-proto
2022-01-26 00:48:11 +03:00
df8560bb6f update micro-proto
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-01-26 00:47:22 +03:00
0257eae936
Merge pull request #85 from unistack-org/master
merge master
2022-01-25 00:41:46 +03:00
58f03d05e7
Merge pull request #84 from unistack-org/errors_marshal
errors: fix MarshalJSON func
2022-01-25 00:41:10 +03:00
60340a749b errors: fix MarshalJSON func
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-01-25 00:40:50 +03:00
56b0df5b7a
Merge pull request #83 from unistack-org/dependabot/go_modules/go.unistack.org/micro-proto/v3-3.2.0
chore(deps): bump go.unistack.org/micro-proto/v3 from 3.1.1 to 3.2.0
2022-01-24 09:15:46 +03:00
dependabot[bot]
bb59d5a2fd
chore(deps): bump go.unistack.org/micro-proto/v3 from 3.1.1 to 3.2.0
Bumps [go.unistack.org/micro-proto/v3](https://github.com/unistack-org/micro-proto) from 3.1.1 to 3.2.0.
- [Release notes](https://github.com/unistack-org/micro-proto/releases)
- [Commits](https://github.com/unistack-org/micro-proto/compare/v3.1.1...v3.2.0)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro-proto/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-24 06:14:48 +00:00
67d5dc7e28
Merge pull request #82 from unistack-org/master
errors: fix parsing
2022-01-21 19:14:08 +03:00
797c0f822d
Merge pull request #81 from unistack-org/errors
errors: fix parsing
2022-01-21 19:13:39 +03:00
8546140e22 errors: fix parsing
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-01-21 19:13:21 +03:00
92b125c1ce
Merge pull request #80 from unistack-org/master
merge master
2022-01-21 18:21:47 +03:00
8f7eebc24f
Merge pull request #79 from unistack-org/errors
errors: fix FromError and errors.Unmarshal
2022-01-21 18:21:13 +03:00
b0def96d14 errors: fix FromError and errors.Unmarshal
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-01-21 18:20:56 +03:00
927ca879b2
Merge pull request #78 from unistack-org/master
merge master
2022-01-21 00:51:14 +03:00
00450c9cc7
Merge pull request #77 from unistack-org/errors
errors: add proto
2022-01-21 00:50:32 +03:00
534bce2d20 errors: add proto
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-01-21 00:50:16 +03:00
53949be0cc
Merge pull request #76 from unistack-org/logger_test
add logger context test
2022-01-20 15:29:44 +03:00
d8fe2ff8b4 add logger context test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-01-20 15:29:21 +03:00
53b5ee2c6f
Merge pull request #75 from unistack-org/logger_test
logger: add logger Fields test
2022-01-20 00:31:23 +03:00
dfd85cd871 logger: add logger Fields test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-01-20 00:28:09 +03:00
52182261af
Merge pull request #74 from unistack-org/master
logger: fix Fields
2022-01-19 19:55:21 +03:00
1f3834e187
Merge pull request #73 from unistack-org/logger
logger: fix fields
2022-01-19 19:54:50 +03:00
0354873c3a logger: fix fields
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-01-19 19:54:33 +03:00
8e5e2167cd
Merge pull request #72 from unistack-org/master
lint fixes
2022-01-10 16:48:27 +03:00
c26a7db47c
Merge pull request #71 from unistack-org/lint
many lint fixes
2022-01-10 16:47:56 +03:00
74765b4c5f many lint fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2022-01-10 16:47:37 +03:00
8bd7323af1
Merge pull request #70 from unistack-org/dependabot/go_modules/go.unistack.org/micro-proto/v3-3.1.1
chore(deps): bump go.unistack.org/micro-proto/v3 from 3.1.0 to 3.1.1
2021-12-31 09:11:36 +03:00
dependabot[bot]
899dc8b3bc
chore(deps): bump go.unistack.org/micro-proto/v3 from 3.1.0 to 3.1.1
Bumps [go.unistack.org/micro-proto/v3](https://github.com/unistack-org/micro-proto) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/unistack-org/micro-proto/releases)
- [Commits](https://github.com/unistack-org/micro-proto/compare/v3.1.0...v3.1.1)

---
updated-dependencies:
- dependency-name: go.unistack.org/micro-proto/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-31 06:11:19 +00:00
6e6c31b5dd
Merge pull request #69 from unistack-org/master
merge master
2021-12-28 09:30:34 +03:00
94929878fe
Merge pull request #68 from unistack-org/improvements
improvements
2021-12-28 09:23:45 +03:00
8ce469a09e tracer: fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-12-28 09:18:52 +03:00
88788776d2 Merge branch 'master' into v3 2021-12-16 15:04:08 +03:00
e143e2b547 client: allow to set metadata for message
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-12-16 15:03:42 +03:00
a36f99e30b
Merge pull request #66 from unistack-org/minor_changes
config: add new error type
2021-11-30 07:35:27 +03:00
326ee53333 config: add new error type
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-11-30 07:34:49 +03:00
1244c5bb4d
Merge pull request #65 from unistack-org/master
merge changes from master
2021-11-24 00:59:00 +03:00
4ccc8a9c85
Merge pull request #64 from unistack-org/minor_changes
minor changes
2021-11-24 00:58:21 +03:00
8a2e84d489 minor changes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-11-24 00:57:59 +03:00
d29363b78d codec: add NewFrame helper
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-11-19 09:22:13 +03:00
734f751055
Merge pull request #63 from unistack-org/master
util/http: add type alias
2021-11-19 03:04:55 +03:00
55d8a9ee20 util/http: add type alias
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-11-19 03:04:08 +03:00
07c93042ba
Merge pull request #62 from unistack-org/master
merge stable
2021-11-18 16:01:10 +03:00
b9bbfdf159 config: add watch option helper
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-11-18 15:57:14 +03:00
fbad257acc
config: add helpers to load/save options (#60) 2021-11-18 15:46:30 +03:00
1829febb6e util/http: fix lint issues
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-11-09 17:07:52 +03:00
7838fa62a8 util/trie: import some code from chi router
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-11-09 16:34:05 +03:00
332803d8de update workflows
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 22:52:48 +03:00
11c868d476 Merge branch 'v3' 2021-10-27 22:51:58 +03:00
38d6e482d7 util/reflect: fix StructFields
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 22:51:35 +03:00
07d4085201 util/reflect: fix reflect methods
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-27 12:55:48 +03:00
45f30c0be3 util/reflect: ZeroFieldByPath and SetFieldByPath
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-26 14:12:37 +03:00
bcaea675a7 util/reflect: add method to zero struct field
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-25 14:41:19 +03:00
3087ba1d73 regen
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-23 23:45:53 +03:00
3f5b19497c meter: add Clone method
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-09 23:50:57 +03:00
37d937d7ae meter: add missing options
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-09 19:16:45 +03:00
7d68f2396e tracer: dont return noop from context
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-07 22:46:47 +03:00
0854a7ea72 micro: add simple test
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-07 20:59:27 +03:00
5eb0e56373 move all imports to own domain
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-02 19:55:07 +03:00
6af837fd25 fixup workflows
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-02 18:42:20 +03:00
ada59119cc util/http: add test case
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-02 18:34:22 +03:00
8abc913b28 codec: add MarshalAppend func
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-10-01 01:08:24 +03:00
3247d144a8 lint fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-30 21:13:13 +03:00
7b2e3cc8aa lint fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-30 21:00:02 +03:00
8688179acd lint fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-30 20:32:59 +03:00
3e40bac5f4 config: add helper funcs
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-30 01:24:16 +03:00
e3fee6f8a6 util/http: add trie case insesitive matching
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-29 13:41:47 +03:00
15c020fac5 fix lint
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-29 13:19:07 +03:00
3bc046e5d4 broker/memory: simplify code
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-29 13:10:11 +03:00
542f36cfa5 util/reflect: fix tests, lint fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-29 13:09:48 +03:00
8237e6a08e util/router: drop google copy of pattern matcher in favour of util/http trie
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-29 13:09:22 +03:00
ecb60e4dc5 fix lint
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-28 23:43:43 +03:00
a1999ff81c util/http: trie add more tests
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-28 01:02:28 +03:00
d0f2bc8346 util/http: add trie matching func
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-27 23:30:53 +03:00
dependabot[bot]
dd29bf457e
chore(deps): bump actions/github-script from 4 to 5 (#58)
Bumps [actions/github-script](https://github.com/actions/github-script) from 4 to 5.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-27 09:33:25 +03:00
d062c248e3 codec: fieldaligment
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-22 17:09:26 +03:00
875f66d36e codec: implement proto v1 message for Frame
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-22 16:59:52 +03:00
818a0e6356 codec: add context helper funcs
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-22 01:07:27 +03:00
56e02ec463 codec: add ability to pass codec options
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-22 00:57:10 +03:00
6ca851401d update workflow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-21 21:46:23 +03:00
bd8216b397 update workflows
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-17 07:47:23 +03:00
2b13b3f128 Revert "update workflows"
This reverts commit 9957380b6d.
2021-09-17 07:42:46 +03:00
9957380b6d update workflows
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-17 07:41:17 +03:00
e10f8c0fa0 util/id: move tests to micro-tests repo
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-16 15:31:01 +03:00
45252fe4a6 enable automerge
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-16 10:30:59 +03:00
faad082efe util/rand: add Shuffle func
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-15 17:51:25 +03:00
8ab35cbd9b update dependabot
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-12 16:17:28 +03:00
ad58ab6943 fix codeql issue
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-06 10:51:13 +03:00
0e97049e1d
Create SECURITY.md 2021-09-06 10:49:18 +03:00
edb0bbf9cf add codeql
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-09-06 10:30:21 +03:00
dependabot[bot]
1b01bd22a6
build(deps): bump github.com/unistack-org/micro-proto (#57) 2021-09-06 06:56:44 +00:00
2fbaa26f0f logger: add Clone method
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-30 16:21:01 +03:00
35d3e4b332 logger: breaking changes to log level parsing
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-30 16:21:01 +03:00
dependabot[bot]
e98a93d530
build(deps): bump github.com/unistack-org/micro-proto (#56)
Bumps [github.com/unistack-org/micro-proto](https://github.com/unistack-org/micro-proto) from 0.0.5 to 0.0.8.
- [Release notes](https://github.com/unistack-org/micro-proto/releases)
- [Commits](https://github.com/unistack-org/micro-proto/compare/v0.0.5...v0.0.8)

---
updated-dependencies:
- dependency-name: github.com/unistack-org/micro-proto
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-30 16:20:10 +03:00
e3545532e8 minor changes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-21 01:00:10 +03:00
09653c2fb2 util/id: specify default size for uuid behaviour
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-20 22:48:03 +03:00
70adfeab0d fix flow
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-20 22:44:17 +03:00
a45b672c98 drop uuid and use modified nanoid
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-20 22:40:48 +03:00
4509323cae update and regen all
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-16 23:56:50 +03:00
b3f4c670d5 regen all
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-16 18:57:19 +03:00
778dd449e2 logger: add NewStdLogger and RedirectStdLogger
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-06 13:45:11 +03:00
1d16983b67 logger: add NewStdLogger that can be used as std *log.Logger
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-06 11:52:04 +03:00
f386bffd37 logger: change logger interface
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-06 02:15:57 +03:00
772bde7938 network/tunnel/broker: fix metadata compile issue
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-06 02:14:56 +03:00
ea16f5f825 config/default: not implement watcher as it cant change
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-04 16:04:58 +03:00
c2f34df493 config: minor changes to split config and watcher files
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-04 13:51:43 +03:00
efe215cd60 config/default: watcher send changes only on non nil
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-04 12:25:29 +03:00
b4f332bf0d config/default: return error on Next() call
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-04 01:15:50 +03:00
f47fbb1030 config: add jitter interval for watcher to avoid dos
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-04 00:37:56 +03:00
1e8e57a708 config/default: minor changes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-03 00:49:21 +03:00
dependabot[bot]
5d0959b0a1
build(deps): bump github.com/golang-jwt/jwt (#54)
Bumps [github.com/golang-jwt/jwt](https://github.com/golang-jwt/jwt) from 3.2.1+incompatible to 3.2.2+incompatible.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v3.2.1...v3.2.2)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-03 00:27:35 +03:00
fa8fb3aed7
fixes and improvements (#55)
* util/router: sync from github
* config: add watcher interface

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-08-03 00:24:40 +03:00
cfd2d53a79 config: cleanup tests
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-27 23:58:45 +03:00
d306f77ffc util/token/jwt: change library
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-27 23:58:29 +03:00
e5b0a7e20d server: add BatchSubscriber
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-27 23:58:06 +03:00
9a5b158b4d change jwt lib
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-27 12:43:56 +03:00
af8d81f3c6 logger: add DefaultCallerSkipCount
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-26 09:48:15 +03:00
5c9b3dae33 broker: improve option naming, move BatchBroker to Broker interface
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-24 16:16:18 +03:00
9f3957d101 client: improve option naming, add BatchPublish to noop client
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-24 16:14:42 +03:00
8fd8bdcb39 logger: fix default logger funcs
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-24 15:22:01 +03:00
80e3d239ab broker/memory: optimize
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-23 15:12:20 +03:00
419cd486cf broker/memory: cleanup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-23 15:06:10 +03:00
e64269b2a8 broker: add BatchBroker interface to avoid breaking older brokers
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-23 12:55:36 +03:00
d18429e024 metadata: add HeaderAuthorization
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-23 12:17:00 +03:00
675e121049 metadata: add default headers
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-23 12:03:18 +03:00
d357fb1e0d WIP: broker batch processing
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-22 22:53:44 +03:00
e4673bcc50 remove old cruft
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-22 15:45:44 +03:00
a839f75a2f util/reflect: add new funcs
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-22 15:45:31 +03:00
a7e6d61b95 meter: fast path for only one label
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-21 14:29:13 +03:00
650d167313 meter: add BuildLabels func that sorts and deletes duplicates
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-21 14:10:20 +03:00
c6ba2a91e6 meter: BuildName func to combine metric name with labels into string
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-21 12:39:59 +03:00
7ece08896f server: use 127.0.0.1:0 if no address provided
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-17 01:57:39 +06:00
dependabot[bot]
57f6f23294
build(deps): bump github.com/google/uuid from 1.2.0 to 1.3.0 (#53)
Bumps [github.com/google/uuid](https://github.com/google/uuid) from 1.2.0 to 1.3.0.
- [Release notes](https://github.com/google/uuid/releases)
- [Commits](https://github.com/google/uuid/compare/v1.2.0...v1.3.0)

---
updated-dependencies:
- dependency-name: github.com/google/uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-16 00:27:56 +03:00
09e6fa2fed flow: implement new methods, add Async ExecutionOption
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-16 00:17:16 +03:00
10a09a5c6f flow: improve store
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-15 22:56:34 +03:00
b4e5d9462a util/router: move some messages to Trace level
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-15 22:56:34 +03:00
96aa0b6906 store/memory: fix List
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-15 22:53:12 +03:00
f54658830d store/memory: fixup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-15 12:11:55 +03:00
1e43122660 store/memory: small fixups for flow usage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-15 11:59:35 +03:00
42800fa247 flow: improve steps handling
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-14 17:12:54 +03:00
5b9c810653 logger: add compile time test for interface compat
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-14 17:12:09 +03:00
c3def24bf4 store: add Wrappers support, create Namespace wrapper
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-14 17:11:37 +03:00
0d1ef31764 client: change AuthToken option signature
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-09 10:47:40 +03:00
d49afa230f logger: add omit logger
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-05 23:04:20 +03:00
e545eb4e13 logger: add wrapper support
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-05 22:32:47 +03:00
f28b107372 broker: fix RawMessage marshal
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-01 23:23:01 +03:00
c592fabe2a minor fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-01 15:56:22 +03:00
eb107020c7 broker: replace Message.Body []byte slice to RawMessage
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-07-01 15:11:17 +03:00
bd4d4c363e
flow improvements (#52)
* flow improvements

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-06-30 17:50:58 +03:00
2a548634fd config: add Save/Load options
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-06-20 23:57:13 +03:00
598dddc476 util/reflect: fix time.Time StructFields parsing
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-06-19 15:58:36 +03:00
887b48f1e7 util/reflect: improve StructFields func
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-06-17 12:53:23 +03:00
6e55d07636 client: allow to publish body only
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-06-13 12:20:35 +03:00
919520219c client: WithBodyOnly publish option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-06-11 14:14:41 +03:00
60a5e737f8 util/reflect: return pointer from helper funcs
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-05-25 22:44:22 +03:00
34f0b209cc codec: add ability to control codec via struct tags
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-05-25 22:20:39 +03:00
ba8e1889fe dependabot
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-05-16 17:18:56 +03:00
dae5c57a60
Create dependabot.yml 2021-05-15 14:46:22 +03:00
ea590d57df
meter/wrapper: add inflight request/message count (#47)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-05-10 17:59:40 +03:00
Renovate Bot
9aa6969836 fix(deps): update golang.org/x/net commit hash to 4163338 2021-05-10 14:29:32 +00:00
Renovate Bot
c00c705c24 fix(deps): update golang.org/x/net commit hash to 16afe75 2021-05-08 09:02:01 +00:00
Renovate Bot
0239f795d8 fix(deps): update golang.org/x/net commit hash to 7fd8e65 2021-05-03 10:07:41 +00:00
Renovate Bot
e69b43881d fix(deps): update golang.org/x/net commit hash to f8dd838 2021-05-01 23:20:59 +00:00
3a48a613fe not fail on lint now
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-27 08:36:11 +03:00
86626c5922 fieldalignment of all structs to save memory
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-27 08:32:47 +03:00
ee11f39a2f fieldaligment
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-27 00:03:18 +03:00
3bdfdd8fd2 meter: fix labels
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-27 00:03:18 +03:00
6dfdff7fd8 fieldaligment
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-27 00:03:18 +03:00
00a4785df3 fixup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-27 00:03:18 +03:00
Renovate Bot
bae3b0ef94 fix(deps): update golang.org/x/net commit hash to 5f58ad6 2021-04-23 23:52:34 +00:00
Renovate Bot
89b0565062 fix(deps): update golang.org/x/net commit hash to 4e50805 2021-04-22 03:04:18 +00:00
1f8b0aeb61 store: remove unused Value type
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-22 00:57:06 +03:00
Renovate Bot
5b6f849e0a fix(deps): update golang.org/x/net commit hash to 798c215 2021-04-20 23:13:59 +00:00
Renovate Bot
3b416fffde fix(deps): update golang.org/x/net commit hash to d25e304 2021-04-20 15:04:17 +00:00
3a60103aed server: drop Internal option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-20 12:45:14 +03:00
41837a67f8 register: drop verbose values export
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-20 12:39:21 +03:00
852f19598d util/reflect: fix protobuf field name detection
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-19 11:34:28 +03:00
6537b35773 util/reflect: add interface merging
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-19 01:19:37 +03:00
b733f1316f remove stale generate stuff
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-16 17:36:27 +03:00
Renovate Bot
840af5574c fix(deps): update golang.org/x/net commit hash to e915ea6 2021-04-16 00:56:52 +00:00
Renovate Bot
56e5b7001c fix(deps): update golang.org/x/net commit hash to 0645797 2021-04-14 21:41:15 +00:00
Renovate Bot
11dc6fd752 fix(deps): update golang.org/x/net commit hash to afb366f 2021-04-10 11:09:36 +00:00
a2695d8699 util/reflect: rewrite struct merging with map
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-10 01:22:40 +03:00
618421de05 client: allow to set content-type for call
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-09 23:09:12 +03:00
Renovate Bot
30baaabd9f fix(deps): update golang.org/x/net commit hash to a5a99cb 2021-04-05 19:46:46 +00:00
df5bce1191 util/reflect: fix StructURLValues
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-03 11:50:23 +03:00
Renovate Bot
089d0fe4df fix(deps): update golang.org/x/net commit hash to 0fccb6f 2021-03-31 22:50:38 +00:00
a06f535303 util/reflect: add StructURLValues func
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-01 00:30:26 +03:00
Renovate Bot
eba586a329 fix(deps): update golang.org/x/net commit hash to cb1fcc7 2021-03-31 09:19:27 +00:00
Renovate Bot
d74a8645e8 fix(deps): update golang.org/x/net commit hash to e572328 2021-03-31 00:52:53 +00:00
Renovate Bot
5a00786192 fix(deps): update golang.org/x/net commit hash to cd0ac97 2021-03-30 22:28:38 +00:00
Renovate Bot
b3e9941634 fix(deps): update golang.org/x/net commit hash to c8897c2 2021-03-30 16:02:28 +00:00
Renovate Bot
a5a5904302 fix(deps): update golang.org/x/net commit hash to 22f4162 2021-03-30 11:44:00 +00:00
Renovate Bot
a59832e57e fix(deps): update golang.org/x/net commit hash to df645c7 2021-03-30 05:11:12 +00:00
0e42033e7f meter/handler: more idiomatic option handling
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-29 17:51:44 +03:00
52d8255974 service init with own context
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-28 23:42:02 +03:00
9830cb48a9 fix compilation
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-28 19:31:03 +03:00
92d7ab2105 regen handlers
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-28 19:28:01 +03:00
d2935ef399 meter/handler: fix proto and generated code
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-28 19:18:11 +03:00
ce4c96ae0a server/health: add health check handler
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-28 19:18:11 +03:00
Renovate Bot
14026d15be fix(deps): update golang.org/x/net commit hash to 61e0566 2021-03-27 01:29:30 +00:00
Renovate Bot
2df0c7643e fix(deps): update golang.org/x/net commit hash to 6b15177 2021-03-26 19:17:30 +00:00
e13c2c48fd client: use router.DefaultRouter in NewOptions helper
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-26 17:45:55 +03:00
8db55d2e55 router: use dns as default router
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-26 17:43:39 +03:00
ed61cad961 meter/handler: regen
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-26 15:45:09 +03:00
040fc4548f client: add TLSConfig option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-26 15:44:34 +03:00
6189a1b980 add SkipEndpoints for wrappers
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-25 23:30:38 +03:00
eb2a450a7b meter/handler: fix func signature
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-25 12:36:48 +03:00
Renovate Bot
d2a30a5da1 fix(deps): update golang.org/x/net commit hash to d1beb07 2021-03-24 23:28:38 +00:00
65889c66f6 meter/wrapper: add DefaultSkipEndpoints
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-25 00:06:38 +03:00
dcdf133d5b server: mask router
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-24 13:26:36 +03:00
8742b55305 auth: fix Init method
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-24 13:12:03 +03:00
4a64ee72f7 meter/handler: provide default metrics handler
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-23 17:26:29 +03:00
881d7afeea meter/handler: provide initial meter handler
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-23 17:12:13 +03:00
8c95448535 util/reflect: add IsZero helper
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-21 16:17:50 +03:00
c1dc041d8c client: fix NewOptions with CallOptions filling
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-16 19:09:49 +03:00
Renovate Bot
25be0ac0f0 fix(deps): update golang.org/x/net commit hash to d523dce 2021-03-16 13:12:17 +00:00
Renovate Bot
86f73cac4e fix(deps): update golang.org/x/net commit hash to 34ac3e1 2021-03-15 22:09:13 +00:00
485eda6ce9 meter/wrapper: fix wrapper build
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-15 00:49:26 +03:00
dbbdb24631 meter: rework labels
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-15 00:44:13 +03:00
723ceb4f32 regsiter: fix extractor
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-12 16:09:20 +03:00
bac9869bb3 register: support map in ExtractValue
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-12 15:48:05 +03:00
610427445f codec: provide proto for codec.Frame
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-11 07:42:42 +03:00
Renovate Bot
c84a66c713 fix(deps): update module github.com/imdario/mergo to v0.3.12 2021-03-10 00:35:57 +00:00
00eaae717b lint fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-06 23:44:54 +03:00
a102e95433 spell fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-06 23:33:37 +03:00
39f66cc86c add logger wrapper, fix default logger fields method
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-06 23:26:47 +03:00
bbbcb22565 fieldalignment of all structs to save memory
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-06 19:45:13 +03:00
cb70dfa664 meter/wrapper: use meter.DefaultMeter in NewOptions
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-05 17:40:03 +03:00
1f0482fbd5 tracer: finalize tracer implementation
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-04 01:12:16 +03:00
a862562284 fixup domain in ListServices
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-03 18:16:54 +03:00
c320c23913 metadata: minor fixup for NewXXXContext functions
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-01 13:00:53 +03:00
Renovate Bot
ae848ba8bb fix(deps): update golang.org/x/net commit hash to e18ecbb 2021-02-26 19:46:14 +00:00
Renovate Bot
8e264cbb3e fix(deps): update golang.org/x/net commit hash to 39120d0 2021-02-26 12:05:10 +00:00
Renovate Bot
54e523ab3f fix(deps): update golang.org/x/net commit hash to 3d97a24 2021-02-26 08:40:37 +00:00
09973af099 server: add error helper
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-22 00:52:18 +03:00
3247da3dd0 metadata: add Pairs helper func
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-22 00:08:05 +03:00
b505455f7c run go mod tidy in renovate update
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-21 23:55:19 +03:00
293949f081 metadata: add Append func to Incoming/Outgoing context
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-21 23:54:59 +03:00
8d7e442b3a server: add SubscriberBodyOnly option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-20 18:12:13 +03:00
renovate[bot]
f7b5211af3
fix(deps): update golang.org/x/net commit hash to 5f55cee (#20)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-20 11:40:35 +03:00
7eb6d030dc meter: fix internal labels sorting
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-18 15:57:42 +03:00
47e75c31c7 meter: export labels len method
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-18 14:41:51 +03:00
20ff5eed22
meter: initial wrapper import (#19)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-18 14:35:10 +03:00
d23ca8db73
Merge pull request #18 from unistack-org/flow
flow: initial tests
2021-02-18 12:49:32 +03:00
4dd28ac720 go 1.15 => 1.16
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-18 12:49:02 +03:00
240b6016df flow: add initial flow dag
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-18 12:44:37 +03:00
cf2aa827e4 update to go 1.16
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-18 12:44:18 +03:00
5596345382 util/rand: replace all non crypto rand stuff with own rand package
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-18 12:44:18 +03:00
67748a2132 util/reflect: import own path based interface lookup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-14 23:33:01 +03:00
c2333a9f35 gh actions not fail on lint errors
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-14 16:26:40 +03:00
4ec4c277b7 lint: fix all major issues
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-14 16:16:01 +03:00
a11dd00174 profiler: fix import
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-14 14:25:04 +03:00
cc7ebedf22 debug/profile: move to profiler interface
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-14 14:02:51 +03:00
e5bf1448f4 lint fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-14 11:28:50 +03:00
f182bba6ff debug/log: remove stale files
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-13 20:01:57 +03:00
1f8810599b go.mod cleanup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-13 15:37:33 +03:00
82248eb3b0
many lint fixes and optimizations (#17)
* util/kubernetes: drop stale files
* debug/log/kubernetes: drop stale files
* util/scope: remove stale files
* util/mdns: drop stale files
* lint fixes

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-13 15:35:56 +03:00
abb9937787
fix lint issues (#16)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-13 01:46:16 +03:00
fd5ed64729 metadata: fix nil metadata from FromIncoming/FromOutgoing context
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-12 17:10:35 +03:00
6751060d05 move memory implementations to core micro repo
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-12 16:33:16 +03:00
ef664607b4 automerge minor version updates
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-12 11:48:09 +03:00
62e482a14b move renovate
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-10 12:44:56 +03:00
a390ebf80f fix renovate.json
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-10 12:43:29 +03:00
9a44960be7 another fix
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-10 10:52:33 +03:00
c846c59b9b fix renovate.json
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-10 10:39:37 +03:00
renovate[bot]
902bf6326b
chore(deps): update golangci/golangci-lint-action action to v2 (#14)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-10 00:35:56 +03:00
renovate[bot]
bddf3bf502
chore(deps): update actions/setup-go action to v2 (#13)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-09 23:04:23 +03:00
renovate[bot]
284131da98
Add renovate.json (#12)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-09 23:01:50 +03:00
927c7ea3c2 metadata: allow to modify metadata via SetXXX functions
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-09 12:46:14 +03:00
0e51a79bb6 metadata: split context to incoming and outgoing
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-09 01:08:45 +03:00
1de9911b73 util/reflect: add missing types for merge
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-06 18:13:43 +03:00
b4092c6619 util/reflect: improve merge for map
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-05 18:27:16 +03:00
024868bfd7 api: encode body param in endpoint
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-02 19:35:16 +03:00
a0bbfd6d02 provide compa options
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-02 15:37:12 +03:00
2cb6843773 codec: fix noop codec
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 23:18:12 +03:00
87e1480077 config: add name to each config imp
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 16:18:17 +03:00
bcd7f6a551 codec: fix noop codec to handle *broker.Message
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 16:07:21 +03:00
925b3af46b register: fix options
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 15:06:47 +03:00
ef4efa6a6b rename util
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 14:50:09 +03:00
125646d89b add Name func option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 14:07:35 +03:00
7af7649448 store: add Name func
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 14:02:54 +03:00
827d467077 micro: rewrite options to support multiple building blocks
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 13:17:32 +03:00
ac8a3a12c4 meter: complete interface
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-27 00:54:19 +03:00
286785491c store: improve interface
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-26 02:09:26 +03:00
375 changed files with 19655 additions and 18029 deletions

View File

@ -0,0 +1,18 @@
---
name: Bug report
about: For reporting bugs in micro
title: "[BUG]"
labels: ''
assignees: ''
---
**Describe the bug**
1. What are you trying to do?
2. What did you expect to happen?
3. What happens instead?
**How to reproduce the bug:**
If possible, please include a minimal code snippet here.

View File

@ -0,0 +1,17 @@
---
name: Feature request / Enhancement
about: If you have a need not served by micro
title: "[FEATURE]"
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -0,0 +1,8 @@
---
name: Question
about: Ask a question about micro
title: ''
labels: ''
assignees: ''
---

28
.gitea/autoapprove.yml Normal file
View File

@ -0,0 +1,28 @@
name: "autoapprove"
on:
pull_request_target:
types: [assigned, opened, synchronize, reopened]
workflow_run:
workflows: ["prbuild"]
types:
- completed
permissions:
pull-requests: write
contents: write
jobs:
autoapprove:
runs-on: ubuntu-latest
steps:
- name: approve
run: [ "curl -o tea https://dl.gitea.com/tea/main/tea-main-linux-amd64",
"chmod +x ./tea",
"./tea login add --name unistack --token ${{ secrets.GITHUB_TOKEN }} --url https://git.unistack.org",
"./tea pr --repo ${{ github.event.repository.name }}"
]
if: github.actor == 'vtolstov'
id: approve
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

3
.gitea/pkgdashcli.yaml Normal file
View File

@ -0,0 +1,3 @@
branches:
- master
- v3

View File

@ -0,0 +1,24 @@
on:
push:
branches:
- 'main'
- 'master'
- 'v3'
schedule:
#- cron: '* * * * *'
- cron: '@hourly'
jobs:
autoupdate:
runs-on: ubuntu-latest
steps:
- name: setup-go
uses: https://gitea.com/actions/setup-go@v3
with:
go-version: 1.21
- name: checkout
uses: https://gitea.com/actions/checkout@v3
- name: get pkgdashcli
run: GOPROXY=direct GONOSUMDB="git.unistack.org/*" GONOPROXY="git.unistack.org/*" GOBIN=/bin go install git.unistack.org/unistack-org/pkgdash/cmd/pkgdashcli@latest
- name: pkgdashcli check
run: /bin/pkgdashcli check

View File

@ -0,0 +1,30 @@
name: Go
on:
push:
branches: [ master, v3 ]
pull_request:
branches: [ master, v3 ]
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup
uses: actions/setup-go@v4
with:
go-version: stable
- name: coverage
run: go test -v -coverprofile coverage.out ./...
- name: badge
uses: ncruces/go-coverage-report@main
with:
coverage-file: coverage.out
reuse-go: true
amend: true

24
.gitea/workflows/lint.yml Normal file
View File

@ -0,0 +1,24 @@
name: lint
on:
pull_request:
branches:
- master
- v3
jobs:
lint:
name: lint
runs-on: ubuntu-latest
steps:
- name: setup-go
uses: https://gitea.com/actions/setup-go@v3
with:
go-version: 1.21
- name: checkout
uses: https://gitea.com/actions/checkout@v3
- name: deps
run: go get -v -d ./...
- name: lint
uses: https://github.com/golangci/golangci-lint-action@v3.4.0
continue-on-error: true
with:
version: v1.52

23
.gitea/workflows/pr.yml Normal file
View File

@ -0,0 +1,23 @@
name: pr
on:
pull_request:
branches:
- master
- v3
jobs:
test:
name: test
runs-on: ubuntu-latest
steps:
- name: checkout
uses: https://gitea.com/actions/checkout@v3
- name: setup-go
uses: https://gitea.com/actions/setup-go@v3
with:
go-version: 1.21
- name: deps
run: go get -v -t -d ./...
- name: test
env:
INTEGRATION_TESTS: yes
run: go test -v -mod readonly -race -coverprofile=coverage.txt -covermode=atomic ./...

View File

@ -0,0 +1,9 @@
## Pull Request template
Please, go through these steps before clicking submit on this PR.
1. Give a descriptive title to your PR.
2. Provide a description of your changes.
3. Make sure you have some relevant tests.
4. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if applicable).
**PLEASE REMOVE THIS TEMPLATE BEFORE SUBMITTING**

View File

@ -0,0 +1,26 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
commit-message:
prefix: "chore"
include: "scope"
# Maintain dependencies for Golang
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"
commit-message:
prefix: "chore"
include: "scope"

View File

@ -0,0 +1,24 @@
name: "autoapprove"
on:
pull_request_target:
types: [assigned, opened, synchronize, reopened]
workflow_run:
workflows: ["prbuild"]
types:
- completed
permissions:
pull-requests: write
contents: write
jobs:
autoapprove:
runs-on: ubuntu-latest
steps:
- name: approve
uses: hmarr/auto-approve-action@v3
if: github.actor == 'vtolstov' || github.actor == 'dependabot[bot]'
id: approve
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -0,0 +1,21 @@
name: "automerge"
on:
pull_request_target:
types: [assigned, opened, synchronize, reopened]
permissions:
pull-requests: write
contents: write
jobs:
automerge:
runs-on: ubuntu-latest
if: github.actor == 'vtolstov'
steps:
- name: merge
id: merge
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.TOKEN}}

View File

@ -1,55 +1,40 @@
name: build name: build
on: on:
push: push:
branches: branches:
- master - master
- v3
jobs: jobs:
test: test:
name: test name: test
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: setup - name: setup
uses: actions/setup-go@v1 uses: actions/setup-go@v3
with: with:
go-version: 1.15 go-version: 1.17
- name: checkout
uses: actions/checkout@v3
- name: cache - name: cache
uses: actions/cache@v2 uses: actions/cache@v3
with: with:
path: ~/go/pkg/mod path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go- restore-keys: ${{ runner.os }}-go-
- name: sdk checkout - name: deps
uses: actions/checkout@v2
- name: sdk deps
run: go get -v -t -d ./... run: go get -v -t -d ./...
- name: sdk test - name: test
env: env:
INTEGRATION_TESTS: yes INTEGRATION_TESTS: yes
run: go test -mod readonly -v ./... run: go test -mod readonly -v ./...
- name: tests checkout
uses: actions/checkout@v2
with:
repository: unistack-org/micro-tests
ref: refs/heads/master
path: micro-tests
fetch-depth: 1
- name: tests deps
run: |
cd micro-tests
go mod edit -replace="github.com/unistack-org/micro/v3=../"
go get -v -t -d ./...
- name: tests test
env:
INTEGRATION_TESTS: yes
run: cd micro-tests && go test -mod readonly -v ./...
lint: lint:
name: lint name: lint
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: lint - name: lint
uses: golangci/golangci-lint-action@v1 uses: golangci/golangci-lint-action@v3.4.0
continue-on-error: true continue-on-error: true
with: with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.

View File

@ -0,0 +1,39 @@
name: "codecov"
on:
workflow_run:
workflows: ["build"]
types:
- completed
push:
branches: [ v3 ]
pull_request:
branches: [ v3 ]
schedule:
- cron: '34 1 * * 0'
jobs:
codecov:
name: codecov
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup
uses: actions/setup-go@v3
with:
go-version: 1.17
- name: Run coverage
run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./...
- name: codecov
uses: codecov/codecov-action@v3.1.1

View File

@ -0,0 +1,78 @@
# 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:
workflow_run:
workflows: ["prbuild"]
types:
- completed
push:
branches: [ master, v3 ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master, v3 ]
schedule:
- cron: '34 1 * * 0'
jobs:
analyze:
name: analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
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
uses: actions/checkout@v3
- name: setup
uses: actions/setup-go@v3
with:
go-version: 1.17
# Initializes the CodeQL tools for scanning.
- name: init
uses: github/codeql-action/init@v2
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@v2
# 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: analyze
uses: github/codeql-action/analyze@v2

View File

@ -0,0 +1,27 @@
name: "dependabot-automerge"
on:
pull_request_target:
types: [assigned, opened, synchronize, reopened]
permissions:
pull-requests: write
contents: write
jobs:
automerge:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- name: metadata
id: metadata
uses: dependabot/fetch-metadata@v1.3.6
with:
github-token: "${{ secrets.TOKEN }}"
- name: merge
id: merge
if: ${{contains(steps.metadata.outputs.dependency-names, 'go.unistack.org')}}
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.TOKEN}}

View File

@ -0,0 +1,40 @@
name: prbuild
on:
pull_request:
branches:
- master
- v3
jobs:
test:
name: test
runs-on: ubuntu-latest
steps:
- name: setup
uses: actions/setup-go@v3
with:
go-version: 1.17
- name: checkout
uses: actions/checkout@v3
- name: cache
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-
- name: deps
run: go get -v -t -d ./...
- name: test
env:
INTEGRATION_TESTS: yes
run: go test -mod readonly -v ./...
lint:
name: lint
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: lint
uses: golangci/golangci-lint-action@v3.4.0
continue-on-error: true
with:
version: v1.30

15
.github/generate.sh vendored
View File

@ -1,15 +0,0 @@
#!/bin/bash -e
find . -type f -name '*.pb.*.go' -o -name '*.pb.go' -a ! -name 'message.pb.go' -delete
PROTOS=$(find . -type f -name '*.proto' | grep -v proto/google/api)
mkdir -p proto/google/api
curl -s -o proto/google/api/annotations.proto -L https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/annotations.proto
curl -s -o proto/google/api/http.proto -L https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/http.proto
for PROTO in $PROTOS; do
echo $PROTO
protoc -I./proto -I. -I$(dirname $PROTO) --go-grpc_out=paths=source_relative:. --go_out=paths=source_relative:. --micro_out=paths=source_relative:. $PROTO
done
rm -r proto

13
.github/stale.sh vendored
View File

@ -1,13 +0,0 @@
#!/bin/bash -ex
export PATH=$PATH:$(pwd)/bin
export GO111MODULE=on
export GOBIN=$(pwd)/bin
#go get github.com/rvflash/goup@v0.4.1
#goup -v ./...
#go get github.com/psampaz/go-mod-outdated@v0.6.0
go list -u -m -mod=mod -json all | go-mod-outdated -update -direct -ci || true
#go list -u -m -json all | go-mod-outdated -update

View File

@ -1,62 +0,0 @@
name: prbuild
on:
pull_request:
branches:
- master
jobs:
test:
name: test
runs-on: ubuntu-latest
steps:
- name: setup
uses: actions/setup-go@v1
with:
go-version: 1.15
- name: cache
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-
- name: sdk checkout
uses: actions/checkout@v2
- name: sdk deps
run: go get -v -t -d ./...
- name: sdk test
env:
INTEGRATION_TESTS: yes
run: go test -mod readonly -v ./...
- name: tests checkout
uses: actions/checkout@v2
with:
repository: unistack-org/micro-tests
ref: refs/heads/master
path: micro-tests
fetch-depth: 1
- name: tests deps
run: |
cd micro-tests
go mod edit -replace="github.com/unistack-org/micro/v3=../"
go get -v -t -d ./...
- name: tests test
env:
INTEGRATION_TESTS: yes
run: cd micro-tests && go test -mod readonly -v ./...
lint:
name: lint
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
- name: lint
uses: golangci/golangci-lint-action@v1
continue-on-error: true
with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v1.30
# Optional: working directory, useful for monorepos
# working-directory: somedir
# Optional: golangci-lint command line arguments.
# args: --issues-exit-code=0
# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true

3
.gitignore vendored
View File

@ -1,6 +1,8 @@
# Develop tools # Develop tools
/.vscode/ /.vscode/
/.idea/ /.idea/
.idea
.vscode
# Binaries for programs and plugins # Binaries for programs and plugins
*.exe *.exe
@ -13,6 +15,7 @@
_obj _obj
_test _test
_build _build
.DS_Store
# Architecture specific extensions/prefixes # Architecture specific extensions/prefixes
*.[568vq] *.[568vq]

View File

@ -1,40 +1,42 @@
run: run:
deadline: 5m concurrency: 4
modules-download-mode: readonly issues-exit-code: 1
skip-dirs: tests: true
- util/mdns.new
skip-files: linters-settings:
- ".*\\.pb\\.go$" govet:
- ".*\\.pb\\.micro\\.go$" check-shadowing: true
enable:
- fieldalignment
linters: linters:
disable-all: false
enable-all: false
enable: enable:
- megacheck
- staticcheck
- deadcode
- varcheck
- gosimple
- unused
- prealloc
- scopelint
- gocritic
- goimports
- unconvert
- govet - govet
- nakedret - errcheck
- structcheck - govet
- gosec - ineffassign
disable: - staticcheck
- maligned
- interfacer
- typecheck - typecheck
- dupl - unused
output: - spancheck
format: colored-line-number - bodyclose
# print lines of code with issue, default is true - gci
print-issued-lines: true - goconst
# print linter name in the end of issue text, default is true - gocritic
print-linter-name: true - gosimple
# make issues output unique by line, default is true - gofmt
uniq-by-line: true - gofumpt
- goimports
- revive
- gosec
- makezero
- misspell
- nakedret
- nestif
- nilerr
- noctx
- prealloc
- unconvert
- unparam
- unused
disable-all: false

View File

@ -1,4 +1,4 @@
# Micro [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Doc](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/unistack-org/micro/v3?tab=overview) [![Status](https://github.com/unistack-org/micro/workflows/build/badge.svg?branch=master)](https://github.com/unistack-org/micro/actions?query=workflow%3Abuild+branch%3Amaster+event%3Apush) [![Lint](https://goreportcard.com/badge/github.com/unistack-org/micro)](https://goreportcard.com/report/github.com/unistack-org/micro) [![Slack](https://img.shields.io/static/v1?label=micro&message=slack&color=blueviolet)](https://unistack-org.slack.com/messages/default) # Micro [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Doc](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/go.unistack.org/micro/v4?tab=overview) [![Status](https://github.com/unistack-org/micro/workflows/build/badge.svg?branch=master)](https://github.com/unistack-org/micro/actions?query=workflow%3Abuild+branch%3Amaster+event%3Apush) [![Lint](https://goreportcard.com/badge/go.unistack.org/micro/v4)](https://goreportcard.com/report/go.unistack.org/micro/v4) [![Coverage](https://codecov.io/gh/unistack-org/micro/branch/v4/graph/badge.svg?token=OZPO2LP7VS)](https://codecov.io/gh/unistack-org/micro)
Micro is a standard library for microservices. Micro is a standard library for microservices.

15
SECURITY.md Normal file
View File

@ -0,0 +1,15 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 3.7.x | :white_check_mark: |
| < 3.7.0 | :x: |
## Reporting a Vulnerability
If you find any issue, please create github issue in this repo

View File

@ -1,192 +0,0 @@
package api
import (
"errors"
"regexp"
"strings"
"github.com/unistack-org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/server"
)
type Api interface {
// Initialise options
Init(...Option) error
// Get the options
Options() Options
// Register a http handler
Register(*Endpoint) error
// Register a route
Deregister(*Endpoint) error
// Implementation of api
String() string
}
type Options struct{}
type Option func(*Options) error
// Endpoint is a mapping between an RPC method and HTTP endpoint
type Endpoint struct {
// RPC Method e.g. Greeter.Hello
Name string
// Description e.g what's this endpoint for
Description string
// API Handler e.g rpc, proxy
Handler string
// HTTP Host e.g example.com
Host []string
// HTTP Methods e.g GET, POST
Method []string
// HTTP Path e.g /greeter. Expect POSIX regex
Path []string
// Body destination
// "*" or "" - top level message value
// "string" - inner message value
Body string
// Stream flag
Stream bool
}
// Service represents an API service
type Service struct {
// Name of service
Name string
// The endpoint for this service
Endpoint *Endpoint
// Versions of this service
Services []*registry.Service
}
func strip(s string) string {
return strings.TrimSpace(s)
}
func slice(s string) []string {
var sl []string
for _, p := range strings.Split(s, ",") {
if str := strip(p); len(str) > 0 {
sl = append(sl, strip(p))
}
}
return sl
}
// Encode encodes an endpoint to endpoint metadata
func Encode(e *Endpoint) map[string]string {
if e == nil {
return nil
}
// endpoint map
ep := make(map[string]string)
// set vals only if they exist
set := func(k, v string) {
if len(v) == 0 {
return
}
ep[k] = v
}
set("endpoint", e.Name)
set("description", e.Description)
set("handler", e.Handler)
set("method", strings.Join(e.Method, ","))
set("path", strings.Join(e.Path, ","))
set("host", strings.Join(e.Host, ","))
return ep
}
// Decode decodes endpoint metadata into an endpoint
func Decode(e metadata.Metadata) *Endpoint {
if e == nil {
return nil
}
ep := &Endpoint{}
ep.Name, _ = e.Get("endpoint")
ep.Description, _ = e.Get("description")
epmethod, _ := e.Get("method")
ep.Method = []string{epmethod}
eppath, _ := e.Get("path")
ep.Path = []string{eppath}
ephost, _ := e.Get("host")
ep.Host = []string{ephost}
ep.Handler, _ = e.Get("handler")
return ep
}
// Validate validates an endpoint to guarantee it won't blow up when being served
func Validate(e *Endpoint) error {
if e == nil {
return errors.New("endpoint is nil")
}
if len(e.Name) == 0 {
return errors.New("name required")
}
for _, p := range e.Path {
ps := p[0]
pe := p[len(p)-1]
if ps == '^' && pe == '$' {
_, err := regexp.CompilePOSIX(p)
if err != nil {
return err
}
} else if ps == '^' && pe != '$' {
return errors.New("invalid path")
} else if ps != '^' && pe == '$' {
return errors.New("invalid path")
}
}
if len(e.Handler) == 0 {
return errors.New("invalid handler")
}
return nil
}
/*
Design ideas
// Gateway is an api gateway interface
type Gateway interface {
// Register a http handler
Handle(pattern string, http.Handler)
// Register a route
RegisterRoute(r Route)
// Init initialises the command line.
// It also parses further options.
Init(...Option) error
// Run the gateway
Run() error
}
// NewGateway returns a new api gateway
func NewGateway() Gateway {
return newGateway()
}
*/
// WithEndpoint returns a server.HandlerOption with endpoint metadata set
//
// Usage:
//
// proto.RegisterHandler(service.Server(), new(Handler), api.WithEndpoint(
// &api.Endpoint{
// Name: "Greeter.Hello",
// Path: []string{"/greeter"},
// },
// ))
func WithEndpoint(e *Endpoint) server.HandlerOption {
return server.EndpointMetadata(e.Name, Encode(e))
}

View File

@ -1,152 +0,0 @@
package api
import (
"strings"
"testing"
)
func TestEncoding(t *testing.T) {
testData := []*Endpoint{
nil,
{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"/test"},
},
}
compare := func(expect, got []string) bool {
// no data to compare, return true
if len(expect) == 0 && len(got) == 0 {
return true
}
// no data expected but got some return false
if len(expect) == 0 && len(got) > 0 {
return false
}
// compare expected with what we got
for _, e := range expect {
var seen bool
for _, g := range got {
if e == g {
seen = true
break
}
}
if !seen {
return false
}
}
// we're done, return true
return true
}
for _, d := range testData {
// encode
e := Encode(d)
// decode
de := Decode(e)
// nil endpoint returns nil
if d == nil {
if e != nil {
t.Fatalf("expected nil got %v", e)
}
if de != nil {
t.Fatalf("expected nil got %v", de)
}
continue
}
// check encoded map
name := e["endpoint"]
desc := e["description"]
method := strings.Split(e["method"], ",")
path := strings.Split(e["path"], ",")
host := strings.Split(e["host"], ",")
handler := e["handler"]
if name != d.Name {
t.Fatalf("expected %v got %v", d.Name, name)
}
if desc != d.Description {
t.Fatalf("expected %v got %v", d.Description, desc)
}
if handler != d.Handler {
t.Fatalf("expected %v got %v", d.Handler, handler)
}
if ok := compare(d.Method, method); !ok {
t.Fatalf("expected %v got %v", d.Method, method)
}
if ok := compare(d.Path, path); !ok {
t.Fatalf("expected %v got %v", d.Path, path)
}
if ok := compare(d.Host, host); !ok {
t.Fatalf("expected %v got %v", d.Host, host)
}
if de.Name != d.Name {
t.Fatalf("expected %v got %v", d.Name, de.Name)
}
if de.Description != d.Description {
t.Fatalf("expected %v got %v", d.Description, de.Description)
}
if de.Handler != d.Handler {
t.Fatalf("expected %v got %v", d.Handler, de.Handler)
}
if ok := compare(d.Method, de.Method); !ok {
t.Fatalf("expected %v got %v", d.Method, de.Method)
}
if ok := compare(d.Path, de.Path); !ok {
t.Fatalf("expected %v got %v", d.Path, de.Path)
}
if ok := compare(d.Host, de.Host); !ok {
t.Fatalf("expected %v got %v", d.Host, de.Host)
}
}
}
func TestValidate(t *testing.T) {
epPcre := &Endpoint{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"^/test/?$"},
}
if err := Validate(epPcre); err != nil {
t.Fatal(err)
}
epGpath := &Endpoint{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"/test/{id}"},
}
if err := Validate(epGpath); err != nil {
t.Fatal(err)
}
epPcreInvalid := &Endpoint{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"/test/?$"},
}
if err := Validate(epPcreInvalid); err == nil {
t.Fatalf("invalid pcre %v", epPcreInvalid.Path[0])
}
}

View File

@ -1,121 +0,0 @@
// Package api provides an http-rpc handler which provides the entire http request over rpc
package api
import (
"net/http"
goapi "github.com/unistack-org/micro/v3/api"
"github.com/unistack-org/micro/v3/api/handler"
api "github.com/unistack-org/micro/v3/api/proto"
"github.com/unistack-org/micro/v3/client"
"github.com/unistack-org/micro/v3/errors"
"github.com/unistack-org/micro/v3/util/ctx"
"github.com/unistack-org/micro/v3/util/router"
)
type apiHandler struct {
opts handler.Options
s *goapi.Service
}
const (
Handler = "api"
)
// API handler is the default handler which takes api.Request and returns api.Response
func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
bsize := handler.DefaultMaxRecvSize
if a.opts.MaxRecvSize > 0 {
bsize = a.opts.MaxRecvSize
}
r.Body = http.MaxBytesReader(w, r.Body, bsize)
request, err := requestToProto(r)
if err != nil {
er := errors.InternalServerError("go.micro.api", err.Error())
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(500)
w.Write([]byte(er.Error()))
return
}
var service *goapi.Service
if a.s != nil {
// we were given the service
service = a.s
} else if a.opts.Router != nil {
// try get service from router
s, err := a.opts.Router.Route(r)
if err != nil {
er := errors.InternalServerError("go.micro.api", err.Error())
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(500)
w.Write([]byte(er.Error()))
return
}
service = s
} else {
// we have no way of routing the request
er := errors.InternalServerError("go.micro.api", "no route found")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(500)
w.Write([]byte(er.Error()))
return
}
// create request and response
c := a.opts.Client
req := c.NewRequest(service.Name, service.Endpoint.Name, request)
rsp := &api.Response{}
// create the context from headers
cx := ctx.FromRequest(r)
if err := c.Call(cx, req, rsp, client.WithRouter(router.New(service.Services))); err != nil {
w.Header().Set("Content-Type", "application/json")
ce := errors.Parse(err.Error())
switch ce.Code {
case 0:
w.WriteHeader(500)
default:
w.WriteHeader(int(ce.Code))
}
w.Write([]byte(ce.Error()))
return
} else if rsp.StatusCode == 0 {
rsp.StatusCode = http.StatusOK
}
for _, header := range rsp.GetHeader() {
for _, val := range header.Values {
w.Header().Add(header.Key, val)
}
}
if len(w.Header().Get("Content-Type")) == 0 {
w.Header().Set("Content-Type", "application/json")
}
w.WriteHeader(int(rsp.StatusCode))
w.Write([]byte(rsp.Body))
}
func (a *apiHandler) String() string {
return "api"
}
func NewHandler(opts ...handler.Option) handler.Handler {
options := handler.NewOptions(opts...)
return &apiHandler{
opts: options,
}
}
func WithService(s *goapi.Service, opts ...handler.Option) handler.Handler {
options := handler.NewOptions(opts...)
return &apiHandler{
opts: options,
s: s,
}
}

View File

@ -1,109 +0,0 @@
package api
import (
"fmt"
"mime"
"net"
"net/http"
"strings"
"github.com/oxtoacart/bpool"
api "github.com/unistack-org/micro/v3/api/proto"
)
var (
// need to calculate later to specify useful defaults
bufferPool = bpool.NewSizedBufferPool(1024, 8)
)
func requestToProto(r *http.Request) (*api.Request, error) {
if err := r.ParseForm(); err != nil {
return nil, fmt.Errorf("Error parsing form: %v", err)
}
req := &api.Request{
Path: r.URL.Path,
Method: r.Method,
Header: make(map[string]*api.Pair),
Get: make(map[string]*api.Pair),
Post: make(map[string]*api.Pair),
Url: r.URL.String(),
}
ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
ct = "text/plain; charset=UTF-8" //default CT is text/plain
r.Header.Set("Content-Type", ct)
}
//set the body:
if r.Body != nil {
switch ct {
case "application/x-www-form-urlencoded":
// expect form vals in Post data
default:
buf := bufferPool.Get()
defer bufferPool.Put(buf)
if _, err = buf.ReadFrom(r.Body); err != nil {
return nil, err
}
req.Body = buf.String()
}
}
// Set X-Forwarded-For if it does not exist
if ip, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
if prior, ok := r.Header["X-Forwarded-For"]; ok {
ip = strings.Join(prior, ", ") + ", " + ip
}
// Set the header
req.Header["X-Forwarded-For"] = &api.Pair{
Key: "X-Forwarded-For",
Values: []string{ip},
}
}
// Host is stripped from net/http Headers so let's add it
req.Header["Host"] = &api.Pair{
Key: "Host",
Values: []string{r.Host},
}
// Get data
for key, vals := range r.URL.Query() {
header, ok := req.Get[key]
if !ok {
header = &api.Pair{
Key: key,
}
req.Get[key] = header
}
header.Values = vals
}
// Post data
for key, vals := range r.PostForm {
header, ok := req.Post[key]
if !ok {
header = &api.Pair{
Key: key,
}
req.Post[key] = header
}
header.Values = vals
}
for key, vals := range r.Header {
header, ok := req.Header[key]
if !ok {
header = &api.Pair{
Key: key,
}
req.Header[key] = header
}
header.Values = vals
}
return req, nil
}

View File

@ -1,46 +0,0 @@
package api
import (
"net/http"
"net/url"
"testing"
)
func TestRequestToProto(t *testing.T) {
testData := []*http.Request{
{
Method: "GET",
Header: http.Header{
"Header": []string{"test"},
},
URL: &url.URL{
Scheme: "http",
Host: "localhost",
Path: "/foo/bar",
RawQuery: "param1=value1",
},
},
}
for _, d := range testData {
p, err := requestToProto(d)
if err != nil {
t.Fatal(err)
}
if p.Path != d.URL.Path {
t.Fatalf("Expected path %s got %s", d.URL.Path, p.Path)
}
if p.Method != d.Method {
t.Fatalf("Expected method %s got %s", d.Method, p.Method)
}
for k, v := range d.Header {
if val, ok := p.Header[k]; !ok {
t.Fatalf("Expected header %s", k)
} else {
if val.Values[0] != v[0] {
t.Fatalf("Expected val %s, got %s", val.Values[0], v[0])
}
}
}
}
}

View File

@ -1,141 +0,0 @@
// Package event provides a handler which publishes an event
package event
import (
"encoding/json"
"fmt"
"net/http"
"path"
"regexp"
"strings"
"time"
"github.com/google/uuid"
"github.com/oxtoacart/bpool"
"github.com/unistack-org/micro/v3/api/handler"
proto "github.com/unistack-org/micro/v3/api/proto"
"github.com/unistack-org/micro/v3/util/ctx"
)
var (
bufferPool = bpool.NewSizedBufferPool(1024, 8)
)
type event struct {
opts handler.Options
}
var (
Handler = "event"
versionRe = regexp.MustCompilePOSIX("^v[0-9]+$")
)
func eventName(parts []string) string {
return strings.Join(parts, ".")
}
func evRoute(ns, p string) (string, string) {
p = path.Clean(p)
p = strings.TrimPrefix(p, "/")
if len(p) == 0 {
return ns, "event"
}
parts := strings.Split(p, "/")
// no path
if len(parts) == 0 {
// topic: namespace
// action: event
return strings.Trim(ns, "."), "event"
}
// Treat /v[0-9]+ as versioning
// /v1/foo/bar => topic: v1.foo action: bar
if len(parts) >= 2 && versionRe.Match([]byte(parts[0])) {
topic := ns + "." + strings.Join(parts[:2], ".")
action := eventName(parts[1:])
return topic, action
}
// /foo => topic: ns.foo action: foo
// /foo/bar => topic: ns.foo action: bar
topic := ns + "." + strings.Join(parts[:1], ".")
action := eventName(parts[1:])
return topic, action
}
func (e *event) ServeHTTP(w http.ResponseWriter, r *http.Request) {
bsize := handler.DefaultMaxRecvSize
if e.opts.MaxRecvSize > 0 {
bsize = e.opts.MaxRecvSize
}
r.Body = http.MaxBytesReader(w, r.Body, bsize)
// request to topic:event
// create event
// publish to topic
topic, action := evRoute(e.opts.Namespace, r.URL.Path)
// create event
ev := &proto.Event{
Name: action,
// TODO: dedupe event
Id: fmt.Sprintf("%s-%s-%s", topic, action, uuid.New().String()),
Header: make(map[string]*proto.Pair),
Timestamp: time.Now().Unix(),
}
// set headers
for key, vals := range r.Header {
header, ok := ev.Header[key]
if !ok {
header = &proto.Pair{
Key: key,
}
ev.Header[key] = header
}
header.Values = vals
}
// set body
if r.Method == "GET" {
bytes, _ := json.Marshal(r.URL.Query())
ev.Data = string(bytes)
} else {
// Read body
buf := bufferPool.Get()
defer bufferPool.Put(buf)
if _, err := buf.ReadFrom(r.Body); err != nil {
http.Error(w, err.Error(), 500)
return
}
ev.Data = buf.String()
}
// get client
c := e.opts.Client
// create publication
p := c.NewMessage(topic, ev)
// publish event
if err := c.Publish(ctx.FromRequest(r), p); err != nil {
http.Error(w, err.Error(), 500)
return
}
}
func (e *event) String() string {
return "event"
}
func NewHandler(opts ...handler.Option) handler.Handler {
return &event{
opts: handler.NewOptions(opts...),
}
}

View File

@ -1,14 +0,0 @@
// Package handler provides http handlers
package handler
import (
"net/http"
)
// Handler represents a HTTP handler that manages a request
type Handler interface {
// standard http handler
http.Handler
// name of handler
String() string
}

View File

@ -1,105 +0,0 @@
// Package http is a http reverse proxy handler
package http
import (
"errors"
"fmt"
"math/rand"
"net/http"
"net/http/httputil"
"net/url"
"github.com/unistack-org/micro/v3/api"
"github.com/unistack-org/micro/v3/api/handler"
"github.com/unistack-org/micro/v3/registry"
)
const (
Handler = "http"
)
type httpHandler struct {
options handler.Options
// set with different initializer
s *api.Service
}
func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
service, err := h.getService(r)
if err != nil {
w.WriteHeader(500)
return
}
if len(service) == 0 {
w.WriteHeader(404)
return
}
rp, err := url.Parse(service)
if err != nil {
w.WriteHeader(500)
return
}
httputil.NewSingleHostReverseProxy(rp).ServeHTTP(w, r)
}
// getService returns the service for this request from the selector
func (h *httpHandler) getService(r *http.Request) (string, error) {
var service *api.Service
if h.s != nil {
// we were given the service
service = h.s
} else if h.options.Router != nil {
// try get service from router
s, err := h.options.Router.Route(r)
if err != nil {
return "", err
}
service = s
} else {
// we have no way of routing the request
return "", errors.New("no route found")
}
if len(service.Services) == 0 {
return "", errors.New("no route found")
}
// get the nodes for this service
nodes := make([]*registry.Node, 0, len(service.Services))
for _, srv := range service.Services {
nodes = append(nodes, srv.Nodes...)
}
// select a random node
node := nodes[rand.Int()%len(nodes)]
return fmt.Sprintf("http://%s", node.Address), nil
}
func (h *httpHandler) String() string {
return "http"
}
// NewHandler returns a http proxy handler
func NewHandler(opts ...handler.Option) handler.Handler {
options := handler.NewOptions(opts...)
return &httpHandler{
options: options,
}
}
// WithService creates a handler with a service
func WithService(s *api.Service, opts ...handler.Option) handler.Handler {
options := handler.NewOptions(opts...)
return &httpHandler{
options: options,
s: s,
}
}

View File

@ -1,129 +0,0 @@
// +build ignore
package http
import (
"net"
"net/http"
"net/http/httptest"
"testing"
"github.com/unistack-org/micro/v3/api/handler"
"github.com/unistack-org/micro/v3/api/resolver"
"github.com/unistack-org/micro/v3/api/resolver/vpath"
"github.com/unistack-org/micro/v3/api/router"
regRouter "github.com/unistack-org/micro/v3/api/router/registry"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/registry/memory"
)
func testHttp(t *testing.T, path, service, ns string) {
r := memory.NewRegistry()
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer l.Close()
s := &registry.Service{
Name: service,
Nodes: []*registry.Node{
{
Id: service + "-1",
Address: l.Addr().String(),
},
},
}
r.Register(s)
defer r.Deregister(s)
// setup the test handler
m := http.NewServeMux()
m.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`you got served`))
})
// start http test serve
go http.Serve(l, m)
// create new request and writer
w := httptest.NewRecorder()
req, err := http.NewRequest("POST", path, nil)
if err != nil {
t.Fatal(err)
}
// initialise the handler
rt := regRouter.NewRouter(
router.WithHandler("http"),
router.WithRegistry(r),
router.WithResolver(vpath.NewResolver(
resolver.WithServicePrefix(ns),
)),
)
p := NewHandler(handler.WithRouter(rt))
// execute the handler
p.ServeHTTP(w, req)
if w.Code != 200 {
t.Fatalf("Expected 200 response got %d %s", w.Code, w.Body.String())
}
if w.Body.String() != "you got served" {
t.Fatalf("Expected body: you got served. Got: %s", w.Body.String())
}
}
func TestHttpHandler(t *testing.T) {
testData := []struct {
path string
service string
namespace string
}{
{
"/test/foo",
"go.micro.api.test",
"go.micro.api",
},
{
"/test/foo/baz",
"go.micro.api.test",
"go.micro.api",
},
{
"/v1/foo",
"go.micro.api.v1.foo",
"go.micro.api",
},
{
"/v1/foo/bar",
"go.micro.api.v1.foo",
"go.micro.api",
},
{
"/v2/baz",
"go.micro.api.v2.baz",
"go.micro.api",
},
{
"/v2/baz/bar",
"go.micro.api.v2.baz",
"go.micro.api",
},
{
"/v2/baz/bar",
"v2.baz",
"",
},
}
for _, d := range testData {
t.Run(d.service, func(t *testing.T) {
testHttp(t, d.path, d.service, d.namespace)
})
}
}

View File

@ -1,65 +0,0 @@
package handler
import (
"github.com/unistack-org/micro/v3/api/router"
"github.com/unistack-org/micro/v3/client"
)
var (
DefaultMaxRecvSize int64 = 1024 * 1024 * 100 // 10Mb
)
type Options struct {
MaxRecvSize int64
Namespace string
Router router.Router
Client client.Client
}
type Option func(o *Options)
// NewOptions fills in the blanks
func NewOptions(opts ...Option) Options {
var options Options
for _, o := range opts {
o(&options)
}
// set namespace if blank
if len(options.Namespace) == 0 {
WithNamespace("go.micro.api")(&options)
}
if options.MaxRecvSize == 0 {
options.MaxRecvSize = DefaultMaxRecvSize
}
return options
}
// WithNamespace specifies the namespace for the handler
func WithNamespace(s string) Option {
return func(o *Options) {
o.Namespace = s
}
}
// WithRouter specifies a router to be used by the handler
func WithRouter(r router.Router) Option {
return func(o *Options) {
o.Router = r
}
}
func WithClient(c client.Client) Option {
return func(o *Options) {
o.Client = c
}
}
// WithMaxRecvSize specifies max body size
func WithMaxRecvSize(size int64) Option {
return func(o *Options) {
o.MaxRecvSize = size
}
}

View File

@ -1,182 +0,0 @@
// Package web contains the web handler including websocket support
package web
import (
"errors"
"fmt"
"io"
"math/rand"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"github.com/unistack-org/micro/v3/api"
"github.com/unistack-org/micro/v3/api/handler"
"github.com/unistack-org/micro/v3/registry"
)
const (
Handler = "web"
)
type webHandler struct {
opts handler.Options
s *api.Service
}
func (wh *webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
service, err := wh.getService(r)
if err != nil {
w.WriteHeader(500)
return
}
if len(service) == 0 {
w.WriteHeader(404)
return
}
rp, err := url.Parse(service)
if err != nil {
w.WriteHeader(500)
return
}
if isWebSocket(r) {
wh.serveWebSocket(rp.Host, w, r)
return
}
httputil.NewSingleHostReverseProxy(rp).ServeHTTP(w, r)
}
// getService returns the service for this request from the selector
func (wh *webHandler) getService(r *http.Request) (string, error) {
var service *api.Service
if wh.s != nil {
// we were given the service
service = wh.s
} else if wh.opts.Router != nil {
// try get service from router
s, err := wh.opts.Router.Route(r)
if err != nil {
return "", err
}
service = s
} else {
// we have no way of routing the request
return "", errors.New("no route found")
}
// get the nodes
nodes := make([]*registry.Node, 0, len(service.Services))
for _, srv := range service.Services {
nodes = append(nodes, srv.Nodes...)
}
if len(nodes) == 0 {
return "", errors.New("no route found")
}
// select a random node
node := nodes[rand.Int()%len(nodes)]
return fmt.Sprintf("http://%s", node.Address), nil
}
// serveWebSocket used to serve a web socket proxied connection
func (wh *webHandler) serveWebSocket(host string, w http.ResponseWriter, r *http.Request) {
req := new(http.Request)
*req = *r
if len(host) == 0 {
http.Error(w, "invalid host", 500)
return
}
// set x-forward-for
if clientIP, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
if ips, ok := req.Header["X-Forwarded-For"]; ok {
clientIP = strings.Join(ips, ", ") + ", " + clientIP
}
req.Header.Set("X-Forwarded-For", clientIP)
}
// connect to the backend host
conn, err := net.Dial("tcp", host)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// hijack the connection
hj, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "failed to connect", 500)
return
}
nc, _, err := hj.Hijack()
if err != nil {
return
}
defer nc.Close()
defer conn.Close()
if err = req.Write(conn); err != nil {
return
}
errCh := make(chan error, 2)
cp := func(dst io.Writer, src io.Reader) {
_, err := io.Copy(dst, src)
errCh <- err
}
go cp(conn, nc)
go cp(nc, conn)
<-errCh
}
func isWebSocket(r *http.Request) bool {
contains := func(key, val string) bool {
vv := strings.Split(r.Header.Get(key), ",")
for _, v := range vv {
if val == strings.ToLower(strings.TrimSpace(v)) {
return true
}
}
return false
}
if contains("Connection", "upgrade") && contains("Upgrade", "websocket") {
return true
}
return false
}
func (wh *webHandler) String() string {
return "web"
}
func NewHandler(opts ...handler.Option) handler.Handler {
return &webHandler{
opts: handler.NewOptions(opts...),
}
}
func WithService(s *api.Service, opts ...handler.Option) handler.Handler {
options := handler.NewOptions(opts...)
return &webHandler{
opts: options,
s: s,
}
}

View File

@ -1,28 +0,0 @@
package proto
type Message struct {
data []byte
}
func (m *Message) ProtoMessage() {}
func (m *Message) Reset() {
*m = Message{}
}
func (m *Message) String() string {
return string(m.data)
}
func (m *Message) Marshal() ([]byte, error) {
return m.data, nil
}
func (m *Message) Unmarshal(data []byte) error {
m.data = data
return nil
}
func NewMessage(data []byte) *Message {
return &Message{data}
}

View File

@ -1,511 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.6.1
// source: api/proto/api.proto
package go_api
import (
proto "github.com/golang/protobuf/proto"
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 Pair struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Values []string `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"`
}
func (x *Pair) Reset() {
*x = Pair{}
if protoimpl.UnsafeEnabled {
mi := &file_api_proto_api_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Pair) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Pair) ProtoMessage() {}
func (x *Pair) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_api_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 Pair.ProtoReflect.Descriptor instead.
func (*Pair) Descriptor() ([]byte, []int) {
return file_api_proto_api_proto_rawDescGZIP(), []int{0}
}
func (x *Pair) GetKey() string {
if x != nil {
return x.Key
}
return ""
}
func (x *Pair) GetValues() []string {
if x != nil {
return x.Values
}
return nil
}
// A HTTP request as RPC
// Forward by the api handler
type Request struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"`
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
Header map[string]*Pair `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Get map[string]*Pair `protobuf:"bytes,4,rep,name=get,proto3" json:"get,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Post map[string]*Pair `protobuf:"bytes,5,rep,name=post,proto3" json:"post,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Body string `protobuf:"bytes,6,opt,name=body,proto3" json:"body,omitempty"` // raw request body; if not application/x-www-form-urlencoded
Url string `protobuf:"bytes,7,opt,name=url,proto3" json:"url,omitempty"`
}
func (x *Request) Reset() {
*x = Request{}
if protoimpl.UnsafeEnabled {
mi := &file_api_proto_api_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Request) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Request) ProtoMessage() {}
func (x *Request) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_api_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 Request.ProtoReflect.Descriptor instead.
func (*Request) Descriptor() ([]byte, []int) {
return file_api_proto_api_proto_rawDescGZIP(), []int{1}
}
func (x *Request) GetMethod() string {
if x != nil {
return x.Method
}
return ""
}
func (x *Request) GetPath() string {
if x != nil {
return x.Path
}
return ""
}
func (x *Request) GetHeader() map[string]*Pair {
if x != nil {
return x.Header
}
return nil
}
func (x *Request) GetGet() map[string]*Pair {
if x != nil {
return x.Get
}
return nil
}
func (x *Request) GetPost() map[string]*Pair {
if x != nil {
return x.Post
}
return nil
}
func (x *Request) GetBody() string {
if x != nil {
return x.Body
}
return ""
}
func (x *Request) GetUrl() string {
if x != nil {
return x.Url
}
return ""
}
// A HTTP response as RPC
// Expected response for the api handler
type Response struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
StatusCode int32 `protobuf:"varint,1,opt,name=statusCode,proto3" json:"statusCode,omitempty"`
Header map[string]*Pair `protobuf:"bytes,2,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Body string `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"`
}
func (x *Response) Reset() {
*x = Response{}
if protoimpl.UnsafeEnabled {
mi := &file_api_proto_api_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Response) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Response) ProtoMessage() {}
func (x *Response) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_api_proto_msgTypes[2]
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 Response.ProtoReflect.Descriptor instead.
func (*Response) Descriptor() ([]byte, []int) {
return file_api_proto_api_proto_rawDescGZIP(), []int{2}
}
func (x *Response) GetStatusCode() int32 {
if x != nil {
return x.StatusCode
}
return 0
}
func (x *Response) GetHeader() map[string]*Pair {
if x != nil {
return x.Header
}
return nil
}
func (x *Response) GetBody() string {
if x != nil {
return x.Body
}
return ""
}
// A HTTP event as RPC
// Forwarded by the event handler
type Event struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// e.g login
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// uuid
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
// unix timestamp of event
Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
// event headers
Header map[string]*Pair `protobuf:"bytes,4,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
// the event data
Data string `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"`
}
func (x *Event) Reset() {
*x = Event{}
if protoimpl.UnsafeEnabled {
mi := &file_api_proto_api_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Event) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Event) ProtoMessage() {}
func (x *Event) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_api_proto_msgTypes[3]
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 Event.ProtoReflect.Descriptor instead.
func (*Event) Descriptor() ([]byte, []int) {
return file_api_proto_api_proto_rawDescGZIP(), []int{3}
}
func (x *Event) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Event) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *Event) GetTimestamp() int64 {
if x != nil {
return x.Timestamp
}
return 0
}
func (x *Event) GetHeader() map[string]*Pair {
if x != nil {
return x.Header
}
return nil
}
func (x *Event) GetData() string {
if x != nil {
return x.Data
}
return ""
}
var File_api_proto_api_proto protoreflect.FileDescriptor
var file_api_proto_api_proto_rawDesc = []byte{
0x0a, 0x13, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x22, 0x30, 0x0a,
0x04, 0x50, 0x61, 0x69, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22,
0xc1, 0x03, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d,
0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74,
0x68, 0x6f, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x33, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65,
0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45,
0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x03,
0x67, 0x65, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e,
0x74, 0x72, 0x79, 0x52, 0x03, 0x67, 0x65, 0x74, 0x12, 0x2d, 0x0a, 0x04, 0x70, 0x6f, 0x73, 0x74,
0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72,
0x79, 0x52, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18,
0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75,
0x72, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x1a, 0x47, 0x0a,
0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22,
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e,
0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x44, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74,
0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69,
0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x45, 0x0a, 0x09,
0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x6f, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
0x02, 0x38, 0x01, 0x22, 0xbd, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65,
0x12, 0x34, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06,
0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x1a, 0x47, 0x0a, 0x0b, 0x48, 0x65,
0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x6f, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
0x02, 0x38, 0x01, 0x22, 0xd9, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69,
0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03,
0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12,
0x31, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x19, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x48,
0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64,
0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x47, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_api_proto_api_proto_rawDescOnce sync.Once
file_api_proto_api_proto_rawDescData = file_api_proto_api_proto_rawDesc
)
func file_api_proto_api_proto_rawDescGZIP() []byte {
file_api_proto_api_proto_rawDescOnce.Do(func() {
file_api_proto_api_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_proto_api_proto_rawDescData)
})
return file_api_proto_api_proto_rawDescData
}
var file_api_proto_api_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
var file_api_proto_api_proto_goTypes = []interface{}{
(*Pair)(nil), // 0: go.api.Pair
(*Request)(nil), // 1: go.api.Request
(*Response)(nil), // 2: go.api.Response
(*Event)(nil), // 3: go.api.Event
nil, // 4: go.api.Request.HeaderEntry
nil, // 5: go.api.Request.GetEntry
nil, // 6: go.api.Request.PostEntry
nil, // 7: go.api.Response.HeaderEntry
nil, // 8: go.api.Event.HeaderEntry
}
var file_api_proto_api_proto_depIdxs = []int32{
4, // 0: go.api.Request.header:type_name -> go.api.Request.HeaderEntry
5, // 1: go.api.Request.get:type_name -> go.api.Request.GetEntry
6, // 2: go.api.Request.post:type_name -> go.api.Request.PostEntry
7, // 3: go.api.Response.header:type_name -> go.api.Response.HeaderEntry
8, // 4: go.api.Event.header:type_name -> go.api.Event.HeaderEntry
0, // 5: go.api.Request.HeaderEntry.value:type_name -> go.api.Pair
0, // 6: go.api.Request.GetEntry.value:type_name -> go.api.Pair
0, // 7: go.api.Request.PostEntry.value:type_name -> go.api.Pair
0, // 8: go.api.Response.HeaderEntry.value:type_name -> go.api.Pair
0, // 9: go.api.Event.HeaderEntry.value:type_name -> go.api.Pair
10, // [10:10] is the sub-list for method output_type
10, // [10:10] is the sub-list for method input_type
10, // [10:10] is the sub-list for extension type_name
10, // [10:10] is the sub-list for extension extendee
0, // [0:10] is the sub-list for field type_name
}
func init() { file_api_proto_api_proto_init() }
func file_api_proto_api_proto_init() {
if File_api_proto_api_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_api_proto_api_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Pair); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_proto_api_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Request); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_proto_api_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Response); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_proto_api_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Event); 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_api_proto_api_proto_rawDesc,
NumEnums: 0,
NumMessages: 9,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_api_proto_api_proto_goTypes,
DependencyIndexes: file_api_proto_api_proto_depIdxs,
MessageInfos: file_api_proto_api_proto_msgTypes,
}.Build()
File_api_proto_api_proto = out.File
file_api_proto_api_proto_rawDesc = nil
file_api_proto_api_proto_goTypes = nil
file_api_proto_api_proto_depIdxs = nil
}

View File

@ -1,21 +0,0 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: api/proto/api.proto
package go_api
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// 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.ProtoPackageIsVersion3 // please upgrade the proto package

View File

@ -1,43 +0,0 @@
syntax = "proto3";
package go.api;
message Pair {
string key = 1;
repeated string values = 2;
}
// A HTTP request as RPC
// Forward by the api handler
message Request {
string method = 1;
string path = 2;
map<string, Pair> header = 3;
map<string, Pair> get = 4;
map<string, Pair> post = 5;
string body = 6; // raw request body; if not application/x-www-form-urlencoded
string url = 7;
}
// A HTTP response as RPC
// Expected response for the api handler
message Response {
int32 statusCode = 1;
map<string, Pair> header = 2;
string body = 3;
}
// A HTTP event as RPC
// Forwarded by the event handler
message Event {
// e.g login
string name = 1;
// uuid
string id = 2;
// unix timestamp of event
int64 timestamp = 3;
// event headers
map<string, Pair> header = 4;
// the event data
string data = 5;
}

View File

@ -1,47 +0,0 @@
// Package grpc resolves a grpc service like /greeter.Say/Hello to greeter service
package grpc
import (
"errors"
"net/http"
"strings"
"github.com/unistack-org/micro/v3/api/resolver"
)
// Resolver struct
type Resolver struct {
opts resolver.Options
}
// Resolve func to resolve enndpoint
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
// parse options
options := resolver.NewResolveOptions(opts...)
// /foo.Bar/Service
if req.URL.Path == "/" {
return nil, errors.New("unknown name")
}
// [foo.Bar, Service]
parts := strings.Split(req.URL.Path[1:], "/")
// [foo, Bar]
name := strings.Split(parts[0], ".")
// foo
return &resolver.Endpoint{
Name: strings.Join(name[:len(name)-1], "."),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}
func (r *Resolver) String() string {
return "grpc"
}
// NewResolver is used to create new Resolver
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{opts: resolver.NewOptions(opts...)}
}

View File

@ -1,33 +0,0 @@
// Package host resolves using http host
package host
import (
"net/http"
"github.com/unistack-org/micro/v3/api/resolver"
)
type Resolver struct {
opts resolver.Options
}
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
// parse options
options := resolver.NewResolveOptions(opts...)
return &resolver.Endpoint{
Name: req.Host,
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}
func (r *Resolver) String() string {
return "host"
}
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{opts: resolver.NewOptions(opts...)}
}

View File

@ -1,67 +0,0 @@
package resolver
import (
"context"
"github.com/unistack-org/micro/v3/registry"
)
// Options struct
type Options struct {
Handler string
ServicePrefix string
Context context.Context
}
// Option func
type Option func(o *Options)
// WithHandler sets the handler being used
func WithHandler(h string) Option {
return func(o *Options) {
o.Handler = h
}
}
// WithServicePrefix sets the ServicePrefix option
func WithServicePrefix(p string) Option {
return func(o *Options) {
o.ServicePrefix = p
}
}
// NewOptions returns new initialised options
func NewOptions(opts ...Option) Options {
options := Options{
Context: context.Background(),
}
for _, o := range opts {
o(&options)
}
return options
}
// ResolveOptions are used when resolving a request
type ResolveOptions struct {
Domain string
}
// ResolveOption sets an option
type ResolveOption func(*ResolveOptions)
// Domain sets the resolve Domain option
func Domain(n string) ResolveOption {
return func(o *ResolveOptions) {
o.Domain = n
}
}
// NewResolveOptions returns new initialised resolve options
func NewResolveOptions(opts ...ResolveOption) ResolveOptions {
options := ResolveOptions{Domain: registry.DefaultDomain}
for _, o := range opts {
o(&options)
}
return options
}

View File

@ -1,40 +0,0 @@
// Package path resolves using http path
package path
import (
"net/http"
"strings"
"github.com/unistack-org/micro/v3/api/resolver"
)
type Resolver struct {
opts resolver.Options
}
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
// parse options
options := resolver.NewResolveOptions(opts...)
if req.URL.Path == "/" {
return nil, resolver.ErrNotFound
}
parts := strings.Split(req.URL.Path[1:], "/")
return &resolver.Endpoint{
Name: r.opts.ServicePrefix + "." + parts[0],
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}
func (r *Resolver) String() string {
return "path"
}
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{opts: resolver.NewOptions(opts...)}
}

View File

@ -1,34 +0,0 @@
// Package resolver resolves a http request to an endpoint
package resolver
import (
"errors"
"net/http"
)
var (
// ErrNotFound returned when endpoint is not found
ErrNotFound = errors.New("not found")
// ErrInvalidPath returned on invalid path
ErrInvalidPath = errors.New("invalid path")
)
// Resolver resolves requests to endpoints
type Resolver interface {
Resolve(r *http.Request, opts ...ResolveOption) (*Endpoint, error)
String() string
}
// Endpoint is the endpoint for a http request
type Endpoint struct {
// Endpoint name e.g greeter
Name string
// HTTP Host e.g example.com
Host string
// HTTP Methods e.g GET, POST
Method string
// HTTP Path e.g /greeter.
Path string
// Domain endpoint exists within
Domain string
}

View File

@ -1,87 +0,0 @@
// Package subdomain is a resolver which uses the subdomain to determine the domain to route to. It
// offloads the endpoint resolution to a child resolver which is provided in New.
package subdomain
import (
"net"
"net/http"
"strings"
"github.com/unistack-org/micro/v3/api/resolver"
"github.com/unistack-org/micro/v3/logger"
"golang.org/x/net/publicsuffix"
)
func NewResolver(parent resolver.Resolver, opts ...resolver.Option) resolver.Resolver {
options := resolver.NewOptions(opts...)
return &Resolver{options, parent}
}
type Resolver struct {
opts resolver.Options
resolver.Resolver
}
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
if dom := r.Domain(req); len(dom) > 0 {
opts = append(opts, resolver.Domain(dom))
}
return r.Resolver.Resolve(req, opts...)
}
func (r *Resolver) Domain(req *http.Request) string {
// determine the host, e.g. foobar.m3o.app
host := req.URL.Hostname()
if len(host) == 0 {
if h, _, err := net.SplitHostPort(req.Host); err == nil {
host = h // host does contain a port
} else if strings.Contains(err.Error(), "missing port in address") {
host = req.Host // host does not contain a port
}
}
// check for an ip address
if net.ParseIP(host) != nil {
return ""
}
// check for dev environment
if host == "localhost" || host == "127.0.0.1" {
return ""
}
// extract the top level domain plus one (e.g. 'myapp.com')
domain, err := publicsuffix.EffectiveTLDPlusOne(host)
if err != nil {
if logger.V(logger.DebugLevel) {
logger.Debug(r.opts.Context, "Unable to extract domain from %v", host)
}
return ""
}
// there was no subdomain
if host == domain {
return ""
}
// remove the domain from the host, leaving the subdomain, e.g. "staging.foo.myapp.com" => "staging.foo"
subdomain := strings.TrimSuffix(host, "."+domain)
// ignore the API subdomain
if subdomain == "api" {
return ""
}
// return the reversed subdomain as the namespace, e.g. "staging.foo" => "foo-staging"
comps := strings.Split(subdomain, ".")
for i := len(comps)/2 - 1; i >= 0; i-- {
opp := len(comps) - 1 - i
comps[i], comps[opp] = comps[opp], comps[i]
}
return strings.Join(comps, "-")
}
func (r *Resolver) String() string {
return "subdomain"
}

View File

@ -1,71 +0,0 @@
package subdomain
import (
"net/http"
"net/url"
"testing"
"github.com/unistack-org/micro/v3/api/resolver/vpath"
"github.com/stretchr/testify/assert"
)
func TestResolve(t *testing.T) {
tt := []struct {
Name string
Host string
Result string
}{
{
Name: "Top level domain",
Host: "micro.mu",
Result: "micro",
},
{
Name: "Effective top level domain",
Host: "micro.com.au",
Result: "micro",
},
{
Name: "Subdomain dev",
Host: "dev.micro.mu",
Result: "dev",
},
{
Name: "Subdomain foo",
Host: "foo.micro.mu",
Result: "foo",
},
{
Name: "Multi-level subdomain",
Host: "staging.myapp.m3o.app",
Result: "myapp-staging",
},
{
Name: "Dev host",
Host: "127.0.0.1",
Result: "micro",
},
{
Name: "Localhost",
Host: "localhost",
Result: "micro",
},
{
Name: "IP host",
Host: "81.151.101.146",
Result: "micro",
},
}
for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
r := NewResolver(vpath.NewResolver())
result, err := r.Resolve(&http.Request{URL: &url.URL{Host: tc.Host, Path: "foo/bar"}})
assert.Nil(t, err, "Expecter err to be nil")
if result != nil {
assert.Equal(t, tc.Result, result.Domain, "Expected %v but got %v", tc.Result, result.Domain)
}
})
}
}

View File

@ -1,75 +0,0 @@
// Package vpath resolves using http path and recognised versioned urls
package vpath
import (
"errors"
"net/http"
"regexp"
"strings"
"github.com/unistack-org/micro/v3/api/resolver"
)
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{opts: resolver.NewOptions(opts...)}
}
type Resolver struct {
opts resolver.Options
}
var (
re = regexp.MustCompile("^v[0-9]+$")
)
func (r *Resolver) Resolve(req *http.Request, opts ...resolver.ResolveOption) (*resolver.Endpoint, error) {
if req.URL.Path == "/" {
return nil, errors.New("unknown name")
}
options := resolver.NewResolveOptions(opts...)
parts := strings.Split(req.URL.Path[1:], "/")
if len(parts) == 1 {
return &resolver.Endpoint{
Name: r.withPrefix(parts...),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}
// /v1/foo
if re.MatchString(parts[0]) {
return &resolver.Endpoint{
Name: r.withPrefix(parts[0:2]...),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}
return &resolver.Endpoint{
Name: r.withPrefix(parts[0]),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
Domain: options.Domain,
}, nil
}
func (r *Resolver) String() string {
return "path"
}
// withPrefix transforms "foo" into "go.micro.api.foo"
func (r *Resolver) withPrefix(parts ...string) string {
p := r.opts.ServicePrefix
if len(p) > 0 {
parts = append([]string{p}, parts...)
}
return strings.Join(parts, ".")
}

View File

@ -1,67 +0,0 @@
package router
import (
"context"
"github.com/unistack-org/micro/v3/api/resolver"
"github.com/unistack-org/micro/v3/api/resolver/vpath"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/registry"
)
type Options struct {
Handler string
Registry registry.Registry
Resolver resolver.Resolver
Logger logger.Logger
Context context.Context
}
type Option func(o *Options)
func NewOptions(opts ...Option) Options {
options := Options{
Context: context.Background(),
Handler: "meta",
}
for _, o := range opts {
o(&options)
}
if options.Resolver == nil {
options.Resolver = vpath.NewResolver(
resolver.WithHandler(options.Handler),
)
}
return options
}
// WithContext sets the context
func WithContext(ctx context.Context) Option {
return func(o *Options) {
o.Context = ctx
}
}
// WithHandler sets the handler
func WithHandler(h string) Option {
return func(o *Options) {
o.Handler = h
}
}
// WithRegistry sets the registry
func WithRegistry(r registry.Registry) Option {
return func(o *Options) {
o.Registry = r
}
}
// WithResolver sets the resolver
func WithResolver(r resolver.Resolver) Option {
return func(o *Options) {
o.Resolver = r
}
}

View File

@ -1,28 +0,0 @@
// Package router provides api service routing
package router
import (
"net/http"
"github.com/unistack-org/micro/v3/api"
)
// Router is used to determine an endpoint for a request
type Router interface {
// Returns options
Options() Options
// Init initialize router
Init(...Option) error
// Stop the router
Close() error
// Endpoint returns an api.Service endpoint or an error if it does not exist
Endpoint(r *http.Request) (*api.Service, error)
// Register endpoint in router
Register(ep *api.Endpoint) error
// Deregister endpoint from router
Deregister(ep *api.Endpoint) error
// Route returns an api.Service route
Route(r *http.Request) (*api.Service, error)
// String represenation of router
String() string
}

View File

@ -1,142 +0,0 @@
// Package auth provides authentication and authorization capability
package auth
import (
"context"
"errors"
"time"
"github.com/unistack-org/micro/v3/metadata"
)
const (
// BearerScheme used for Authorization header
BearerScheme = "Bearer "
// ScopePublic is the scope applied to a rule to allow access to the public
ScopePublic = ""
// ScopeAccount is the scope applied to a rule to limit to users with any valid account
ScopeAccount = "*"
)
var (
DefaultAuth Auth = NewAuth()
// ErrInvalidToken is when the token provided is not valid
ErrInvalidToken = errors.New("invalid token provided")
// ErrForbidden is when a user does not have the necessary scope to access a resource
ErrForbidden = errors.New("resource forbidden")
)
// Auth provides authentication and authorization
type Auth interface {
// Init the auth
Init(opts ...Option)
// Options set for auth
Options() Options
// Generate a new account
Generate(id string, opts ...GenerateOption) (*Account, error)
// Verify an account has access to a resource using the rules
Verify(acc *Account, res *Resource, opts ...VerifyOption) error
// Inspect a token
Inspect(token string) (*Account, error)
// Token generated using refresh token or credentials
Token(opts ...TokenOption) (*Token, error)
// Grant access to a resource
Grant(rule *Rule) error
// Revoke access to a resource
Revoke(rule *Rule) error
// Rules returns all the rules used to verify requests
Rules(...RulesOption) ([]*Rule, error)
// String returns the name of the implementation
String() string
}
// Account provided by an auth provider
type Account struct {
// ID of the account e.g. email
ID string `json:"id"`
// Type of the account, e.g. service
Type string `json:"type"`
// Issuer of the account
Issuer string `json:"issuer"`
// Any other associated metadata
Metadata metadata.Metadata `json:"metadata"`
// Scopes the account has access to
Scopes []string `json:"scopes"`
// Secret for the account, e.g. the password
Secret string `json:"secret"`
}
// Token can be short or long lived
type Token struct {
// The token to be used for accessing resources
AccessToken string `json:"access_token"`
// RefreshToken to be used to generate a new token
RefreshToken string `json:"refresh_token"`
// Time of token creation
Created time.Time `json:"created"`
// Time of token expiry
Expiry time.Time `json:"expiry"`
}
// Expired returns a boolean indicating if the token needs to be refreshed
func (t *Token) Expired() bool {
return t.Expiry.Unix() < time.Now().Unix()
}
// Resource is an entity such as a user or
type Resource struct {
// Name of the resource, e.g. go.micro.service.notes
Name string `json:"name"`
// Type of resource, e.g. service
Type string `json:"type"`
// Endpoint resource e.g NotesService.Create
Endpoint string `json:"endpoint"`
}
// Access defines the type of access a rule grants
type Access int
const (
// AccessGranted to a resource
AccessGranted Access = iota
// AccessDenied to a resource
AccessDenied
)
// Rule is used to verify access to a resource
type Rule struct {
// ID of the rule, e.g. "public"
ID string
// Scope the rule requires, a blank scope indicates open to the public and * indicates the rule
// applies to any valid account
Scope string
// Resource the rule applies to
Resource *Resource
// Access determines if the rule grants or denies access to the resource
Access Access
// Priority the rule should take when verifying a request, the higher the value the sooner the
// rule will be applied
Priority int32
}
type accountKey struct{}
// AccountFromContext gets the account from the context, which
// is set by the auth wrapper at the start of a call. If the account
// is not set, a nil account will be returned. The error is only returned
// when there was a problem retrieving an account
func AccountFromContext(ctx context.Context) (*Account, bool) {
if ctx == nil {
return nil, false
}
acc, ok := ctx.Value(accountKey{}).(*Account)
return acc, ok
}
// ContextWithAccount sets the account in the context
func ContextWithAccount(ctx context.Context, account *Account) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, accountKey{}, account)
}

View File

@ -1,78 +0,0 @@
package auth
import (
"github.com/google/uuid"
)
type noopAuth struct {
opts Options
}
// String returns the name of the implementation
func (n *noopAuth) String() string {
return "noop"
}
// Init the auth
func (n *noopAuth) Init(opts ...Option) {
for _, o := range opts {
o(&n.opts)
}
}
// Options set for auth
func (n *noopAuth) Options() Options {
return n.opts
}
// Generate a new account
func (n *noopAuth) Generate(id string, opts ...GenerateOption) (*Account, error) {
options := NewGenerateOptions(opts...)
return &Account{
ID: id,
Secret: options.Secret,
Metadata: options.Metadata,
Scopes: options.Scopes,
Issuer: n.Options().Issuer,
}, nil
}
// Grant access to a resource
func (n *noopAuth) Grant(rule *Rule) error {
return nil
}
// Revoke access to a resource
func (n *noopAuth) Revoke(rule *Rule) error {
return nil
}
// Rules used to verify requests
func (n *noopAuth) Rules(opts ...RulesOption) ([]*Rule, error) {
return []*Rule{}, nil
}
// Verify an account has access to a resource
func (n *noopAuth) Verify(acc *Account, res *Resource, opts ...VerifyOption) error {
return nil
}
// Inspect a token
func (n *noopAuth) Inspect(token string) (*Account, error) {
uid, err := uuid.NewRandom()
if err != nil {
return nil, err
}
return &Account{ID: uid.String(), Issuer: n.Options().Issuer}, nil
}
// Token generation using an account id and secret
func (n *noopAuth) Token(opts ...TokenOption) (*Token, error) {
return &Token{}, nil
}
// NewAuth returns new noop auth
func NewAuth(opts ...Option) Auth {
return &noopAuth{opts: NewOptions(opts...)}
}

View File

@ -1,312 +0,0 @@
package auth
import (
"context"
"time"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/store"
"github.com/unistack-org/micro/v3/tracer"
)
// NewOptions creates Options struct from slice of options
func NewOptions(opts ...Option) Options {
options := Options{
Tracer: tracer.DefaultTracer,
Logger: logger.DefaultLogger,
Meter: meter.DefaultMeter,
}
for _, o := range opts {
o(&options)
}
return options
}
type Options struct {
// Issuer of the service's account
Issuer string
// ID is the services auth ID
ID string
// Secret is used to authenticate the service
Secret string
// Token is the services token used to authenticate itself
Token *Token
// PublicKey for decoding JWTs
PublicKey string
// PrivateKey for encoding JWTs
PrivateKey string
// LoginURL is the relative url path where a user can login
LoginURL string
// Store to back auth
Store store.Store
// Addrs sets the addresses of auth
Addrs []string
// Logger sets the logger
Logger logger.Logger
// Meter sets tht meter
Meter meter.Meter
// Tracer
Tracer tracer.Tracer
// Context to store other options
Context context.Context
}
// Option func
type Option func(o *Options)
// Addrs is the auth addresses to use
func Addrs(addrs ...string) Option {
return func(o *Options) {
o.Addrs = addrs
}
}
// Issuer of the services account
func Issuer(i string) Option {
return func(o *Options) {
o.Issuer = i
}
}
// Store to back auth
func Store(s store.Store) Option {
return func(o *Options) {
o.Store = s
}
}
// PublicKey is the JWT public key
func PublicKey(key string) Option {
return func(o *Options) {
o.PublicKey = key
}
}
// PrivateKey is the JWT private key
func PrivateKey(key string) Option {
return func(o *Options) {
o.PrivateKey = key
}
}
// Credentials sets the auth credentials
func Credentials(id, secret string) Option {
return func(o *Options) {
o.ID = id
o.Secret = secret
}
}
// ClientToken sets the auth token to use when making requests
func ClientToken(token *Token) Option {
return func(o *Options) {
o.Token = token
}
}
// LoginURL sets the auth LoginURL
func LoginURL(url string) Option {
return func(o *Options) {
o.LoginURL = url
}
}
// GenerateOptions struct
type GenerateOptions struct {
// Metadata associated with the account
Metadata metadata.Metadata
// Scopes the account has access too
Scopes []string
// Provider of the account, e.g. oauth
Provider string
// Type of the account, e.g. user
Type string
// Secret used to authenticate the account
Secret string
// Issuer of the account, e.g. micro
Issuer string
}
// GenerateOption func
type GenerateOption func(o *GenerateOptions)
// WithSecret for the generated account
func WithSecret(s string) GenerateOption {
return func(o *GenerateOptions) {
o.Secret = s
}
}
// WithType for the generated account
func WithType(t string) GenerateOption {
return func(o *GenerateOptions) {
o.Type = t
}
}
// WithMetadata for the generated account
func WithMetadata(md metadata.Metadata) GenerateOption {
return func(o *GenerateOptions) {
o.Metadata = metadata.Copy(md)
}
}
// WithProvider for the generated account
func WithProvider(p string) GenerateOption {
return func(o *GenerateOptions) {
o.Provider = p
}
}
// WithScopes for the generated account
func WithScopes(s ...string) GenerateOption {
return func(o *GenerateOptions) {
o.Scopes = s
}
}
// WithIssuer for the generated account
func WithIssuer(i string) GenerateOption {
return func(o *GenerateOptions) {
o.Issuer = i
}
}
// NewGenerateOptions from a slice of options
func NewGenerateOptions(opts ...GenerateOption) GenerateOptions {
var options GenerateOptions
for _, o := range opts {
o(&options)
}
return options
}
// TokenOptions struct
type TokenOptions struct {
// ID for the account
ID string
// Secret for the account
Secret string
// RefreshToken is used to refesh a token
RefreshToken string
// Expiry is the time the token should live for
Expiry time.Duration
// Issuer of the account
Issuer string
}
// TokenOption func
type TokenOption func(o *TokenOptions)
// WithExpiry for the token
func WithExpiry(ex time.Duration) TokenOption {
return func(o *TokenOptions) {
o.Expiry = ex
}
}
// WithCredentials sets tye id and secret
func WithCredentials(id, secret string) TokenOption {
return func(o *TokenOptions) {
o.ID = id
o.Secret = secret
}
}
// WithToken sets the refresh token
func WithToken(rt string) TokenOption {
return func(o *TokenOptions) {
o.RefreshToken = rt
}
}
// WithTokenIssuer sets the token issuer option
func WithTokenIssuer(iss string) TokenOption {
return func(o *TokenOptions) {
o.Issuer = iss
}
}
// NewTokenOptions from a slice of options
func NewTokenOptions(opts ...TokenOption) TokenOptions {
var options TokenOptions
for _, o := range opts {
o(&options)
}
// set default expiry of token
if options.Expiry == 0 {
options.Expiry = time.Minute
}
return options
}
// VerifyOptions struct
type VerifyOptions struct {
Context context.Context
Namespace string
}
// VerifyOption func
type VerifyOption func(o *VerifyOptions)
// VerifyContext pass context to verify
func VerifyContext(ctx context.Context) VerifyOption {
return func(o *VerifyOptions) {
o.Context = ctx
}
}
// VerifyNamespace sets thhe namespace for verify
func VerifyNamespace(ns string) VerifyOption {
return func(o *VerifyOptions) {
o.Namespace = ns
}
}
// RulesOptions struct
type RulesOptions struct {
Context context.Context
Namespace string
}
// RulesOption func
type RulesOption func(o *RulesOptions)
// RulesContext pass rules context
func RulesContext(ctx context.Context) RulesOption {
return func(o *RulesOptions) {
o.Context = ctx
}
}
// RulesNamespace sets the rule namespace
func RulesNamespace(ns string) RulesOption {
return func(o *RulesOptions) {
o.Namespace = ns
}
}
// Logger sets the logger
func Logger(l logger.Logger) Option {
return func(o *Options) {
o.Logger = l
}
}
// Meter sets the meter
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
// Tracer sets the meter
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}

View File

@ -1,91 +0,0 @@
package auth
import (
"fmt"
"sort"
"strings"
)
// VerifyAccess an account has access to a resource using the rules provided. If the account does not have
// access an error will be returned. If there are no rules provided which match the resource, an error
// will be returned
func VerifyAccess(rules []*Rule, acc *Account, res *Resource) error {
// the rule is only to be applied if the type matches the resource or is catch-all (*)
validTypes := []string{"*", res.Type}
// the rule is only to be applied if the name matches the resource or is catch-all (*)
validNames := []string{"*", res.Name}
// rules can have wildcard excludes on endpoints since this can also be a path for web services,
// e.g. /foo/* would include /foo/bar. We also want to check for wildcards and the exact endpoint
validEndpoints := []string{"*", res.Endpoint}
if comps := strings.Split(res.Endpoint, "/"); len(comps) > 1 {
for i := 1; i < len(comps)+1; i++ {
wildcard := fmt.Sprintf("%v/*", strings.Join(comps[0:i], "/"))
validEndpoints = append(validEndpoints, wildcard)
}
}
// filter the rules to the ones which match the criteria above
filteredRules := make([]*Rule, 0)
for _, rule := range rules {
if !include(validTypes, rule.Resource.Type) {
continue
}
if !include(validNames, rule.Resource.Name) {
continue
}
if !include(validEndpoints, rule.Resource.Endpoint) {
continue
}
filteredRules = append(filteredRules, rule)
}
// sort the filtered rules by priority, highest to lowest
sort.SliceStable(filteredRules, func(i, j int) bool {
return filteredRules[i].Priority > filteredRules[j].Priority
})
// loop through the rules and check for a rule which applies to this account
for _, rule := range filteredRules {
// a blank scope indicates the rule applies to everyone, even nil accounts
if rule.Scope == ScopePublic && rule.Access == AccessDenied {
return ErrForbidden
} else if rule.Scope == ScopePublic && rule.Access == AccessGranted {
return nil
}
// all further checks require an account
if acc == nil {
continue
}
// this rule applies to any account
if rule.Scope == ScopeAccount && rule.Access == AccessDenied {
return ErrForbidden
} else if rule.Scope == ScopeAccount && rule.Access == AccessGranted {
return nil
}
// if the account has the necessary scope
if include(acc.Scopes, rule.Scope) && rule.Access == AccessDenied {
return ErrForbidden
} else if include(acc.Scopes, rule.Scope) && rule.Access == AccessGranted {
return nil
}
}
// if no rules matched then return forbidden
return ErrForbidden
}
// include is a helper function which checks to see if the slice contains the value. includes is
// not case sensitive.
func include(slice []string, val string) bool {
for _, s := range slice {
if strings.EqualFold(s, val) {
return true
}
}
return false
}

View File

@ -1,288 +0,0 @@
package auth
import (
"testing"
)
func TestVerify(t *testing.T) {
srvResource := &Resource{
Type: "service",
Name: "go.micro.service.foo",
Endpoint: "Foo.Bar",
}
webResource := &Resource{
Type: "service",
Name: "go.micro.web.foo",
Endpoint: "/foo/bar",
}
catchallResource := &Resource{
Type: "*",
Name: "*",
Endpoint: "*",
}
tt := []struct {
Name string
Rules []*Rule
Account *Account
Resource *Resource
Error error
}{
{
Name: "NoRules",
Rules: []*Rule{},
Account: nil,
Resource: srvResource,
Error: ErrForbidden,
},
{
Name: "CatchallPublicAccount",
Account: &Account{},
Resource: srvResource,
Rules: []*Rule{
{
Scope: "",
Resource: catchallResource,
},
},
},
{
Name: "CatchallPublicNoAccount",
Resource: srvResource,
Rules: []*Rule{
{
Scope: "",
Resource: catchallResource,
},
},
},
{
Name: "CatchallPrivateAccount",
Account: &Account{},
Resource: srvResource,
Rules: []*Rule{
{
Scope: "*",
Resource: catchallResource,
},
},
},
{
Name: "CatchallPrivateNoAccount",
Resource: srvResource,
Rules: []*Rule{
{
Scope: "*",
Resource: catchallResource,
},
},
Error: ErrForbidden,
},
{
Name: "CatchallServiceRuleMatch",
Resource: srvResource,
Account: &Account{},
Rules: []*Rule{
{
Scope: "*",
Resource: &Resource{
Type: srvResource.Type,
Name: srvResource.Name,
Endpoint: "*",
},
},
},
},
{
Name: "CatchallServiceRuleNoMatch",
Resource: srvResource,
Account: &Account{},
Rules: []*Rule{
{
Scope: "*",
Resource: &Resource{
Type: srvResource.Type,
Name: "wrongname",
Endpoint: "*",
},
},
},
Error: ErrForbidden,
},
{
Name: "ExactRuleValidScope",
Resource: srvResource,
Account: &Account{
Scopes: []string{"neededscope"},
},
Rules: []*Rule{
{
Scope: "neededscope",
Resource: srvResource,
},
},
},
{
Name: "ExactRuleInvalidScope",
Resource: srvResource,
Account: &Account{
Scopes: []string{"neededscope"},
},
Rules: []*Rule{
{
Scope: "invalidscope",
Resource: srvResource,
},
},
Error: ErrForbidden,
},
{
Name: "CatchallDenyWithAccount",
Resource: srvResource,
Account: &Account{},
Rules: []*Rule{
{
Scope: "*",
Resource: catchallResource,
Access: AccessDenied,
},
},
Error: ErrForbidden,
},
{
Name: "CatchallDenyWithNoAccount",
Resource: srvResource,
Account: &Account{},
Rules: []*Rule{
{
Scope: "*",
Resource: catchallResource,
Access: AccessDenied,
},
},
Error: ErrForbidden,
},
{
Name: "RulePriorityGrantFirst",
Resource: srvResource,
Account: &Account{},
Rules: []*Rule{
{
Scope: "*",
Resource: catchallResource,
Access: AccessGranted,
Priority: 1,
},
{
Scope: "*",
Resource: catchallResource,
Access: AccessDenied,
Priority: 0,
},
},
},
{
Name: "RulePriorityDenyFirst",
Resource: srvResource,
Account: &Account{},
Rules: []*Rule{
{
Scope: "*",
Resource: catchallResource,
Access: AccessGranted,
Priority: 0,
},
{
Scope: "*",
Resource: catchallResource,
Access: AccessDenied,
Priority: 1,
},
},
Error: ErrForbidden,
},
{
Name: "WebExactEndpointValid",
Resource: webResource,
Account: &Account{},
Rules: []*Rule{
{
Scope: "*",
Resource: webResource,
},
},
},
{
Name: "WebExactEndpointInalid",
Resource: webResource,
Account: &Account{},
Rules: []*Rule{
{
Scope: "*",
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "invalidendpoint",
},
},
},
Error: ErrForbidden,
},
{
Name: "WebWildcardEndpoint",
Resource: webResource,
Account: &Account{},
Rules: []*Rule{
{
Scope: "*",
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "*",
},
},
},
},
{
Name: "WebWildcardPathEndpointValid",
Resource: webResource,
Account: &Account{},
Rules: []*Rule{
{
Scope: "*",
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "/foo/*",
},
},
},
},
{
Name: "WebWildcardPathEndpointInvalid",
Resource: webResource,
Account: &Account{},
Rules: []*Rule{
{
Scope: "*",
Resource: &Resource{
Type: webResource.Type,
Name: webResource.Name,
Endpoint: "/bar/*",
},
},
},
Error: ErrForbidden,
},
}
for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
if err := VerifyAccess(tc.Rules, tc.Account, tc.Resource); err != tc.Error {
t.Errorf("Expected %v but got %v", tc.Error, err)
}
})
}
}

View File

@ -1,49 +1,80 @@
// Package broker is an interface used for asynchronous messaging // Package broker is an interface used for asynchronous messaging
package broker package broker // import "go.unistack.org/micro/v4/broker"
import ( import (
"context" "context"
"errors"
"time"
"github.com/unistack-org/micro/v3/metadata" "go.unistack.org/micro/v4/metadata"
"go.unistack.org/micro/v4/options"
) )
// DefaultBroker default memory broker
var DefaultBroker Broker = NewBroker()
var ( var (
// DefaultBroker default broker // ErrNotConnected returns when broker used but not connected yet
DefaultBroker Broker = NewBroker() ErrNotConnected = errors.New("broker not connected")
// ErrDisconnected returns when broker disconnected
ErrDisconnected = errors.New("broker disconnected")
// ErrInvalidMessage returns when message has nvalid format
ErrInvalidMessage = errors.New("broker message has invalid format")
// DefaultGracefulTimeout
DefaultGracefulTimeout = 5 * time.Second
) )
// Broker is an interface used for asynchronous messaging. // Broker is an interface used for asynchronous messaging.
type Broker interface { type Broker interface {
Init(...Option) error // Name returns broker instance name
Name() string
// Init initilize broker
Init(opts ...options.Option) error
// Options returns broker options
Options() Options Options() Options
// Address return configured address
Address() string Address() string
Connect(context.Context) error // Connect connects to broker
Disconnect(context.Context) error Connect(ctx context.Context) error
Publish(context.Context, string, *Message, ...PublishOption) error // Disconnect disconnect from broker
Subscribe(context.Context, string, Handler, ...SubscribeOption) (Subscriber, error) Disconnect(ctx context.Context) error
// Publish message, msg can be single broker.Message or []broker.Message
Publish(ctx context.Context, msg interface{}, opts ...options.Option) error
// Subscribe subscribes to topic message via handler
Subscribe(ctx context.Context, topic string, handler interface{}, opts ...options.Option) (Subscriber, error)
// String type of broker
String() string String() string
} }
// Handler is used to process messages via a subscription of a topic. // Message is given to a subscription handler for processing
type Handler func(Event) error type Message interface {
// Context for the message
// Event is given to a subscription handler for processing Context() context.Context
type Event interface { // Topic
Topic() string Topic() string
Message() *Message // Header returns message headers
Header() metadata.Metadata
// Body returns broker message may be []byte slice or some go struct
Body() interface{}
// Ack acknowledge message
Ack() error Ack() error
// Error returns message error (like decoding errors or some other)
// In this case Body contains raw []byte from broker
Error() error Error() error
} }
// Message is used to transfer data
type Message struct {
Header metadata.Metadata // contains message metadata
Body []byte // contains message body
}
// Subscriber is a convenience return type for the Subscribe method // Subscriber is a convenience return type for the Subscribe method
type Subscriber interface { type Subscriber interface {
// Options returns subscriber options
Options() SubscribeOptions Options() SubscribeOptions
// Topic returns topic for subscription
Topic() string Topic() string
Unsubscribe(context.Context) error // Unsubscribe from topic
Unsubscribe(ctx context.Context) error
} }
// MessageHandler func signature for single message processing
type MessageHandler func(Message) error
// MessagesHandler func signature for batch message processing
type MessagesHandler func([]Message) error

View File

@ -22,33 +22,3 @@ func NewContext(ctx context.Context, s Broker) context.Context {
} }
return context.WithValue(ctx, brokerKey{}, s) return context.WithValue(ctx, brokerKey{}, s)
} }
// SetSubscribeOption returns a function to setup a context with given value
func SetSubscribeOption(k, v interface{}) SubscribeOption {
return func(o *SubscribeOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}
// SetOption returns a function to setup a context with given value
func SetOption(k, v interface{}) Option {
return func(o *Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}
// SetPublishOption returns a function to setup a context with given value
func SetPublishOption(k, v interface{}) PublishOption {
return func(o *PublishOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}

39
broker/context_test.go Normal file
View File

@ -0,0 +1,39 @@
package broker
import (
"context"
"testing"
)
func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), brokerKey{}, NewBroker())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("FromContext not works")
}
}
func TestFromNilContext(t *testing.T) {
// nolint: staticcheck
c, ok := FromContext(nil)
if ok || c != nil {
t.Fatal("FromContext not works")
}
}
func TestNewContext(t *testing.T) {
ctx := NewContext(context.TODO(), NewBroker())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestNewNilContext(t *testing.T) {
// nolint: staticcheck
ctx := NewContext(nil, NewBroker())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}

334
broker/memory.go Normal file
View File

@ -0,0 +1,334 @@
package broker
import (
"context"
"fmt"
"sync"
"time"
"go.unistack.org/micro/v4/codec"
"go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/metadata"
"go.unistack.org/micro/v4/options"
"go.unistack.org/micro/v4/semconv"
maddr "go.unistack.org/micro/v4/util/addr"
"go.unistack.org/micro/v4/util/id"
mnet "go.unistack.org/micro/v4/util/net"
"go.unistack.org/micro/v4/util/rand"
)
type MemoryBroker struct {
subscribers map[string][]*memorySubscriber
addr string
opts Options
sync.RWMutex
connected bool
}
func (m *MemoryBroker) Options() Options {
return m.opts
}
func (m *MemoryBroker) Address() string {
return m.addr
}
func (m *MemoryBroker) Connect(ctx context.Context) error {
m.Lock()
defer m.Unlock()
if m.connected {
return nil
}
// use 127.0.0.1 to avoid scan of all network interfaces
addr, err := maddr.Extract("127.0.0.1")
if err != nil {
return err
}
var rng rand.Rand
i := rng.Intn(20000)
// set addr with port
addr = mnet.HostPort(addr, 10000+i)
m.addr = addr
m.connected = true
return nil
}
func (m *MemoryBroker) Disconnect(ctx context.Context) error {
m.Lock()
defer m.Unlock()
select {
case <-ctx.Done():
return ctx.Err()
default:
if m.connected {
m.connected = false
}
}
return nil
}
func (m *MemoryBroker) Init(opts ...options.Option) error {
var err error
for _, o := range opts {
if err = o(&m.opts); err != nil {
return err
}
}
return nil
}
func (m *MemoryBroker) Publish(ctx context.Context, message interface{}, opts ...options.Option) error {
m.RLock()
if !m.connected {
m.RUnlock()
return ErrNotConnected
}
m.RUnlock()
var err error
select {
case <-ctx.Done():
return ctx.Err()
default:
options := NewPublishOptions(opts...)
var msgs []Message
switch v := message.(type) {
case []Message:
msgs = v
case Message:
msgs = append(msgs, v)
default:
return ErrInvalidMessage
}
msgTopicMap := make(map[string][]*memoryMessage)
for _, msg := range msgs {
p := &memoryMessage{opts: options}
p.topic, _ = msg.Header().Get(metadata.HeaderTopic)
if v, ok := msg.Body().(*codec.Frame); ok {
p.body = msg.Body()
} else if len(m.opts.Codecs) == 0 {
p.body = msg.Body()
} else {
cf, ok := m.opts.Codecs[options.ContentType]
if !ok {
return fmt.Errorf("%s: %s", codec.ErrUnknownContentType, options.ContentType)
}
p.body, err = cf.Marshal(v)
if err != nil {
return err
}
}
msgTopicMap[p.topic] = append(msgTopicMap[p.topic], p)
}
eh := m.opts.ErrorHandler
for t, ms := range msgTopicMap {
ts := time.Now()
m.opts.Meter.Counter(semconv.PublishMessageInflight, "endpoint", t).Add(len(ms))
m.opts.Meter.Counter(semconv.SubscribeMessageInflight, "endpoint", t).Add(len(ms))
m.RLock()
subs, ok := m.subscribers[t]
m.RUnlock()
if !ok {
m.opts.Meter.Counter(semconv.PublishMessageTotal, "endpoint", t, "status", "failure").Add(len(ms))
m.opts.Meter.Counter(semconv.PublishMessageInflight, "endpoint", t).Add(-len(ms))
m.opts.Meter.Counter(semconv.SubscribeMessageInflight, "endpoint", t).Add(-len(ms))
continue
}
m.opts.Meter.Counter(semconv.PublishMessageTotal, "endpoint", t, "status", "success").Add(len(ms))
for _, sub := range subs {
if sub.opts.ErrorHandler != nil {
eh = sub.opts.ErrorHandler
}
switch mh := sub.handler.(type) {
case MessagesHandler:
mhs := make([]Message, 0, len(ms))
for _, m := range ms {
mhs = append(mhs, m)
}
if err = mh(mhs); err != nil {
m.opts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", t, "status", "failure").Add(len(ms))
if eh != nil {
switch meh := eh.(type) {
case MessagesHandler:
_ = meh(mhs)
case MessageHandler:
for _, me := range mhs {
_ = meh(me)
}
}
} else if m.opts.Logger.V(logger.ErrorLevel) {
m.opts.Logger.Error(m.opts.Context, err.Error())
}
}
case MessageHandler:
for _, p := range ms {
if err = mh(p); err != nil {
m.opts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", t, "status", "failure").Inc()
if eh != nil {
switch meh := eh.(type) {
case MessageHandler:
_ = meh(p)
case MessagesHandler:
_ = meh([]Message{p})
}
} else if m.opts.Logger.V(logger.ErrorLevel) {
m.opts.Logger.Error(m.opts.Context, err.Error())
}
} else {
if sub.opts.AutoAck {
if err = p.Ack(); err != nil {
m.opts.Logger.Error(m.opts.Context, "ack failed: "+err.Error())
m.opts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", t, "status", "failure").Inc()
} else {
m.opts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", t, "status", "success").Inc()
}
} else {
m.opts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", t, "status", "success").Inc()
}
}
m.opts.Meter.Counter(semconv.PublishMessageInflight, "endpoint", t).Add(-1)
m.opts.Meter.Counter(semconv.SubscribeMessageInflight, "endpoint", t).Add(-1)
}
}
}
te := time.Since(ts)
m.opts.Meter.Summary(semconv.PublishMessageLatencyMicroseconds, "endpoint", t).Update(te.Seconds())
m.opts.Meter.Histogram(semconv.PublishMessageDurationSeconds, "endpoint", t).Update(te.Seconds())
m.opts.Meter.Summary(semconv.SubscribeMessageLatencyMicroseconds, "endpoint", t).Update(te.Seconds())
m.opts.Meter.Histogram(semconv.SubscribeMessageDurationSeconds, "endpoint", t).Update(te.Seconds())
}
}
return nil
}
func (m *MemoryBroker) Subscribe(ctx context.Context, topic string, handler interface{}, opts ...options.Option) (Subscriber, error) {
m.RLock()
if !m.connected {
m.RUnlock()
return nil, ErrNotConnected
}
m.RUnlock()
sid, err := id.New()
if err != nil {
return nil, err
}
options := NewSubscribeOptions(opts...)
sub := &memorySubscriber{
exit: make(chan bool, 1),
id: sid,
topic: topic,
handler: handler,
opts: options,
ctx: ctx,
}
m.Lock()
m.subscribers[topic] = append(m.subscribers[topic], sub)
m.Unlock()
go func() {
<-sub.exit
m.Lock()
newSubscribers := make([]*memorySubscriber, 0, len(m.subscribers)-1)
for _, sb := range m.subscribers[topic] {
if sb.id == sub.id {
continue
}
newSubscribers = append(newSubscribers, sb)
}
m.subscribers[topic] = newSubscribers
m.Unlock()
}()
return sub, nil
}
func (m *MemoryBroker) String() string {
return "memory"
}
func (m *MemoryBroker) Name() string {
return m.opts.Name
}
type memoryMessage struct {
err error
body interface{}
topic string
header metadata.Metadata
opts PublishOptions
ctx context.Context
}
func (m *memoryMessage) Topic() string {
return m.topic
}
func (m *memoryMessage) Header() metadata.Metadata {
return m.header
}
func (m *memoryMessage) Body() interface{} {
return m.body
}
func (m *memoryMessage) Ack() error {
return nil
}
func (m *memoryMessage) Error() error {
return m.err
}
func (m *memoryMessage) Context() context.Context {
return m.ctx
}
type memorySubscriber struct {
ctx context.Context
exit chan bool
handler interface{}
id string
topic string
opts SubscribeOptions
}
func (m *memorySubscriber) Options() SubscribeOptions {
return m.opts
}
func (m *memorySubscriber) Topic() string {
return m.topic
}
func (m *memorySubscriber) Unsubscribe(ctx context.Context) error {
m.exit <- true
return nil
}
// NewBroker return new memory broker
func NewBroker(opts ...options.Option) *MemoryBroker {
return &MemoryBroker{
opts: NewOptions(opts...),
subscribers: make(map[string][]*memorySubscriber),
}
}

107
broker/memory_test.go Normal file
View File

@ -0,0 +1,107 @@
package broker
import (
"context"
"fmt"
"testing"
"go.unistack.org/micro/v4/metadata"
)
func TestMemoryBatchBroker(t *testing.T) {
b := NewBroker()
ctx := context.Background()
if err := b.Connect(ctx); err != nil {
t.Fatalf("Unexpected connect error %v", err)
}
topic := "test"
count := 10
fn := func(evts []Message) error {
var err error
for _, evt := range evts {
if err = evt.Ack(); err != nil {
return err
}
}
return nil
}
sub, err := b.Subscribe(ctx, topic, fn)
if err != nil {
t.Fatalf("Unexpected error subscribing %v", err)
}
msgs := make([]Message, 0, count)
for i := 0; i < count; i++ {
message := &memoryMessage{
header: metadata.Metadata{
metadata.HeaderTopic: []string{topic},
"foo": []string{"bar"},
"id": []string{fmt.Sprintf("%d", i)},
},
body: []byte(`"hello world"`),
}
msgs = append(msgs, message)
}
if err := b.Publish(ctx, msgs); err != nil {
t.Fatalf("Unexpected error publishing %v", err)
}
if err := sub.Unsubscribe(ctx); err != nil {
t.Fatalf("Unexpected error unsubscribing from %s: %v", topic, err)
}
if err := b.Disconnect(ctx); err != nil {
t.Fatalf("Unexpected connect error %v", err)
}
}
func TestMemoryBroker(t *testing.T) {
b := NewBroker()
ctx := context.Background()
if err := b.Connect(ctx); err != nil {
t.Fatalf("Unexpected connect error %v", err)
}
topic := "test"
count := 10
fn := func(p Message) error {
return p.Ack()
}
sub, err := b.Subscribe(ctx, topic, fn)
if err != nil {
t.Fatalf("Unexpected error subscribing %v", err)
}
msgs := make([]Message, 0, count)
for i := 0; i < count; i++ {
message := &memoryMessage{
header: metadata.Metadata{
metadata.HeaderTopic: []string{topic},
"foo": []string{"bar"},
"id": []string{fmt.Sprintf("%d", i)},
},
body: []byte(`"hello world"`),
}
msgs = append(msgs, message)
}
if err := b.Publish(ctx, msgs); err != nil {
t.Fatalf("Unexpected error publishing %v", err)
}
if err := sub.Unsubscribe(ctx); err != nil {
t.Fatalf("Unexpected error unsubscribing from %s: %v", topic, err)
}
if err := b.Disconnect(ctx); err != nil {
t.Fatalf("Unexpected connect error %v", err)
}
}

View File

@ -1,77 +0,0 @@
package broker
import "context"
type noopBroker struct {
opts Options
}
type noopSubscriber struct {
topic string
opts SubscribeOptions
}
// NewBroker returns new noop broker
func NewBroker(opts ...Option) Broker {
return &noopBroker{opts: NewOptions(opts...)}
}
// Init initialize broker
func (n *noopBroker) Init(opts ...Option) error {
for _, o := range opts {
o(&n.opts)
}
return nil
}
// Options returns broker Options
func (n *noopBroker) Options() Options {
return n.opts
}
// Address returns broker address
func (n *noopBroker) Address() string {
return ""
}
// Connect connects to broker
func (n *noopBroker) Connect(ctx context.Context) error {
return nil
}
// Disconnect disconnects from broker
func (n *noopBroker) Disconnect(ctx context.Context) error {
return nil
}
// Publish publishes message to broker
func (n *noopBroker) Publish(ctx context.Context, topic string, m *Message, opts ...PublishOption) error {
return nil
}
// Subscribe subscribes to broker topic
func (n *noopBroker) Subscribe(ctx context.Context, topic string, h Handler, opts ...SubscribeOption) (Subscriber, error) {
options := NewSubscribeOptions(opts...)
return &noopSubscriber{topic: topic, opts: options}, nil
}
// String return broker string representation
func (n *noopBroker) String() string {
return "noop"
}
// Options returns subscriber options
func (n *noopSubscriber) Options() SubscribeOptions {
return n.opts
}
// TOpic returns subscriber topic
func (n *noopSubscriber) Topic() string {
return n.topic
}
// Unsubscribe unsbscribes from broker topic
func (n *noopSubscriber) Unsubscribe(ctx context.Context) error {
return nil
}

View File

@ -3,243 +3,165 @@ package broker
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
"time"
"github.com/unistack-org/micro/v3/codec" "go.unistack.org/micro/v4/codec"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v4/logger"
"github.com/unistack-org/micro/v3/meter" "go.unistack.org/micro/v4/metadata"
"github.com/unistack-org/micro/v3/registry" "go.unistack.org/micro/v4/meter"
"github.com/unistack-org/micro/v3/tracer" "go.unistack.org/micro/v4/options"
"go.unistack.org/micro/v4/register"
"go.unistack.org/micro/v4/sync"
"go.unistack.org/micro/v4/tracer"
) )
// Options struct // Options struct
type Options struct { type Options struct {
// Addrs useed by broker // Tracer used for tracing
Addrs []string
// ErrorHandler executed when errors occur processing messages
ErrorHandler Handler
// Codec used to marshal/unmarshal messages
Codec codec.Codec
// Logger the used logger
Logger logger.Logger
// Meter the used for metrics
Meter meter.Meter
// Tracer used for trace
Tracer tracer.Tracer Tracer tracer.Tracer
// TLSConfig for secure communication // Register can be used for clustering
TLSConfig *tls.Config Register register.Register
// Registry used for clustering // Codecs holds the codec for marshal/unmarshal
Registry registry.Registry Codecs map[string]codec.Codec
// Context is used for non default options // Logger used for logging
Logger logger.Logger
// Meter used for metrics
Meter meter.Meter
// Context holds external options
Context context.Context Context context.Context
// TLSConfig holds tls.TLSConfig options
TLSConfig *tls.Config
// ErrorHandler used when broker have error while processing message
ErrorHandler interface{}
// Name holds the broker name
Name string
// Address holds the broker address
Address []string
Wait *sync.WaitGroup
GracefulTimeout time.Duration
} }
// NewOptions create new Options // NewOptions create new Options
func NewOptions(opts ...Option) Options { func NewOptions(opts ...options.Option) Options {
options := Options{ newOpts := Options{
Registry: registry.DefaultRegistry, Register: register.DefaultRegister,
Logger: logger.DefaultLogger, Logger: logger.DefaultLogger,
Context: context.Background(), Context: context.Background(),
Meter: meter.DefaultMeter, Meter: meter.DefaultMeter,
Codec: codec.DefaultCodec, Codecs: make(map[string]codec.Codec),
Tracer: tracer.DefaultTracer, Tracer: tracer.DefaultTracer,
GracefulTimeout: DefaultGracefulTimeout,
} }
for _, o := range opts { for _, o := range opts {
o(&options) o(&newOpts)
}
return options
}
// Context sets the context option
func Context(ctx context.Context) Option {
return func(o *Options) {
o.Context = ctx
} }
return newOpts
} }
// PublishOptions struct // PublishOptions struct
type PublishOptions struct { type PublishOptions struct {
// BodyOnly says that only body of the message must be published // Context holds external options
BodyOnly bool
// Context for non default options
Context context.Context Context context.Context
// Message metadata usually passed as message headers
Metadata metadata.Metadata
// Content-Type of message for marshal
ContentType string
// Topic destination
Topic string
// BodyOnly flag says the message contains raw body bytes
BodyOnly bool
} }
// NewPublishOptions creates PublishOptions struct // NewPublishOptions creates PublishOptions struct
func NewPublishOptions(opts ...PublishOption) PublishOptions { func NewPublishOptions(opts ...options.Option) PublishOptions {
options := PublishOptions{ options := PublishOptions{
Context: context.Background(), Context: context.Background(),
} }
for _, o := range opts { for _, o := range opts {
o(&options) o(&options)
} }
return options return options
} }
// PublishTopic pass topic for messages
func PublishTopic(t string) options.Option {
return func(src interface{}) error {
return options.Set(src, t, ".Topic")
}
}
// SubscribeOptions struct // SubscribeOptions struct
type SubscribeOptions struct { type SubscribeOptions struct {
// AutoAck ack messages if handler returns nil err // Context holds external options
AutoAck bool
// ErrorHandler executed when errors occur processing messages
ErrorHandler Handler
// Group for subscriber, Subscribers with the same group name
// will create a shared subscription where each
// receives a subset of messages.
Group string
// BodyOnly says that consumed only body of the message
BodyOnly bool
// Context is used for non default options
Context context.Context Context context.Context
} // ErrorHandler used when broker have error while processing message
ErrorHandler interface{}
// Option func // QueueGroup holds consumer group
type Option func(*Options) QueueGroup string
// AutoAck flag specifies auto ack of incoming message when no error happens
// PublishOption func AutoAck bool
type PublishOption func(*PublishOptions) // BodyOnly flag specifies that message contains only body bytes without header
BodyOnly bool
// PublishBodyOnly publish only body of the message // BatchSize flag specifies max batch size
func PublishBodyOnly(b bool) PublishOption { BatchSize int
return func(o *PublishOptions) { // BatchWait flag specifies max wait time for batch filling
o.BodyOnly = b BatchWait time.Duration
}
}
// PublishContext sets the context
func PublishContext(ctx context.Context) PublishOption {
return func(o *PublishOptions) {
o.Context = ctx
}
}
// SubscribeOption func
type SubscribeOption func(*SubscribeOptions)
// NewSubscribeOptions creates new SubscribeOptions
func NewSubscribeOptions(opts ...SubscribeOption) SubscribeOptions {
options := SubscribeOptions{
AutoAck: true,
Context: context.Background(),
}
for _, o := range opts {
o(&options)
}
return options
}
// Addrs sets the host addresses to be used by the broker
func Addrs(addrs ...string) Option {
return func(o *Options) {
o.Addrs = addrs
}
}
// Codec sets the codec used for encoding/decoding used where
// a broker does not support headers
func Codec(c codec.Codec) Option {
return func(o *Options) {
o.Codec = c
}
}
// DisableAutoAck disables auto ack
func DisableAutoAck() SubscribeOption {
return func(o *SubscribeOptions) {
o.AutoAck = false
}
}
// SubscribeAutoAck will disable auto acking of messages
// after they have been handled.
func SubscribeAutoAck(b bool) SubscribeOption {
return func(o *SubscribeOptions) {
o.AutoAck = b
}
}
// SubscribeBodyOnly consumes only body of the message
func SubscribeBodyOnly(b bool) SubscribeOption {
return func(o *SubscribeOptions) {
o.BodyOnly = b
}
} }
// ErrorHandler will catch all broker errors that cant be handled // ErrorHandler will catch all broker errors that cant be handled
// in normal way, for example Codec errors // in normal way, for example Codec errors
func ErrorHandler(h Handler) Option { func ErrorHandler(h interface{}) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.ErrorHandler = h return options.Set(src, h, ".ErrorHandler")
} }
} }
// SubscribeErrorHandler will catch all broker errors that cant be handled // NewSubscribeOptions creates new SubscribeOptions
// in normal way, for example Codec errors func NewSubscribeOptions(opts ...options.Option) SubscribeOptions {
func SubscribeErrorHandler(h Handler) SubscribeOption { options := SubscribeOptions{
return func(o *SubscribeOptions) { AutoAck: true,
o.ErrorHandler = h Context: context.Background(),
}
for _, o := range opts {
o(&options)
}
return options
}
// SubscribeAutoAck contol auto acking of messages
// after they have been handled.
func SubscribeAutoAck(b bool) options.Option {
return func(src interface{}) error {
return options.Set(src, b, ".AutoAck")
} }
} }
// Queue sets the subscribers queue // BodyOnly transfer only body without
// Deprecated func BodyOnly(b bool) options.Option {
func Queue(name string) SubscribeOption { return func(src interface{}) error {
return func(o *SubscribeOptions) { return options.Set(src, b, ".BodyOnly")
o.Group = name
} }
} }
// SubscribeGroup sets the name of the queue to share messages on // SubscribeBatchSize specifies max batch size
func SubscribeGroup(name string) SubscribeOption { func SubscribeBatchSize(n int) options.Option {
return func(o *SubscribeOptions) { return func(src interface{}) error {
o.Group = name return options.Set(src, n, ".BatchSize")
} }
} }
// Registry sets registry option // SubscribeBatchWait specifies max batch wait time
func Registry(r registry.Registry) Option { func SubscribeBatchWait(td time.Duration) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.Registry = r return options.Set(src, td, ".BatchWait")
} }
} }
// TLSConfig sets the TLS Config // SubscribeQueueGroup sets the shared queue name distributed messages across subscribers
func TLSConfig(t *tls.Config) Option { func SubscribeQueueGroup(n string) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.TLSConfig = t return options.Set(src, n, ".QueueGroup")
}
}
// Logger sets the logger
func Logger(l logger.Logger) Option {
return func(o *Options) {
o.Logger = l
}
}
// Tracer to be used for tracing
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}
// Meter sets the meter
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
// SubscribeContext set context
func SubscribeContext(ctx context.Context) SubscribeOption {
return func(o *SubscribeOptions) {
o.Context = ctx
} }
} }

98
broker/subscriber.go Normal file
View File

@ -0,0 +1,98 @@
package broker
import (
"fmt"
"reflect"
"strings"
"unicode"
"unicode/utf8"
)
const (
subSig = "func(context.Context, interface{}) error"
batchSubSig = "func([]context.Context, []interface{}) error"
)
// Precompute the reflect type for error. Can't use error directly
// because Typeof takes an empty interface value. This is annoying.
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
// Is this an exported - upper case - name?
func isExported(name string) bool {
r, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(r)
}
// Is this type exported or a builtin?
func isExportedOrBuiltinType(t reflect.Type) bool {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
// PkgPath will be non-empty even for an exported type,
// so we need to check the type name as well.
return isExported(t.Name()) || t.PkgPath() == ""
}
// ValidateSubscriber func signature
func ValidateSubscriber(sub interface{}) error {
typ := reflect.TypeOf(sub)
var argType reflect.Type
switch typ.Kind() {
case reflect.Func:
name := "Func"
switch typ.NumIn() {
case 1: // func(Message) error
case 2: // func(context.Context, Message) error or func(context.Context, []Message) error
argType = typ.In(2)
// if sub.Options().Batch {
if argType.Kind() != reflect.Slice {
return fmt.Errorf("subscriber %v dont have required signature %s", name, batchSubSig)
}
if strings.Compare(fmt.Sprintf("%v", argType), "[]interface{}") == 0 {
return fmt.Errorf("subscriber %v dont have required signaure %s", name, batchSubSig)
}
// }
default:
return fmt.Errorf("subscriber %v takes wrong number of args: %v required signature %s or %s", name, typ.NumIn(), subSig, batchSubSig)
}
if !isExportedOrBuiltinType(argType) {
return fmt.Errorf("subscriber %v argument type not exported: %v", name, argType)
}
if typ.NumOut() != 1 {
return fmt.Errorf("subscriber %v has wrong number of return values: %v require signature %s or %s",
name, typ.NumOut(), subSig, batchSubSig)
}
if returnType := typ.Out(0); returnType != typeOfError {
return fmt.Errorf("subscriber %v returns %v not error", name, returnType.String())
}
default:
hdlr := reflect.ValueOf(sub)
name := reflect.Indirect(hdlr).Type().Name()
for m := 0; m < typ.NumMethod(); m++ {
method := typ.Method(m)
switch method.Type.NumIn() {
case 3:
argType = method.Type.In(2)
default:
return fmt.Errorf("subscriber %v.%v takes wrong number of args: %v required signature %s or %s",
name, method.Name, method.Type.NumIn(), subSig, batchSubSig)
}
if !isExportedOrBuiltinType(argType) {
return fmt.Errorf("%v argument type not exported: %v", name, argType)
}
if method.Type.NumOut() != 1 {
return fmt.Errorf(
"subscriber %v.%v has wrong number of return values: %v require signature %s or %s",
name, method.Name, method.Type.NumOut(), subSig, batchSubSig)
}
if returnType := method.Type.Out(0); returnType != typeOfError {
return fmt.Errorf("subscriber %v.%v returns %v not error", name, method.Name, returnType.String())
}
}
}
return nil
}

View File

@ -1,32 +0,0 @@
// Package build is for building source into a package
package build
// Build is an interface for building packages
type Build interface {
// Package builds a package
Package(name string, src *Source) (*Package, error)
// Remove removes the package
Remove(*Package) error
}
// Source is the source of a build
type Source struct {
// Path to the source if local
Path string
// Language is the language of code
Language string
// Location of the source
Repository string
}
// Package is packaged format for source
type Package struct {
// Name of the package
Name string
// Location of the package
Path string
// Type of package e.g tarball, binary, docker
Type string
// Source of the package
Source *Source
}

View File

@ -1,17 +0,0 @@
package build
// Options struct
type Options struct {
// local path to download source
Path string
}
// Option func
type Option func(o *Options)
// Path is the Local path for repository
func Path(p string) Option {
return func(o *Options) {
o.Path = p
}
}

View File

@ -2,14 +2,29 @@ package client
import ( import (
"context" "context"
"math"
"time" "time"
"github.com/unistack-org/micro/v3/util/backoff" "go.unistack.org/micro/v4/util/backoff"
) )
// BackoffFunc is the backoff call func // BackoffFunc is the backoff call func
type BackoffFunc func(ctx context.Context, req Request, attempts int) (time.Duration, error) type BackoffFunc func(ctx context.Context, req Request, attempts int) (time.Duration, error)
func exponentialBackoff(ctx context.Context, req Request, attempts int) (time.Duration, error) { // BackoffExp using exponential backoff func
func BackoffExp(_ context.Context, _ Request, attempts int) (time.Duration, error) {
return backoff.Do(attempts), nil return backoff.Do(attempts), nil
} }
// BackoffInterval specifies randomization interval for backoff func
func BackoffInterval(min time.Duration, max time.Duration) BackoffFunc {
return func(_ context.Context, _ Request, attempts int) (time.Duration, error) {
td := time.Duration(math.Pow(float64(attempts), math.E)) * time.Millisecond * 100
if td < min {
return min, nil
} else if td > max {
return max, nil
}
return td, nil
}
}

View File

@ -6,7 +6,7 @@ import (
"time" "time"
) )
func TestBackoff(t *testing.T) { func TestBackoffExp(t *testing.T) {
results := []time.Duration{ results := []time.Duration{
0 * time.Second, 0 * time.Second,
100 * time.Millisecond, 100 * time.Millisecond,
@ -22,7 +22,7 @@ func TestBackoff(t *testing.T) {
} }
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
d, err := exponentialBackoff(context.TODO(), r, i) d, err := BackoffExp(context.TODO(), r, i)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -32,3 +32,25 @@ func TestBackoff(t *testing.T) {
} }
} }
} }
func TestBackoffInterval(t *testing.T) {
min := 100 * time.Millisecond
max := 300 * time.Millisecond
r := &testRequest{
service: "test",
method: "test",
}
fn := BackoffInterval(min, max)
for i := 0; i < 5; i++ {
d, err := fn(context.TODO(), r, i)
if err != nil {
t.Fatal(err)
}
if d < min || d > max {
t.Fatalf("Expected %v < %v < %v", min, d, max)
}
}
}

View File

@ -1,40 +1,48 @@
// Package client is an interface for an RPC client // Package client is an interface for an RPC client
package client package client // import "go.unistack.org/micro/v4/client"
import ( import (
"context" "context"
"time" "time"
"github.com/unistack-org/micro/v3/codec" "go.unistack.org/micro/v4/codec"
"github.com/unistack-org/micro/v3/metadata" "go.unistack.org/micro/v4/options"
) )
var ( var (
// DefaultClient is the global default client // DefaultClient is the global default client
DefaultClient Client = NewClient() DefaultClient = NewClient()
// DefaultContentType is the default content-type if not specified
DefaultContentType = ""
// DefaultBackoff is the default backoff function for retries (minimum 10 millisecond and maximum 5 second)
DefaultBackoff = BackoffInterval(10*time.Millisecond, 5*time.Second)
// DefaultRetry is the default check-for-retry function for retries
DefaultRetry = RetryNever
// DefaultRetries is the default number of times a request is tried
DefaultRetries = 0
// DefaultRequestTimeout is the default request timeout
DefaultRequestTimeout = time.Second * 5
// DefaultDialTimeout the default dial timeout
DefaultDialTimeout = time.Second * 5
// DefaultPoolSize sets the connection pool size
DefaultPoolSize = 100
// DefaultPoolTTL sets the connection pool ttl
DefaultPoolTTL = time.Minute
) )
// Client is the interface used to make requests to services. // Client is the interface used to make requests to services.
// It supports Request/Response via Transport and Publishing via the Broker. // It supports Request/Response via Transport and Publishing via the Broker.
// It also supports bidirectional streaming of requests. // It also supports bidirectional streaming of requests.
type Client interface { type Client interface {
Init(...Option) error Name() string
Init(opts ...options.Option) error
Options() Options Options() Options
NewMessage(topic string, msg interface{}, opts ...MessageOption) Message NewRequest(service string, endpoint string, req interface{}, opts ...options.Option) Request
NewRequest(service, endpoint string, req interface{}, reqOpts ...RequestOption) Request Call(ctx context.Context, req Request, rsp interface{}, opts ...options.Option) error
Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error Stream(ctx context.Context, req Request, opts ...options.Option) (Stream, error)
Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error)
Publish(ctx context.Context, msg Message, opts ...PublishOption) error
String() string String() string
} }
// Message is the interface for publishing asynchronously
type Message interface {
Topic() string
Payload() interface{}
ContentType() string
}
// Request is the interface for a synchronous request used by Call or Stream // Request is the interface for a synchronous request used by Call or Stream
type Request interface { type Request interface {
// The service to call // The service to call
@ -51,16 +59,22 @@ type Request interface {
Codec() codec.Codec Codec() codec.Codec
// indicates whether the request will be a streaming one rather than unary // indicates whether the request will be a streaming one rather than unary
Stream() bool Stream() bool
// Header data
// Header() metadata.Metadata
} }
// Response is the response received from a service // Response is the response received from a service
type Response interface { type Response interface {
// Read the response // Read the response
Codec() codec.Codec Codec() codec.Codec
// read the header // The content type
Header() metadata.Metadata // ContentType() string
// Header data
// Header() metadata.Metadata
// Read the undecoded response // Read the undecoded response
Read() ([]byte, error) Read() ([]byte, error)
// The unencoded request body
// Body() interface{}
} }
// Stream is the interface for a bidirectional synchronous stream // Stream is the interface for a bidirectional synchronous stream
@ -72,41 +86,17 @@ type Stream interface {
// The response read // The response read
Response() Response Response() Response
// Send will encode and send a request // Send will encode and send a request
Send(interface{}) error Send(msg interface{}) error
// Recv will decode and read a response // Recv will decode and read a response
Recv(interface{}) error Recv(msg interface{}) error
// SendMsg will encode and send a request
SendMsg(msg interface{}) error
// RecvMsg will decode and read a response
RecvMsg(msg interface{}) error
// Error returns the stream error // Error returns the stream error
Error() error Error() error
// Close closes the stream // Close closes the stream
Close() error Close() error
// CloseSend closes the send direction of the stream
CloseSend() error
} }
// Option used by the Client
type Option func(*Options)
// CallOption used by Call or Stream
type CallOption func(*CallOptions)
// PublishOption used by Publish
type PublishOption func(*PublishOptions)
// MessageOption used by NewMessage
type MessageOption func(*MessageOptions)
// RequestOption used by NewRequest
type RequestOption func(*RequestOptions)
var (
// DefaultBackoff is the default backoff function for retries
DefaultBackoff = exponentialBackoff
// DefaultRetry is the default check-for-retry function for retries
DefaultRetry = RetryNever
// DefaultRetries is the default number of times a request is tried
DefaultRetries = 0
// DefaultRequestTimeout is the default request timeout
DefaultRequestTimeout = time.Second * 5
// DefaultPoolSize sets the connection pool size
DefaultPoolSize = 100
// DefaultPoolTTL sets the connection pool ttl
DefaultPoolTTL = time.Minute
)

View File

@ -2,22 +2,24 @@ package client
import ( import (
"context" "context"
"go.unistack.org/micro/v4/options"
) )
type clientCallOptions struct { type clientCallOptions struct {
Client Client
opts []CallOption opts []options.Option
} }
func (s *clientCallOptions) Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error { func (s *clientCallOptions) Call(ctx context.Context, req Request, rsp interface{}, opts ...options.Option) error {
return s.Client.Call(ctx, req, rsp, append(s.opts, opts...)...) return s.Client.Call(ctx, req, rsp, append(s.opts, opts...)...)
} }
func (s *clientCallOptions) Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) { func (s *clientCallOptions) Stream(ctx context.Context, req Request, opts ...options.Option) (Stream, error) {
return s.Client.Stream(ctx, req, append(s.opts, opts...)...) return s.Client.Stream(ctx, req, append(s.opts, opts...)...)
} }
// NewClientCallOptions add CallOption to every call // NewClientCallOptions add CallOption to every call
func NewClientCallOptions(c Client, opts ...CallOption) Client { func NewClientCallOptions(c Client, opts ...options.Option) Client {
return &clientCallOptions{c, opts} return &clientCallOptions{c, opts}
} }

View File

@ -0,0 +1,28 @@
package client
import (
"context"
"testing"
"time"
"go.unistack.org/micro/v4/options"
)
func TestNewClientCallOptions(t *testing.T) {
var flag bool
w := func(fn CallFunc) CallFunc {
flag = true
return fn
}
c := NewClientCallOptions(NewClient(),
options.Address("127.0.0.1"),
WithCallWrapper(w),
RequestTimeout(1*time.Millisecond),
Retries(0),
Backoff(BackoffInterval(10*time.Millisecond, 100*time.Millisecond)),
)
_ = c.Call(context.TODO(), c.NewRequest("service", "endpoint", nil), nil)
if !flag {
t.Fatalf("NewClientCallOptions not works")
}
}

View File

@ -22,33 +22,3 @@ func NewContext(ctx context.Context, c Client) context.Context {
} }
return context.WithValue(ctx, clientKey{}, c) return context.WithValue(ctx, clientKey{}, c)
} }
// SetPublishOption returns a function to setup a context with given value
func SetPublishOption(k, v interface{}) PublishOption {
return func(o *PublishOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}
// SetCallOption returns a function to setup a context with given value
func SetCallOption(k, v interface{}) CallOption {
return func(o *CallOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}
// SetOption returns a function to setup a context with given value
func SetOption(k, v interface{}) Option {
return func(o *Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}

40
client/context_test.go Normal file
View File

@ -0,0 +1,40 @@
package client
import (
"context"
"testing"
)
func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), clientKey{}, NewClient())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("FromContext not works")
}
}
func TestFromNilContext(t *testing.T) {
// nolint: staticcheck
c, ok := FromContext(nil)
if ok || c != nil {
t.Fatal("FromContext not works")
}
}
func TestNewContext(t *testing.T) {
ctx := NewContext(context.TODO(), NewClient())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestNewNilContext(t *testing.T) {
// nolint: staticcheck
ctx := NewContext(nil, NewClient())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}

View File

@ -4,15 +4,15 @@ import (
"context" "context"
"sort" "sort"
"github.com/unistack-org/micro/v3/errors" "go.unistack.org/micro/v4/errors"
"github.com/unistack-org/micro/v3/router" "go.unistack.org/micro/v4/router"
) )
// LookupFunc is used to lookup routes for a service // LookupFunc is used to lookup routes for a service
type LookupFunc func(context.Context, Request, CallOptions) ([]string, error) type LookupFunc func(context.Context, Request, CallOptions) ([]string, error)
// LookupRoute for a request using the router and then choose one using the selector // LookupRoute for a request using the router and then choose one using the selector
func LookupRoute(ctx context.Context, req Request, opts CallOptions) ([]string, error) { func LookupRoute(_ context.Context, req Request, opts CallOptions) ([]string, error) {
// check to see if an address was provided as a call option // check to see if an address was provided as a call option
if len(opts.Address) > 0 { if len(opts.Address) > 0 {
return opts.Address, nil return opts.Address, nil

View File

@ -2,51 +2,48 @@ package client
import ( import (
"context" "context"
"fmt"
"time"
"github.com/unistack-org/micro/v3/broker" "go.unistack.org/micro/v4/codec"
"github.com/unistack-org/micro/v3/codec" "go.unistack.org/micro/v4/errors"
"github.com/unistack-org/micro/v3/errors" "go.unistack.org/micro/v4/metadata"
"github.com/unistack-org/micro/v3/metadata" "go.unistack.org/micro/v4/options"
"go.unistack.org/micro/v4/selector"
"go.unistack.org/micro/v4/semconv"
) )
var ( // DefaultCodecs will be used to encode/decode data
// DefaultCodecs will be used to encode/decode data var DefaultCodecs = map[string]codec.Codec{
DefaultCodecs = map[string]codec.Codec{ "application/octet-stream": codec.NewCodec(),
//"application/json": cjson.NewCodec, }
//"application/json-rpc": cjsonrpc.NewCodec,
//"application/protobuf": cproto.NewCodec,
//"application/proto-rpc": cprotorpc.NewCodec,
"application/octet-stream": codec.NewCodec(),
}
)
const (
defaultContentType = "application/json"
)
type noopClient struct { type noopClient struct {
opts Options opts Options
} }
type noopMessage struct {
topic string
payload interface{}
opts MessageOptions
}
type noopRequest struct { type noopRequest struct {
body interface{}
codec codec.Codec
service string service string
method string method string
endpoint string endpoint string
contentType string contentType string
body interface{}
codec codec.Codec
stream bool stream bool
} }
// NewClient returns new noop client // NewClient returns new noop client
func NewClient(opts ...Option) Client { func NewClient(opts ...options.Option) Client {
return &noopClient{opts: NewOptions(opts...)} nc := &noopClient{opts: NewOptions(opts...)}
// wrap in reverse
c := Client(nc)
return c
}
func (n *noopClient) Name() string {
return n.opts.Name
} }
func (n *noopRequest) Service() string { func (n *noopRequest) Service() string {
@ -116,6 +113,14 @@ func (n *noopStream) Recv(interface{}) error {
return nil return nil
} }
func (n *noopStream) SendMsg(interface{}) error {
return nil
}
func (n *noopStream) RecvMsg(interface{}) error {
return nil
}
func (n *noopStream) Error() error { func (n *noopStream) Error() error {
return nil return nil
} }
@ -124,16 +129,8 @@ func (n *noopStream) Close() error {
return nil return nil
} }
func (n *noopMessage) Topic() string { func (n *noopStream) CloseSend() error {
return n.topic return nil
}
func (n *noopMessage) Payload() interface{} {
return n.payload
}
func (n *noopMessage) ContentType() string {
return n.opts.ContentType
} }
func (n *noopClient) newCodec(contentType string) (codec.Codec, error) { func (n *noopClient) newCodec(contentType string) (codec.Codec, error) {
@ -146,7 +143,7 @@ func (n *noopClient) newCodec(contentType string) (codec.Codec, error) {
return nil, codec.ErrUnknownContentType return nil, codec.ErrUnknownContentType
} }
func (n *noopClient) Init(opts ...Option) error { func (n *noopClient) Init(opts ...options.Option) error {
for _, o := range opts { for _, o := range opts {
o(&n.opts) o(&n.opts)
} }
@ -161,62 +158,307 @@ func (n *noopClient) String() string {
return "noop" return "noop"
} }
func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error { func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opts ...options.Option) error {
// make a copy of call opts
callOpts := n.opts.CallOptions
for _, opt := range opts {
opt(&callOpts)
}
// check if we already have a deadline
d, ok := ctx.Deadline()
if !ok {
var cancel context.CancelFunc
// no deadline so we create a new one
ctx, cancel = context.WithTimeout(ctx, callOpts.RequestTimeout)
defer cancel()
} else {
// got a deadline so no need to setup context
// but we need to set the timeout we pass along
opt := RequestTimeout(time.Until(d))
opt(&callOpts)
}
// should we noop right here?
select {
case <-ctx.Done():
return errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408)
default:
}
// make copy of call method
hcall := n.call
// wrap the call in reverse
for i := len(callOpts.CallWrappers); i > 0; i-- {
hcall = callOpts.CallWrappers[i-1](hcall)
}
// use the router passed as a call option, or fallback to the rpc clients router
if callOpts.Router == nil {
callOpts.Router = n.opts.Router
}
if callOpts.Selector == nil {
callOpts.Selector = n.opts.Selector
}
// inject proxy address
// TODO: don't even bother using Lookup/Select in this case
if len(n.opts.Proxy) > 0 {
callOpts.Address = []string{n.opts.Proxy}
}
var next selector.Next
// return errors.New("go.micro.client", "request timeout", 408)
call := func(i int) error {
// call backoff first. Someone may want an initial start delay
t, err := callOpts.Backoff(ctx, req, i)
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
// only sleep if greater than 0
if t.Seconds() > 0 {
time.Sleep(t)
}
if next == nil {
var routes []string
// lookup the route to send the reques to
// TODO apply any filtering here
routes, err = n.opts.Lookup(ctx, req, callOpts)
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
// balance the list of nodes
next, err = callOpts.Selector.Select(routes)
if err != nil {
return err
}
}
node := next()
// make the call
err = hcall(ctx, node, req, rsp, callOpts)
// record the result of the call to inform future routing decisions
if verr := n.opts.Selector.Record(node, err); verr != nil {
return verr
}
// try and transform the error to a go-micro error
if verr, ok := err.(*errors.Error); ok {
return verr
}
return err
}
ch := make(chan error, callOpts.Retries)
var gerr error
ts := time.Now()
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
n.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", endpoint).Inc()
for i := 0; i <= callOpts.Retries; i++ {
go func() {
ch <- call(i)
}()
select {
case <-ctx.Done():
return errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408)
case err := <-ch:
// if the call succeeded lets bail early
if err == nil {
return nil
}
retry, rerr := callOpts.Retry(ctx, req, i, err)
if rerr != nil {
return rerr
}
if !retry {
return err
}
gerr = err
}
}
if gerr != nil {
n.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", endpoint, "status", "failure").Inc()
} else {
n.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", endpoint, "status", "success").Inc()
}
n.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", endpoint).Dec()
te := time.Since(ts)
n.opts.Meter.Summary(semconv.ClientRequestLatencyMicroseconds, "endpoint", endpoint).Update(te.Seconds())
n.opts.Meter.Histogram(semconv.ClientRequestDurationSeconds, "endpoint", endpoint).Update(te.Seconds())
return gerr
}
func (n *noopClient) call(ctx context.Context, addr string, req Request, rsp interface{}, opts CallOptions) error {
return nil return nil
} }
func (n *noopClient) NewRequest(service, endpoint string, req interface{}, opts ...RequestOption) Request { func (n *noopClient) NewRequest(service, endpoint string, req interface{}, opts ...options.Option) Request {
return &noopRequest{} return &noopRequest{service: service, endpoint: endpoint}
} }
func (n *noopClient) NewMessage(topic string, msg interface{}, opts ...MessageOption) Message { func (n *noopClient) Stream(ctx context.Context, req Request, opts ...options.Option) (Stream, error) {
options := NewMessageOptions(opts...) var err error
return &noopMessage{topic: topic, payload: msg, opts: options}
// make a copy of call opts
callOpts := n.opts.CallOptions
for _, o := range opts {
o(&callOpts)
}
// check if we already have a deadline
d, ok := ctx.Deadline()
if !ok && callOpts.StreamTimeout > time.Duration(0) {
var cancel context.CancelFunc
// no deadline so we create a new one
ctx, cancel = context.WithTimeout(ctx, callOpts.StreamTimeout)
defer cancel()
} else {
// got a deadline so no need to setup context
// but we need to set the timeout we pass along
o := StreamTimeout(time.Until(d))
o(&callOpts)
}
// should we noop right here?
select {
case <-ctx.Done():
return nil, errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408)
default:
}
/*
// make copy of call method
hstream := h.stream
// wrap the call in reverse
for i := len(callOpts.CallWrappers); i > 0; i-- {
hstream = callOpts.CallWrappers[i-1](hstream)
}
*/
// use the router passed as a call option, or fallback to the rpc clients router
if callOpts.Router == nil {
callOpts.Router = n.opts.Router
}
if callOpts.Selector == nil {
callOpts.Selector = n.opts.Selector
}
// inject proxy address
// TODO: don't even bother using Lookup/Select in this case
if len(n.opts.Proxy) > 0 {
callOpts.Address = []string{n.opts.Proxy}
}
var next selector.Next
call := func(i int) (Stream, error) {
// call backoff first. Someone may want an initial start delay
t, cerr := callOpts.Backoff(ctx, req, i)
if cerr != nil {
return nil, errors.InternalServerError("go.micro.client", cerr.Error())
}
// only sleep if greater than 0
if t.Seconds() > 0 {
time.Sleep(t)
}
if next == nil {
var routes []string
// lookup the route to send the reques to
// TODO apply any filtering here
routes, err = n.opts.Lookup(ctx, req, callOpts)
if err != nil {
return nil, errors.InternalServerError("go.micro.client", err.Error())
}
// balance the list of nodes
next, err = callOpts.Selector.Select(routes)
if err != nil {
return nil, err
}
}
node := next()
// ts := time.Now()
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
n.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", endpoint).Inc()
stream, cerr := n.stream(ctx, node, req, callOpts)
if cerr != nil {
n.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", endpoint, "status", "failure").Inc()
} else {
n.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", endpoint, "status", "success").Inc()
}
// record the result of the call to inform future routing decisions
if verr := n.opts.Selector.Record(node, cerr); verr != nil {
return nil, verr
}
// try and transform the error to a go-micro error
if verr, ok := cerr.(*errors.Error); ok {
return nil, verr
}
return stream, cerr
}
type response struct {
stream Stream
err error
}
ch := make(chan response, callOpts.Retries)
var grr error
for i := 0; i <= callOpts.Retries; i++ {
go func() {
s, cerr := call(i)
ch <- response{s, cerr}
}()
select {
case <-ctx.Done():
return nil, errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408)
case rsp := <-ch:
// if the call succeeded lets bail early
if rsp.err == nil {
return rsp.stream, nil
}
retry, rerr := callOpts.Retry(ctx, req, i, err)
if rerr != nil {
return nil, rerr
}
if !retry {
return nil, rsp.err
}
grr = rsp.err
}
}
return nil, grr
} }
func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) { func (n *noopClient) stream(ctx context.Context, addr string, req Request, opts CallOptions) (*noopStream, error) {
return &noopStream{}, nil return &noopStream{}, nil
} }
func (n *noopClient) Publish(ctx context.Context, p Message, opts ...PublishOption) error {
var body []byte
options := NewPublishOptions(opts...)
md, ok := metadata.FromContext(ctx)
if !ok {
md = metadata.New(0)
}
md["Content-Type"] = p.ContentType()
md["Micro-Topic"] = p.Topic()
// passed in raw data
if d, ok := p.Payload().(*codec.Frame); ok {
body = d.Data
} else {
// use codec for payload
cf, err := n.newCodec(p.ContentType())
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
// set the body
b, err := cf.Marshal(p.Payload())
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error())
}
body = b
}
topic := p.Topic()
// get the exchange
if len(options.Exchange) > 0 {
topic = options.Exchange
}
return n.opts.Broker.Publish(ctx, topic, &broker.Message{
Header: md,
Body: body,
}, broker.PublishContext(options.Context))
}

View File

@ -2,54 +2,61 @@ package client
import ( import (
"context" "context"
"crypto/tls"
"net"
"time" "time"
"github.com/unistack-org/micro/v3/broker" "go.unistack.org/micro/v4/codec"
"github.com/unistack-org/micro/v3/codec" "go.unistack.org/micro/v4/logger"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v4/metadata"
"github.com/unistack-org/micro/v3/meter" "go.unistack.org/micro/v4/meter"
"github.com/unistack-org/micro/v3/network/transport" "go.unistack.org/micro/v4/options"
"github.com/unistack-org/micro/v3/registry" "go.unistack.org/micro/v4/router"
"github.com/unistack-org/micro/v3/router" "go.unistack.org/micro/v4/selector"
"github.com/unistack-org/micro/v3/selector" "go.unistack.org/micro/v4/selector/random"
"github.com/unistack-org/micro/v3/selector/random" "go.unistack.org/micro/v4/tracer"
"github.com/unistack-org/micro/v3/tracer"
) )
// Options holds client options // Options holds client options
type Options struct { type Options struct {
// Used to select codec // Selector used to select needed address
ContentType string Selector selector.Selector
// Proxy address to send requests via // Logger used to log messages
Proxy string Logger logger.Logger
// Tracer used for tracing
// Plugged interfaces Tracer tracer.Tracer
Broker broker.Broker // Meter used for metrics
Codecs map[string]codec.Codec Meter meter.Meter
Router router.Router // Context is used for external options
Selector selector.Selector
Transport transport.Transport
Logger logger.Logger
Meter meter.Meter
// Lookup used for looking up routes
Lookup LookupFunc
// Connection Pool
PoolSize int
PoolTTL time.Duration
Tracer tracer.Tracer
// Wrapper that used client
Wrappers []Wrapper
// CallOptions that used by default
CallOptions CallOptions
// Context is used for non default options
Context context.Context Context context.Context
// Router used to get route
Router router.Router
// TLSConfig specifies tls.Config for secure connection
TLSConfig *tls.Config
// Codecs map
Codecs map[string]codec.Codec
// Lookup func used to get destination addr
Lookup LookupFunc
// Proxy is used for proxy requests
Proxy string
// ContentType is used to select codec
ContentType string
// Name is the client name
Name string
// CallOptions contains default CallOptions
CallOptions CallOptions
// PoolSize connection pool size
PoolSize int
// PoolTTL connection pool ttl
PoolTTL time.Duration
// ContextDialer used to connect
ContextDialer func(context.Context, string) (net.Conn, error)
// Hooks may contains Client func wrapper
Hooks options.Hooks
} }
// NewCallOptions creates new call options struct // NewCallOptions creates new call options struct
func NewCallOptions(opts ...CallOption) CallOptions { func NewCallOptions(opts ...options.Option) CallOptions {
options := CallOptions{} options := CallOptions{}
for _, o := range opts { for _, o := range opts {
o(&options) o(&options)
@ -59,77 +66,53 @@ func NewCallOptions(opts ...CallOption) CallOptions {
// CallOptions holds client call options // CallOptions holds client call options
type CallOptions struct { type CallOptions struct {
// Address of remote hosts // Selector selects addr
Address []string
// Backoff func
Backoff BackoffFunc
// Transport Dial Timeout
DialTimeout time.Duration
// Number of Call attempts
Retries int
// Check if retriable func
Retry RetryFunc
// Request/Response timeout
RequestTimeout time.Duration
// Router to use for this call
Router router.Router
// Selector to use for the call
Selector selector.Selector Selector selector.Selector
// SelectOptions to use when selecting a route // Context used for deadline
SelectOptions []selector.SelectOption Context context.Context
// Stream timeout for the stream // Router used for route
StreamTimeout time.Duration Router router.Router
// Use the auth token as the authorization header // Retry func used for retries
AuthToken bool Retry RetryFunc
// Network to lookup the route within // Backoff func used for backoff when retry
Backoff BackoffFunc
// Network name
Network string Network string
// Middleware for low level call func // Content-Type
CallWrappers []CallWrapper
// Context is uded for non default options
Context context.Context
}
// Context pass context to client
func Context(ctx context.Context) Option {
return func(o *Options) {
o.Context = ctx
}
}
// NewPublishOptions create new PublishOptions struct from option
func NewPublishOptions(opts ...PublishOption) PublishOptions {
options := PublishOptions{}
for _, o := range opts {
o(&options)
}
return options
}
// PublishOptions holds publish options
type PublishOptions struct {
// Exchange is the routing exchange for the message
Exchange string
// Other options for implementations of the interface
// can be stored in a context
Context context.Context
}
// NewMessageOptions creates message options struct
func NewMessageOptions(opts ...MessageOption) MessageOptions {
options := MessageOptions{}
for _, o := range opts {
o(&options)
}
return options
}
// MessageOptions holds client message options
type MessageOptions struct {
ContentType string ContentType string
// AuthToken string
AuthToken string
// Address specifies static addr list
Address []string
// SelectOptions selector options
SelectOptions []selector.SelectOption
// CallWrappers call wrappers
CallWrappers []CallWrapper
// StreamTimeout stream timeout
StreamTimeout time.Duration
// RequestTimeout request timeout
RequestTimeout time.Duration
// RequestMetadata holds additional metadata for call
RequestMetadata metadata.Metadata
// ResponseMetadata holds additional metadata from call
ResponseMetadata *metadata.Metadata
// DialTimeout dial timeout
DialTimeout time.Duration
// Retries specifies retries num
Retries int
// ContextDialer used to connect
ContextDialer func(context.Context, string) (net.Conn, error)
}
// ContextDialer pass ContextDialer to client
func ContextDialer(fn func(context.Context, string) (net.Conn, error)) options.Option {
return func(src interface{}) error {
return options.Set(src, fn, ".ContextDialer")
}
} }
// NewRequestOptions creates new RequestOptions struct // NewRequestOptions creates new RequestOptions struct
func NewRequestOptions(opts ...RequestOption) RequestOptions { func NewRequestOptions(opts ...options.Option) RequestOptions {
options := RequestOptions{} options := RequestOptions{}
for _, o := range opts { for _, o := range opts {
o(&options) o(&options)
@ -139,34 +122,36 @@ func NewRequestOptions(opts ...RequestOption) RequestOptions {
// RequestOptions holds client request options // RequestOptions holds client request options
type RequestOptions struct { type RequestOptions struct {
ContentType string // Context used for external options
Stream bool
// Other options for implementations of the interface
// can be stored in a context
Context context.Context Context context.Context
// ContentType specify content-type of message
ContentType string
// Stream flag
Stream bool
} }
// NewOptions creates new options struct // NewOptions creates new options struct
func NewOptions(opts ...Option) Options { func NewOptions(opts ...options.Option) Options {
options := Options{ options := Options{
Context: context.Background(), Context: context.Background(),
ContentType: "application/json", ContentType: DefaultContentType,
Codecs: make(map[string]codec.Codec), Codecs: make(map[string]codec.Codec),
CallOptions: CallOptions{ CallOptions: CallOptions{
Context: context.Background(),
Backoff: DefaultBackoff, Backoff: DefaultBackoff,
Retry: DefaultRetry, Retry: DefaultRetry,
Retries: DefaultRetries, Retries: DefaultRetries,
RequestTimeout: DefaultRequestTimeout, RequestTimeout: DefaultRequestTimeout,
DialTimeout: transport.DefaultDialTimeout, DialTimeout: DefaultDialTimeout,
}, },
Lookup: LookupRoute, Lookup: LookupRoute,
PoolSize: DefaultPoolSize, PoolSize: DefaultPoolSize,
PoolTTL: DefaultPoolTTL, PoolTTL: DefaultPoolTTL,
Selector: random.NewSelector(), Selector: random.NewSelector(),
Logger: logger.DefaultLogger, Logger: logger.DefaultLogger,
Broker: broker.DefaultBroker,
Meter: meter.DefaultMeter, Meter: meter.DefaultMeter,
Tracer: tracer.DefaultTracer, Tracer: tracer.DefaultTracer,
Router: router.DefaultRouter,
} }
for _, o := range opts { for _, o := range opts {
@ -176,297 +161,131 @@ func NewOptions(opts ...Option) Options {
return options return options
} }
// Broker to be used for pub/sub
func Broker(b broker.Broker) Option {
return func(o *Options) {
o.Broker = b
}
}
// Tracer to be used for tracing
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}
// Logger to be used for log mesages
func Logger(l logger.Logger) Option {
return func(o *Options) {
o.Logger = l
}
}
// Meter to be used for metrics
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
// Codec to be used to encode/decode requests for a given content type
func Codec(contentType string, c codec.Codec) Option {
return func(o *Options) {
o.Codecs[contentType] = c
}
}
// Default content type of the client
func ContentType(ct string) Option {
return func(o *Options) {
o.ContentType = ct
}
}
// Proxy sets the proxy address // Proxy sets the proxy address
func Proxy(addr string) Option { func Proxy(addr string) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.Proxy = addr return options.Set(src, addr, ".Proxy")
} }
} }
// PoolSize sets the connection pool size // PoolSize sets the connection pool size
func PoolSize(d int) Option { func PoolSize(d int) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.PoolSize = d return options.Set(src, d, ".PoolSize")
} }
} }
// PoolTTL sets the connection pool ttl // PoolTTL sets the connection pool ttl
func PoolTTL(d time.Duration) Option { func PoolTTL(td time.Duration) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.PoolTTL = d return options.Set(src, td, ".PoolTTL")
}
}
// Transport to use for communication e.g http, rabbitmq, etc
func Transport(t transport.Transport) Option {
return func(o *Options) {
o.Transport = t
}
}
// Registry sets the routers registry
func Registry(r registry.Registry) Option {
return func(o *Options) {
if o.Router != nil {
o.Router.Init(router.Registry(r))
}
}
}
// Router is used to lookup routes for a service
func Router(r router.Router) Option {
return func(o *Options) {
o.Router = r
} }
} }
// Selector is used to select a route // Selector is used to select a route
func Selector(s selector.Selector) Option { func Selector(s selector.Selector) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.Selector = s return options.Set(src, s, ".Selector")
} }
} }
// Adds a Wrapper to a list of options passed into the client // Backoff is used to set the backoff function used when retrying Calls
func Wrap(w Wrapper) Option { func Backoff(fn BackoffFunc) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.Wrappers = append(o.Wrappers, w) return options.Set(src, fn, ".Backoff")
}
}
// Adds a Wrapper to the list of CallFunc wrappers
func WrapCall(cw ...CallWrapper) Option {
return func(o *Options) {
o.CallOptions.CallWrappers = append(o.CallOptions.CallWrappers, cw...)
}
}
// Backoff is used to set the backoff function used
// when retrying Calls
func Backoff(fn BackoffFunc) Option {
return func(o *Options) {
o.CallOptions.Backoff = fn
} }
} }
// Lookup sets the lookup function to use for resolving service names // Lookup sets the lookup function to use for resolving service names
func Lookup(l LookupFunc) Option { func Lookup(fn LookupFunc) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.Lookup = l return options.Set(src, fn, ".Lookup")
}
}
// WithCallWrapper sets the retry function to be used when re-trying.
func WithCallWrapper(fn CallWrapper) options.Option {
return func(src interface{}) error {
return options.Set(src, fn, ".CallWrappers")
} }
} }
// Retries sets the retry count when making the request. // Retries sets the retry count when making the request.
// Should this be a Call Option? func Retries(n int) options.Option {
func Retries(i int) Option { return func(src interface{}) error {
return func(o *Options) { return options.Set(src, n, ".Retries")
o.CallOptions.Retries = i
} }
} }
// Retry sets the retry function to be used when re-trying. // Retry sets the retry function to be used when re-trying.
func Retry(fn RetryFunc) Option { func Retry(fn RetryFunc) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.CallOptions.Retry = fn return options.Set(src, fn, ".Retry")
} }
} }
// RequestTimeout is the request timeout. // RequestTimeout is the request timeout.
// Should this be a Call Option? func RequestTimeout(td time.Duration) options.Option {
func RequestTimeout(d time.Duration) Option { return func(src interface{}) error {
return func(o *Options) { return options.Set(src, td, ".RequestTimeout")
o.CallOptions.RequestTimeout = d
} }
} }
// StreamTimeout sets the stream timeout // StreamTimeout sets the stream timeout
func StreamTimeout(d time.Duration) Option { func StreamTimeout(td time.Duration) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.CallOptions.StreamTimeout = d return options.Set(src, td, ".StreamTimeout")
} }
} }
// Transport dial timeout // DialTimeout sets the dial timeout
func DialTimeout(d time.Duration) Option { func DialTimeout(td time.Duration) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.CallOptions.DialTimeout = d return options.Set(src, td, ".DialTimeout")
} }
} }
// Call Options // WithResponseMetadata is a CallOption which adds metadata.Metadata to Options.CallOptions
func ResponseMetadata(md *metadata.Metadata) options.Option {
// WithExchange sets the exchange to route a message through return func(src interface{}) error {
func WithExchange(e string) PublishOption { return options.Set(src, md, ".ResponseMetadata")
return func(o *PublishOptions) {
o.Exchange = e
} }
} }
// PublishContext sets the context in publish options // WithRequestMetadata is a CallOption which adds metadata.Metadata to Options.CallOptions
func PublishContext(ctx context.Context) PublishOption { func RequestMetadata(md metadata.Metadata) options.Option {
return func(o *PublishOptions) { return func(src interface{}) error {
o.Context = ctx return options.Set(src, metadata.Copy(md), ".RequestMetadata")
} }
} }
// WithAddress sets the remote addresses to use rather than using service discovery // AuthToken is a CallOption which overrides the
func WithAddress(a ...string) CallOption {
return func(o *CallOptions) {
o.Address = a
}
}
// WithCallWrapper is a CallOption which adds to the existing CallFunc wrappers
func WithCallWrapper(cw ...CallWrapper) CallOption {
return func(o *CallOptions) {
o.CallWrappers = append(o.CallWrappers, cw...)
}
}
// WithBackoff is a CallOption which overrides that which
// set in Options.CallOptions
func WithBackoff(fn BackoffFunc) CallOption {
return func(o *CallOptions) {
o.Backoff = fn
}
}
// WithRetry is a CallOption which overrides that which
// set in Options.CallOptions
func WithRetry(fn RetryFunc) CallOption {
return func(o *CallOptions) {
o.Retry = fn
}
}
// WithRetries is a CallOption which overrides that which
// set in Options.CallOptions
func WithRetries(i int) CallOption {
return func(o *CallOptions) {
o.Retries = i
}
}
// WithRequestTimeout is a CallOption which overrides that which
// set in Options.CallOptions
func WithRequestTimeout(d time.Duration) CallOption {
return func(o *CallOptions) {
o.RequestTimeout = d
}
}
// WithStreamTimeout sets the stream timeout
func WithStreamTimeout(d time.Duration) CallOption {
return func(o *CallOptions) {
o.StreamTimeout = d
}
}
// WithDialTimeout is a CallOption which overrides that which
// set in Options.CallOptions
func WithDialTimeout(d time.Duration) CallOption {
return func(o *CallOptions) {
o.DialTimeout = d
}
}
// WithAuthToken is a CallOption which overrides the
// authorization header with the services own auth token // authorization header with the services own auth token
func WithAuthToken() CallOption { func AuthToken(t string) options.Option {
return func(o *CallOptions) { return func(src interface{}) error {
o.AuthToken = true return options.Set(src, t, ".AuthToken")
} }
} }
// WithNetwork is a CallOption which sets the network attribute // Network is a CallOption which sets the network attribute
func WithNetwork(n string) CallOption { func Network(n string) options.Option {
return func(o *CallOptions) { return func(src interface{}) error {
o.Network = n return options.Set(src, n, ".Network")
}
}
// WithRouter sets the router to use for this call
func WithRouter(r router.Router) CallOption {
return func(o *CallOptions) {
o.Router = r
}
}
// WithSelector sets the selector to use for this call
func WithSelector(s selector.Selector) CallOption {
return func(o *CallOptions) {
o.Selector = s
} }
} }
/*
// WithSelectOptions sets the options to pass to the selector for this call // WithSelectOptions sets the options to pass to the selector for this call
func WithSelectOptions(sops ...selector.SelectOption) CallOption { func WithSelectOptions(sops ...selector.SelectOption) options.Option {
return func(o *CallOptions) { return func(o *CallOptions) {
o.SelectOptions = sops o.SelectOptions = sops
} }
} }
*/
// WithMessageContentType sets the message content type
func WithMessageContentType(ct string) MessageOption {
return func(o *MessageOptions) {
o.ContentType = ct
}
}
// Request Options
// WithContentType specifies request content type
func WithContentType(ct string) RequestOption {
return func(o *RequestOptions) {
o.ContentType = ct
}
}
// StreamingRequest specifies that request is streaming // StreamingRequest specifies that request is streaming
func StreamingRequest() RequestOption { func StreamingRequest(b bool) options.Option {
return func(o *RequestOptions) { return func(src interface{}) error {
o.Stream = true return options.Set(src, b, ".Stream")
} }
} }

View File

@ -3,7 +3,7 @@ package client
import ( import (
"context" "context"
"github.com/unistack-org/micro/v3/errors" "go.unistack.org/micro/v4/errors"
) )
// RetryFunc that returning either false or a non-nil error will result in the call not being retried // RetryFunc that returning either false or a non-nil error will result in the call not being retried
@ -19,18 +19,32 @@ func RetryNever(ctx context.Context, req Request, retryCount int, err error) (bo
return false, nil return false, nil
} }
// RetryOnError retries a request on a 500 or timeout error // RetryOnError retries a request on a 500 or 408 (timeout) error
func RetryOnError(ctx context.Context, req Request, retryCount int, err error) (bool, error) { func RetryOnError(_ context.Context, _ Request, _ int, err error) (bool, error) {
if err == nil { if err == nil {
return false, nil return false, nil
} }
me := errors.FromError(err) me := errors.FromError(err)
switch me.Code { switch me.Code {
// retry on timeout or internal server error // retry on timeout or internal server error
case 408, 500: case 408, 500:
return true, nil return true, nil
} }
return false, nil return false, nil
} }
// RetryOnErrors retries a request on specified error codes
func RetryOnErrors(codes ...int32) RetryFunc {
return func(_ context.Context, _ Request, _ int, err error) (bool, error) {
if err == nil {
return false, nil
}
me := errors.FromError(err)
for _, code := range codes {
if me.Code == code {
return true, nil
}
}
return false, nil
}
}

70
client/retry_test.go Normal file
View File

@ -0,0 +1,70 @@
package client
import (
"context"
"fmt"
"testing"
"go.unistack.org/micro/v4/errors"
)
func TestRetryAlways(t *testing.T) {
tests := []error{
nil,
errors.InternalServerError("test", "%s", "test"),
fmt.Errorf("test"),
}
for _, e := range tests {
ok, er := RetryAlways(context.TODO(), nil, 1, e)
if !ok || er != nil {
t.Fatal("RetryAlways not works properly")
}
}
}
func TestRetryNever(t *testing.T) {
tests := []error{
nil,
errors.InternalServerError("test", "%s", "test"),
fmt.Errorf("test"),
}
for _, e := range tests {
ok, er := RetryNever(context.TODO(), nil, 1, e)
if ok || er != nil {
t.Fatal("RetryNever not works properly")
}
}
}
func TestRetryOnError(t *testing.T) {
tests := []error{
fmt.Errorf("test"),
errors.NotFound("test", "%s", "test"),
errors.Timeout("test", "%s", "test"),
}
for i, e := range tests {
ok, er := RetryOnError(context.TODO(), nil, 1, e)
if i == 2 && (!ok || er != nil) {
t.Fatal("RetryOnError not works properly")
}
}
}
func TestRetryOnErrors(t *testing.T) {
tests := []error{
fmt.Errorf("test"),
errors.NotFound("test", "%s", "test"),
errors.Timeout("test", "%s", "test"),
}
fn := RetryOnErrors(404)
for i, e := range tests {
ok, er := fn(context.TODO(), nil, 1, e)
if i == 1 && (!ok || er != nil) {
t.Fatal("RetryOnErrors not works properly")
}
}
}

View File

@ -1,16 +1,16 @@
package client package client
import ( import (
"github.com/unistack-org/micro/v3/codec" "go.unistack.org/micro/v4/codec"
) )
type testRequest struct { type testRequest struct {
codec codec.Codec
body interface{}
service string service string
method string method string
endpoint string endpoint string
contentType string contentType string
codec codec.Codec
body interface{}
opts RequestOptions opts RequestOptions
} }

View File

@ -1,15 +1,15 @@
// Package codec is an interface for encoding messages // Package codec is an interface for encoding messages
package codec package codec // import "go.unistack.org/micro/v4/codec"
import ( import (
"errors" "errors"
"io" "io"
"github.com/unistack-org/micro/v3/metadata" "go.unistack.org/micro/v4/metadata"
) )
// Message types
const ( const (
// Message types
Error MessageType = iota Error MessageType = iota
Request Request
Response Response
@ -25,11 +25,14 @@ var (
var ( var (
// DefaultMaxMsgSize specifies how much data codec can handle // DefaultMaxMsgSize specifies how much data codec can handle
DefaultMaxMsgSize int = 1024 * 1024 * 4 // 4Mb DefaultMaxMsgSize = 1024 * 1024 * 4 // 4Mb
DefaultCodec Codec = NewCodec() // DefaultCodec is the global default codec
DefaultCodec = NewCodec()
// DefaultTagName specifies struct tag name to control codec Marshal/Unmarshal
DefaultTagName = "codec"
) )
// MessageType // MessageType specifies message type for codec
type MessageType int type MessageType int
// Codec encodes/decodes various types of messages used within micro. // Codec encodes/decodes various types of messages used within micro.
@ -38,11 +41,11 @@ type MessageType int
// connection. ReadBody may be called with a nil argument to force the // connection. ReadBody may be called with a nil argument to force the
// body to be read and discarded. // body to be read and discarded.
type Codec interface { type Codec interface {
ReadHeader(io.Reader, *Message, MessageType) error ReadHeader(r io.Reader, m *Message, mt MessageType) error
ReadBody(io.Reader, interface{}) error ReadBody(r io.Reader, v interface{}) error
Write(io.Writer, *Message, interface{}) error Write(w io.Writer, m *Message, v interface{}) error
Marshal(interface{}) ([]byte, error) Marshal(v interface{}, opts ...Option) ([]byte, error)
Unmarshal([]byte, interface{}) error Unmarshal(b []byte, v interface{}, opts ...Option) error
String() string String() string
} }
@ -50,19 +53,55 @@ type Codec interface {
// the communication, likely followed by the body. // the communication, likely followed by the body.
// In the case of an error, body may be nil. // In the case of an error, body may be nil.
type Message struct { type Message struct {
Id string Header metadata.Metadata
Type MessageType
Target string Target string
Method string Method string
Endpoint string Endpoint string
Error string Error string
ID string
// The values read from the socket Body []byte
Header metadata.Metadata Type MessageType
Body []byte
} }
// NewMessage creates new codec message // NewMessage creates new codec message
func NewMessage(t MessageType) *Message { func NewMessage(t MessageType) *Message {
return &Message{Type: t, Header: metadata.New(0)} return &Message{Type: t, Header: metadata.New(0)}
} }
// MarshalAppend calls codec.Marshal(v) and returns the data appended to buf.
// If codec implements MarshalAppend, that is called instead.
func MarshalAppend(buf []byte, c Codec, v interface{}, opts ...Option) ([]byte, error) {
if nc, ok := c.(interface {
MarshalAppend([]byte, interface{}, ...Option) ([]byte, error)
}); ok {
return nc.MarshalAppend(buf, v, opts...)
}
mbuf, err := c.Marshal(v, opts...)
if err != nil {
return nil, err
}
return append(buf, mbuf...), nil
}
// RawMessage is a raw encoded JSON value.
// It implements Marshaler and Unmarshaler and can be used to delay decoding or precompute a encoding.
type RawMessage []byte
// MarshalJSON returns m as the JSON encoding of m.
func (m *RawMessage) MarshalJSON() ([]byte, error) {
if m == nil {
return []byte("null"), nil
}
return *m, nil
}
// UnmarshalJSON sets *m to a copy of data.
func (m *RawMessage) UnmarshalJSON(data []byte) error {
if m == nil {
return errors.New("RawMessage UnmarshalJSON on nil pointer")
}
*m = append((*m)[0:0], data...)
return nil
}

View File

@ -1,26 +1,26 @@
package transport package codec
import ( import (
"context" "context"
) )
type transportKey struct{} type codecKey struct{}
// FromContext get transport from context // FromContext returns codec from context
func FromContext(ctx context.Context) (Transport, bool) { func FromContext(ctx context.Context) (Codec, bool) {
if ctx == nil { if ctx == nil {
return nil, false return nil, false
} }
c, ok := ctx.Value(transportKey{}).(Transport) c, ok := ctx.Value(codecKey{}).(Codec)
return c, ok return c, ok
} }
// NewContext put transport in context // NewContext put codec in context
func NewContext(ctx context.Context, c Transport) context.Context { func NewContext(ctx context.Context, c Codec) context.Context {
if ctx == nil { if ctx == nil {
ctx = context.Background() ctx = context.Background()
} }
return context.WithValue(ctx, transportKey{}, c) return context.WithValue(ctx, codecKey{}, c)
} }
// SetOption returns a function to setup a context with given value // SetOption returns a function to setup a context with given value

35
codec/context_test.go Normal file
View File

@ -0,0 +1,35 @@
package codec
import (
"context"
"testing"
)
func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), codecKey{}, NewCodec())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("FromContext not works")
}
}
func TestNewContext(t *testing.T) {
ctx := NewContext(context.TODO(), NewCodec())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestSetOption(t *testing.T) {
type key struct{}
o := SetOption(key{}, "test")
opts := &Options{}
o(opts)
if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" {
t.Fatal("SetOption not works")
}
}

45
codec/frame.go Normal file
View File

@ -0,0 +1,45 @@
package codec
// Frame gives us the ability to define raw data to send over the pipes
type Frame struct {
Data []byte
}
// NewFrame returns new frame with data
func NewFrame(data []byte) *Frame {
return &Frame{Data: data}
}
// MarshalJSON returns frame data
func (m *Frame) MarshalJSON() ([]byte, error) {
return m.Marshal()
}
// UnmarshalJSON set frame data
func (m *Frame) UnmarshalJSON(data []byte) error {
return m.Unmarshal(data)
}
// ProtoMessage noop func
func (m *Frame) ProtoMessage() {}
// Reset resets frame
func (m *Frame) Reset() {
*m = Frame{}
}
// String returns frame as string
func (m *Frame) String() string {
return string(m.Data)
}
// Marshal returns frame data
func (m *Frame) Marshal() ([]byte, error) {
return m.Data, nil
}
// Unmarshal set frame data
func (m *Frame) Unmarshal(data []byte) error {
m.Data = data
return nil
}

28
codec/frame.proto Normal file
View File

@ -0,0 +1,28 @@
// Copyright 2021-2023 Unistack LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package micro.codec;
option cc_enable_arenas = true;
option go_package = "go.unistack.org/micro/v4/codec;codec";
option java_multiple_files = true;
option java_outer_classname = "MicroCodec";
option java_package = "micro.codec";
option objc_class_prefix = "MCODEC";
message Frame {
bytes data = 1;
}

View File

@ -1,16 +1,12 @@
package codec package codec
import ( import (
"encoding/json"
"io" "io"
"io/ioutil"
) )
type noopCodec struct { type noopCodec struct {
} opts Options
// Frame gives us the ability to define raw data to send over the pipes
type Frame struct {
Data []byte
} }
func (c *noopCodec) ReadHeader(conn io.Reader, m *Message, t MessageType) error { func (c *noopCodec) ReadHeader(conn io.Reader, m *Message, t MessageType) error {
@ -19,7 +15,7 @@ func (c *noopCodec) ReadHeader(conn io.Reader, m *Message, t MessageType) error
func (c *noopCodec) ReadBody(conn io.Reader, b interface{}) error { func (c *noopCodec) ReadBody(conn io.Reader, b interface{}) error {
// read bytes // read bytes
buf, err := ioutil.ReadAll(conn) buf, err := io.ReadAll(conn)
if err != nil { if err != nil {
return err return err
} }
@ -29,18 +25,14 @@ func (c *noopCodec) ReadBody(conn io.Reader, b interface{}) error {
} }
switch v := b.(type) { switch v := b.(type) {
case string:
v = string(buf)
case *string: case *string:
*v = string(buf) *v = string(buf)
case []byte:
v = buf
case *[]byte: case *[]byte:
*v = buf *v = buf
case *Frame: case *Frame:
v.Data = buf v.Data = buf
default: default:
return ErrInvalidMessage return json.Unmarshal(buf, v)
} }
return nil return nil
@ -64,7 +56,11 @@ func (c *noopCodec) Write(conn io.Writer, m *Message, b interface{}) error {
case []byte: case []byte:
v = vb v = vb
default: default:
return ErrInvalidMessage var err error
v, err = json.Marshal(vb)
if err != nil {
return err
}
} }
_, err := conn.Write(v) _, err := conn.Write(v)
return err return err
@ -75,11 +71,11 @@ func (c *noopCodec) String() string {
} }
// NewCodec returns new noop codec // NewCodec returns new noop codec
func NewCodec() Codec { func NewCodec(opts ...Option) Codec {
return &noopCodec{} return &noopCodec{opts: NewOptions(opts...)}
} }
func (c *noopCodec) Marshal(v interface{}) ([]byte, error) { func (c *noopCodec) Marshal(v interface{}, opts ...Option) ([]byte, error) {
if v == nil { if v == nil {
return nil, nil return nil, nil
} }
@ -98,30 +94,31 @@ func (c *noopCodec) Marshal(v interface{}) ([]byte, error) {
case *Message: case *Message:
return ve.Body, nil return ve.Body, nil
} }
return nil, ErrInvalidMessage
return json.Marshal(v)
} }
func (c *noopCodec) Unmarshal(d []byte, v interface{}) error { func (c *noopCodec) Unmarshal(d []byte, v interface{}, opts ...Option) error {
var err error
if v == nil { if v == nil {
return nil return nil
} }
switch ve := v.(type) { switch ve := v.(type) {
case string:
ve = string(d)
case *string: case *string:
*ve = string(d) *ve = string(d)
return nil
case []byte: case []byte:
ve = d copy(ve, d)
return nil
case *[]byte: case *[]byte:
*ve = d *ve = d
return nil
case *Frame: case *Frame:
ve.Data = d ve.Data = d
return nil
case *Message: case *Message:
ve.Body = d ve.Body = d
default: return nil
err = ErrInvalidMessage
} }
return err return json.Unmarshal(d, v)
} }

48
codec/noop_test.go Normal file
View File

@ -0,0 +1,48 @@
package codec
import (
"bytes"
"testing"
)
func TestNoopBytesPtr(t *testing.T) {
req := []byte("test req")
rsp := make([]byte, len(req))
nc := NewCodec()
if err := nc.Unmarshal(req, &rsp); err != nil {
t.Fatal(err)
}
if !bytes.Equal(req, rsp) {
t.Fatalf("req not eq rsp: %s != %s", req, rsp)
}
}
func TestNoopBytes(t *testing.T) {
req := []byte("test req")
var rsp []byte
nc := NewCodec()
if err := nc.Unmarshal(req, &rsp); err != nil {
t.Fatal(err)
}
if !bytes.Equal(req, rsp) {
t.Fatalf("req not eq rsp: %s != %s", req, rsp)
}
}
func TestNoopString(t *testing.T) {
req := []byte("test req")
var rsp string
nc := NewCodec()
if err := nc.Unmarshal(req, &rsp); err != nil {
t.Fatal(err)
}
if !bytes.Equal(req, []byte(rsp)) {
t.Fatalf("req not eq rsp: %s != %s", req, rsp)
}
}

View File

@ -1,9 +1,11 @@
package codec package codec
import ( import (
"github.com/unistack-org/micro/v3/logger" "context"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/tracer" "go.unistack.org/micro/v4/logger"
"go.unistack.org/micro/v4/meter"
"go.unistack.org/micro/v4/tracer"
) )
// Option func // Option func
@ -11,10 +13,18 @@ type Option func(*Options)
// Options contains codec options // Options contains codec options
type Options struct { type Options struct {
// Meter used for metrics
Meter meter.Meter
// Logger used for logging
Logger logger.Logger
// Tracer used for tracing
Tracer tracer.Tracer
// Context stores additional codec options
Context context.Context
// TagName specifies tag name in struct to control codec
TagName string
// MaxMsgSize specifies max messages size that reads by codec
MaxMsgSize int MaxMsgSize int
Meter meter.Meter
Logger logger.Logger
Tracer tracer.Tracer
} }
// MaxMsgSize sets the max message size // MaxMsgSize sets the max message size
@ -24,6 +34,13 @@ func MaxMsgSize(n int) Option {
} }
} }
// TagName sets the codec tag name in struct
func TagName(n string) Option {
return func(o *Options) {
o.TagName = n
}
}
// Logger sets the logger // Logger sets the logger
func Logger(l logger.Logger) Option { func Logger(l logger.Logger) Option {
return func(o *Options) { return func(o *Options) {
@ -45,12 +62,15 @@ func Meter(m meter.Meter) Option {
} }
} }
// NewOptions returns new options
func NewOptions(opts ...Option) Options { func NewOptions(opts ...Option) Options {
options := Options{ options := Options{
Context: context.Background(),
Logger: logger.DefaultLogger, Logger: logger.DefaultLogger,
Meter: meter.DefaultMeter, Meter: meter.DefaultMeter,
Tracer: tracer.DefaultTracer, Tracer: tracer.DefaultTracer,
MaxMsgSize: DefaultMaxMsgSize, MaxMsgSize: DefaultMaxMsgSize,
TagName: DefaultTagName,
} }
for _, o := range opts { for _, o := range opts {

View File

@ -1,15 +1,27 @@
// Package config is an interface for dynamic configuration. // Package config is an interface for dynamic configuration.
package config package config // import "go.unistack.org/micro/v4/config"
import ( import (
"context" "context"
"errors" "errors"
"reflect"
"time"
"go.unistack.org/micro/v4/options"
) )
var ( type Validator interface {
// DefaultConfig default config Validate() error
DefaultConfig Config = NewConfig() }
)
// DefaultConfig default config
var DefaultConfig = NewConfig()
// DefaultWatcherMinInterval default min interval for poll changes
var DefaultWatcherMinInterval = 5 * time.Second
// DefaultWatcherMaxInterval default max interval for poll changes
var DefaultWatcherMaxInterval = 9 * time.Second
var ( var (
// ErrCodecMissing is returned when codec needed and not specified // ErrCodecMissing is returned when codec needed and not specified
@ -18,39 +30,190 @@ var (
ErrInvalidStruct = errors.New("invalid struct specified") ErrInvalidStruct = errors.New("invalid struct specified")
// ErrWatcherStopped is returned when source watcher has been stopped // ErrWatcherStopped is returned when source watcher has been stopped
ErrWatcherStopped = errors.New("watcher stopped") ErrWatcherStopped = errors.New("watcher stopped")
// ErrWatcherNotImplemented returned when config does not implement watch
ErrWatcherNotImplemented = errors.New("watcher not implemented")
) )
// Config is an interface abstraction for dynamic configuration // Config is an interface abstraction for dynamic configuration
type Config interface { type Config interface {
// Name returns name of config
Name() string
// Init the config // Init the config
Init(opts ...Option) error Init(opts ...options.Option) error
// Options in the config // Options in the config
Options() Options Options() Options
// Load config from sources // Load config from sources
Load(context.Context) error Load(context.Context, ...options.Option) error
// Save config to sources // Save config to sources
Save(context.Context) error Save(context.Context, ...options.Option) error
// Watch a value for changes // Watch a config for changes
// Watch(interface{}) (Watcher, error) Watch(context.Context, ...options.Option) (Watcher, error)
// String returns config type name
String() string String() string
} }
// Watcher is the config watcher // Watcher is the config watcher
//type Watcher interface { type Watcher interface {
// Next() (, error) // Next blocks until update happens or error returned
// Stop() error Next() (map[string]interface{}, error)
//} // Stop stops watcher
Stop() error
}
// Load loads config from config sources // Load loads config from config sources
func Load(ctx context.Context, cs ...Config) error { func Load(ctx context.Context, cs []Config, opts ...options.Option) error {
var err error var err error
for _, c := range cs { for _, c := range cs {
if err = c.Init(); err != nil { if err = c.Init(); err != nil {
return err return err
} }
if err = c.Load(ctx); err != nil { if err = c.Load(ctx, opts...); err != nil {
return err return err
} }
} }
return nil return nil
} }
// Validate runs Validate() error func for each struct field
func Validate(ctx context.Context, cfg interface{}) error {
if cfg == nil {
return nil
}
if v, ok := cfg.(Validator); ok {
if err := v.Validate(); err != nil {
return err
}
}
sv := reflect.ValueOf(cfg)
if sv.Kind() == reflect.Ptr {
sv = sv.Elem()
}
if sv.Kind() != reflect.Struct {
return nil
}
typ := sv.Type()
for idx := 0; idx < typ.NumField(); idx++ {
fld := typ.Field(idx)
val := sv.Field(idx)
if !val.IsValid() || len(fld.PkgPath) != 0 {
continue
}
if v, ok := val.Interface().(Validator); ok {
if err := v.Validate(); err != nil {
return err
}
}
switch val.Kind() {
case reflect.Ptr:
if reflect.Indirect(val).Kind() == reflect.Struct {
if err := Validate(ctx, val.Interface()); err != nil {
return err
}
}
case reflect.Struct:
if err := Validate(ctx, val.Interface()); err != nil {
return err
}
}
}
return nil
}
var (
// DefaultBeforeLoad default func that runs before config Load
DefaultBeforeLoad = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().BeforeLoad {
if fn == nil {
return nil
}
if err := fn(ctx, c); err != nil {
c.Options().Logger.Error(ctx, c.String()+" BeforeLoad error "+err.Error())
if !c.Options().AllowFail {
return err
}
}
}
return nil
}
// DefaultAfterLoad default func that runs after config Load
DefaultAfterLoad = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().AfterLoad {
if fn == nil {
return nil
}
if err := fn(ctx, c); err != nil {
c.Options().Logger.Error(ctx, c.String()+" AfterLoad error "+err.Error())
if !c.Options().AllowFail {
return err
}
}
}
return nil
}
// DefaultBeforeSave default func that runs befora config Save
DefaultBeforeSave = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().BeforeSave {
if fn == nil {
return nil
}
if err := fn(ctx, c); err != nil {
c.Options().Logger.Error(ctx, c.String()+" BeforeSave error "+err.Error())
if !c.Options().AllowFail {
return err
}
}
}
return nil
}
// DefaultAfterSave default func that runs after config Save
DefaultAfterSave = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().AfterSave {
if fn == nil {
return nil
}
if err := fn(ctx, c); err != nil {
c.Options().Logger.Error(ctx, c.String()+" AfterSave error "+err.Error())
if !c.Options().AllowFail {
return err
}
}
}
return nil
}
// DefaultBeforeInit default func that runs befora config Init
DefaultBeforeInit = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().BeforeInit {
if fn == nil {
return nil
}
if err := fn(ctx, c); err != nil {
c.Options().Logger.Error(ctx, c.String()+" BeforeInit error "+err.Error())
if !c.Options().AllowFail {
return err
}
}
}
return nil
}
// DefaultAfterInit default func that runs after config Init
DefaultAfterInit = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().AfterSave {
if fn == nil {
return nil
}
if err := fn(ctx, c); err != nil {
c.Options().Logger.Error(ctx, c.String()+" AfterInit error "+err.Error())
if !c.Options().AllowFail {
return err
}
}
}
return nil
}
)

View File

@ -6,6 +6,7 @@ import (
type configKey struct{} type configKey struct{}
// FromContext returns store from context
func FromContext(ctx context.Context) (Config, bool) { func FromContext(ctx context.Context) (Config, bool) {
if ctx == nil { if ctx == nil {
return nil, false return nil, false
@ -14,19 +15,10 @@ func FromContext(ctx context.Context) (Config, bool) {
return c, ok return c, ok
} }
// NewContext put store in context
func NewContext(ctx context.Context, c Config) context.Context { func NewContext(ctx context.Context, c Config) context.Context {
if ctx == nil { if ctx == nil {
ctx = context.Background() ctx = context.Background()
} }
return context.WithValue(ctx, configKey{}, c) return context.WithValue(ctx, configKey{}, c)
} }
// SetOption returns a function to setup a context with given value
func SetOption(k, v interface{}) Option {
return func(o *Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}

42
config/context_test.go Normal file
View File

@ -0,0 +1,42 @@
package config
import (
"context"
"testing"
)
func TestFromNilContext(t *testing.T) {
// nolint: staticcheck
c, ok := FromContext(nil)
if ok || c != nil {
t.Fatal("FromContext not works")
}
}
func TestNewNilContext(t *testing.T) {
// nolint: staticcheck
ctx := NewContext(nil, NewConfig())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}
func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), configKey{}, NewConfig())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("FromContext not works")
}
}
func TestNewContext(t *testing.T) {
ctx := NewContext(context.TODO(), NewConfig())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}

View File

@ -5,9 +5,14 @@ import (
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/imdario/mergo" "dario.cat/mergo"
rutil "github.com/unistack-org/micro/v3/util/reflect" "github.com/google/uuid"
"go.unistack.org/micro/v4/options"
mid "go.unistack.org/micro/v4/util/id"
rutil "go.unistack.org/micro/v4/util/reflect"
mtime "go.unistack.org/micro/v4/util/time"
) )
type defaultConfig struct { type defaultConfig struct {
@ -18,45 +23,76 @@ func (c *defaultConfig) Options() Options {
return c.opts return c.opts
} }
func (c *defaultConfig) Init(opts ...Option) error { func (c *defaultConfig) Init(opts ...options.Option) error {
for _, o := range opts { for _, o := range opts {
o(&c.opts) o(&c.opts)
} }
if err := DefaultBeforeInit(c.opts.Context, c); err != nil && !c.opts.AllowFail {
return err
}
if err := DefaultAfterInit(c.opts.Context, c); err != nil && !c.opts.AllowFail {
return err
}
return nil return nil
} }
func (c *defaultConfig) Load(ctx context.Context) error { func (c *defaultConfig) Load(ctx context.Context, opts ...options.Option) error {
for _, fn := range c.opts.BeforeLoad { if c.opts.SkipLoad != nil && c.opts.SkipLoad(ctx, c) {
if err := fn(ctx, c); err != nil && !c.opts.AllowFail { return nil
}
if err := DefaultBeforeLoad(ctx, c); err != nil && !c.opts.AllowFail {
return err
}
options := NewLoadOptions(opts...)
mopts := []func(*mergo.Config){mergo.WithTypeCheck}
if options.Override {
mopts = append(mopts, mergo.WithOverride)
}
if options.Append {
mopts = append(mopts, mergo.WithAppendSlice)
}
dst := c.opts.Struct
if options.Struct != nil {
dst = options.Struct
}
src, err := rutil.Zero(dst)
if err != nil {
if !c.opts.AllowFail {
return err
}
if err = DefaultAfterLoad(ctx, c); err != nil && !c.opts.AllowFail {
return err return err
} }
} }
src, err := rutil.Zero(c.opts.Struct) if err = fillValues(reflect.ValueOf(src), c.opts.StructTag); err == nil {
if err == nil { err = mergo.Merge(dst, src, mopts...)
valueOf := reflect.ValueOf(src)
if err = c.fillValues(ctx, valueOf); err == nil {
err = mergo.Merge(c.opts.Struct, src, mergo.WithOverride, mergo.WithTypeCheck, mergo.WithAppendSlice)
}
} }
if err != nil && !c.opts.AllowFail { if err != nil && !c.opts.AllowFail {
return err return err
} }
for _, fn := range c.opts.AfterLoad { if err := DefaultAfterLoad(ctx, c); err != nil && !c.opts.AllowFail {
if err := fn(ctx, c); err != nil && !c.opts.AllowFail { return err
return err
}
} }
return nil return nil
} }
func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val string) error { //nolint:gocyclo
func fillValue(value reflect.Value, val string) error {
if !rutil.IsEmpty(value) { if !rutil.IsEmpty(value) {
return nil return nil
} }
switch value.Kind() { switch value.Kind() {
case reflect.Map: case reflect.Map:
t := value.Type() t := value.Type()
@ -70,10 +106,10 @@ func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val
kv := strings.FieldsFunc(nval, func(c rune) bool { return c == '=' }) kv := strings.FieldsFunc(nval, func(c rune) bool { return c == '=' })
mkey := reflect.Indirect(reflect.New(kt)) mkey := reflect.Indirect(reflect.New(kt))
mval := reflect.Indirect(reflect.New(et)) mval := reflect.Indirect(reflect.New(et))
if err := c.fillValue(ctx, mkey, kv[0]); err != nil { if err := fillValue(mkey, kv[0]); err != nil {
return err return err
} }
if err := c.fillValue(ctx, mval, kv[1]); err != nil { if err := fillValue(mval, kv[1]); err != nil {
return err return err
} }
value.SetMapIndex(mkey, mval) value.SetMapIndex(mkey, mval)
@ -83,7 +119,7 @@ func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val
value.Set(reflect.MakeSlice(reflect.SliceOf(value.Type().Elem()), len(nvals), len(nvals))) value.Set(reflect.MakeSlice(reflect.SliceOf(value.Type().Elem()), len(nvals), len(nvals)))
for idx, nval := range nvals { for idx, nval := range nvals {
nvalue := reflect.Indirect(reflect.New(value.Type().Elem())) nvalue := reflect.Indirect(reflect.New(value.Type().Elem()))
if err := c.fillValue(ctx, nvalue, nval); err != nil { if err := fillValue(nvalue, nval); err != nil {
return err return err
} }
value.Index(idx).Set(nvalue) value.Index(idx).Set(nvalue)
@ -95,6 +131,20 @@ func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val
} }
value.Set(reflect.ValueOf(v)) value.Set(reflect.ValueOf(v))
case reflect.String: case reflect.String:
switch val {
case "micro:generate uuid":
uid, err := uuid.NewRandom()
if err != nil {
return err
}
val = uid.String()
case "micro:generate id":
uid, err := mid.New()
if err != nil {
return err
}
val = uid
}
value.Set(reflect.ValueOf(val)) value.Set(reflect.ValueOf(val))
case reflect.Float32: case reflect.Float32:
v, err := strconv.ParseFloat(val, 32) v, err := strconv.ParseFloat(val, 32)
@ -107,7 +157,7 @@ func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val
if err != nil { if err != nil {
return err return err
} }
value.Set(reflect.ValueOf(float64(v))) value.Set(reflect.ValueOf(v))
case reflect.Int: case reflect.Int:
v, err := strconv.ParseInt(val, 10, 0) v, err := strconv.ParseInt(val, 10, 0)
if err != nil { if err != nil {
@ -133,11 +183,26 @@ func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val
} }
value.Set(reflect.ValueOf(int32(v))) value.Set(reflect.ValueOf(int32(v)))
case reflect.Int64: case reflect.Int64:
v, err := strconv.ParseInt(val, 10, 64) switch {
if err != nil { case value.Type().String() == "time.Duration" && value.Type().PkgPath() == "time":
return err v, err := time.ParseDuration(val)
if err != nil {
return err
}
value.Set(reflect.ValueOf(v))
case value.Type().String() == "time.Duration" && value.Type().PkgPath() == "go.unistack.org/micro/v4/util/time":
v, err := mtime.ParseDuration(val)
if err != nil {
return err
}
value.SetInt(int64(v))
default:
v, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return err
}
value.Set(reflect.ValueOf(v))
} }
value.Set(reflect.ValueOf(int64(v)))
case reflect.Uint: case reflect.Uint:
v, err := strconv.ParseUint(val, 10, 0) v, err := strconv.ParseUint(val, 10, 0)
if err != nil { if err != nil {
@ -167,12 +232,12 @@ func (c *defaultConfig) fillValue(ctx context.Context, value reflect.Value, val
if err != nil { if err != nil {
return err return err
} }
value.Set(reflect.ValueOf(uint64(v))) value.Set(reflect.ValueOf(v))
} }
return nil return nil
} }
func (c *defaultConfig) fillValues(ctx context.Context, valueOf reflect.Value) error { func fillValues(valueOf reflect.Value, tname string) error {
var values reflect.Value var values reflect.Value
if valueOf.Kind() == reflect.Ptr { if valueOf.Kind() == reflect.Ptr {
@ -199,7 +264,7 @@ func (c *defaultConfig) fillValues(ctx context.Context, valueOf reflect.Value) e
switch value.Kind() { switch value.Kind() {
case reflect.Struct: case reflect.Struct:
value.Set(reflect.Indirect(reflect.New(value.Type()))) value.Set(reflect.Indirect(reflect.New(value.Type())))
if err := c.fillValues(ctx, value); err != nil { if err := fillValues(value, tname); err != nil {
return err return err
} }
continue continue
@ -213,17 +278,17 @@ func (c *defaultConfig) fillValues(ctx context.Context, valueOf reflect.Value) e
value.Set(reflect.New(value.Type().Elem())) value.Set(reflect.New(value.Type().Elem()))
} }
value = value.Elem() value = value.Elem()
if err := c.fillValues(ctx, value); err != nil { if err := fillValues(value, tname); err != nil {
return err return err
} }
continue continue
} }
tag, ok := field.Tag.Lookup(c.opts.StructTag) tag, ok := field.Tag.Lookup(tname)
if !ok { if !ok {
continue continue
} }
if err := c.fillValue(ctx, value, tag); err != nil { if err := fillValue(value, tag); err != nil {
return err return err
} }
} }
@ -231,17 +296,17 @@ func (c *defaultConfig) fillValues(ctx context.Context, valueOf reflect.Value) e
return nil return nil
} }
func (c *defaultConfig) Save(ctx context.Context) error { func (c *defaultConfig) Save(ctx context.Context, _ ...options.Option) error {
for _, fn := range c.opts.BeforeSave { if c.opts.SkipSave != nil && c.opts.SkipSave(ctx, c) {
if err := fn(ctx, c); err != nil && !c.opts.AllowFail { return nil
return err
}
} }
for _, fn := range c.opts.AfterSave { if err := DefaultBeforeSave(ctx, c); err != nil {
if err := fn(ctx, c); err != nil && !c.opts.AllowFail { return err
return err }
}
if err := DefaultAfterSave(ctx, c); err != nil {
return err
} }
return nil return nil
@ -251,7 +316,16 @@ func (c *defaultConfig) String() string {
return "default" return "default"
} }
func NewConfig(opts ...Option) Config { func (c *defaultConfig) Name() string {
return c.opts.Name
}
func (c *defaultConfig) Watch(ctx context.Context, opts ...options.Option) (Watcher, error) {
return nil, ErrWatcherNotImplemented
}
// NewConfig returns new default config source
func NewConfig(opts ...options.Option) Config {
options := NewOptions(opts...) options := NewOptions(opts...)
if len(options.StructTag) == 0 { if len(options.StructTag) == 0 {
options.StructTag = "default" options.StructTag = "default"

View File

@ -4,36 +4,63 @@ import (
"context" "context"
"fmt" "fmt"
"testing" "testing"
"time"
"github.com/unistack-org/micro/v3/config" "go.unistack.org/micro/v4/config"
mid "go.unistack.org/micro/v4/util/id"
mtime "go.unistack.org/micro/v4/util/time"
) )
type Cfg struct { type cfg struct {
StringValue string `default:"string_value"`
IgnoreValue string `json:"-"`
StructValue *cfgStructValue
IntValue int `default:"99"`
DurationValue time.Duration `default:"10s"`
MDurationValue mtime.Duration `default:"10s"`
MapValue map[string]bool `default:"key1=true,key2=false"`
UUIDValue string `default:"micro:generate uuid"`
IDValue string `default:"micro:generate id"`
}
type cfgStructValue struct {
StringValue string `default:"string_value"` StringValue string `default:"string_value"`
IntValue int `default:"99"` }
IgnoreValue string `json:"-"`
StructValue struct { func (c *cfg) Validate() error {
StringValue string `default:"string_value"` if c.IntValue != 10 {
return fmt.Errorf("invalid IntValue %d != %d", 10, c.IntValue)
} }
if c.MapValue["key1"] != true {
return fmt.Errorf("invalid MapValue %t != %t", true, c.MapValue["key1"])
}
return nil
}
func (c *cfgStructValue) Validate() error {
if c.StringValue != "string_value" {
return fmt.Errorf("invalid StringValue %s != %s", "string_value", c.StringValue)
}
return nil
} }
func TestDefault(t *testing.T) { func TestDefault(t *testing.T) {
ctx := context.Background() ctx := context.Background()
conf := &Cfg{IntValue: 10} conf := &cfg{IntValue: 10}
blfn := func(ctx context.Context, cfg config.Config) error { blfn := func(_ context.Context, c config.Config) error {
conf, ok := cfg.Options().Struct.(*Cfg) nconf, ok := c.Options().Struct.(*cfg)
if !ok { if !ok {
return fmt.Errorf("failed to get Struct from options: %v", cfg.Options()) return fmt.Errorf("failed to get Struct from options: %v", c.Options())
} }
conf.StringValue = "before_load" nconf.StringValue = "before_load"
return nil return nil
} }
alfn := func(ctx context.Context, cfg config.Config) error { alfn := func(_ context.Context, c config.Config) error {
conf, ok := cfg.Options().Struct.(*Cfg) nconf, ok := c.Options().Struct.(*cfg)
if !ok { if !ok {
return fmt.Errorf("failed to get Struct from options: %v", cfg.Options()) return fmt.Errorf("failed to get Struct from options: %v", c.Options())
} }
conf.StringValue = "after_load" nconf.StringValue = "after_load"
return nil return nil
} }
@ -47,6 +74,53 @@ func TestDefault(t *testing.T) {
if conf.StringValue != "after_load" { if conf.StringValue != "after_load" {
t.Fatal("AfterLoad option not working") t.Fatal("AfterLoad option not working")
} }
if len(conf.MapValue) != 2 {
t.Fatalf("map value invalid: %#+v\n", conf.MapValue)
}
t.Logf("%#+v\n", conf) if conf.UUIDValue == "" {
t.Fatalf("uuid value empty")
} else if len(conf.UUIDValue) != 36 {
t.Fatalf("uuid value invalid: %s", conf.UUIDValue)
}
if conf.IDValue == "" {
t.Fatalf("id value empty")
} else if len(conf.IDValue) != mid.DefaultSize {
t.Fatalf("id value invalid: %s", conf.IDValue)
}
_ = conf
// t.Logf("%#+v\n", conf)
}
func TestValidate(t *testing.T) {
ctx := context.Background()
conf := &cfg{IntValue: 10}
cfg := config.NewConfig(config.Struct(conf))
if err := cfg.Init(); err != nil {
t.Fatal(err)
}
if err := cfg.Load(ctx); err != nil {
t.Fatal(err)
}
if err := config.Validate(ctx, conf); err != nil {
t.Fatal(err)
}
}
func TestString(t *testing.T) {
cfg := config.NewConfig()
res := cfg.String()
if res != "default" {
t.Fatalf("string value invalid: %s", res)
}
}
func TestName(t *testing.T) {
cfg := config.NewConfig()
res := cfg.Name()
if res != "" {
t.Fatal("name value not empty")
}
} }

View File

@ -2,38 +2,55 @@ package config
import ( import (
"context" "context"
"time"
"github.com/unistack-org/micro/v3/codec" "go.unistack.org/micro/v4/codec"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v4/logger"
"github.com/unistack-org/micro/v3/meter" "go.unistack.org/micro/v4/meter"
"github.com/unistack-org/micro/v3/tracer" "go.unistack.org/micro/v4/options"
"go.unistack.org/micro/v4/tracer"
) )
// Options hold the config options
type Options struct { type Options struct {
AllowFail bool // Struct holds the destination config struct
BeforeLoad []func(context.Context, Config) error
AfterLoad []func(context.Context, Config) error
BeforeSave []func(context.Context, Config) error
AfterSave []func(context.Context, Config) error
// Struct that holds config data
Struct interface{} Struct interface{}
// StructTag name
StructTag string
// Logger that will be used
Logger logger.Logger
// Meter that will be used
Meter meter.Meter
// Tracer used for trace
Tracer tracer.Tracer
// Codec that used for load/save // Codec that used for load/save
Codec codec.Codec Codec codec.Codec
// Context for alternative data // Tracer that will be used
Tracer tracer.Tracer
// Meter that will be used
Meter meter.Meter
// Logger that will be used
Logger logger.Logger
// Context used for external options
Context context.Context Context context.Context
// Name of the config
Name string
// StructTag name
StructTag string
// BeforeSave contains slice of funcs that runs before Save
BeforeSave []func(context.Context, Config) error
// AfterSave contains slice of funcs that runs after Save
AfterSave []func(context.Context, Config) error
// BeforeLoad contains slice of funcs that runs before Load
BeforeLoad []func(context.Context, Config) error
// AfterLoad contains slice of funcs that runs after Load
AfterLoad []func(context.Context, Config) error
// BeforeInit contains slice of funcs that runs before Init
BeforeInit []func(context.Context, Config) error
// AfterInit contains slice of funcs that runs after Init
AfterInit []func(context.Context, Config) error
// AllowFail flag to allow fail in config source
AllowFail bool
// SkipLoad runs only if condition returns true
SkipLoad func(context.Context, Config) bool
// SkipSave runs only if condition returns true
SkipSave func(context.Context, Config) bool
} }
type Option func(o *Options) // NewOptions new options struct with filed values
func NewOptions(opts ...options.Option) Options {
func NewOptions(opts ...Option) Options {
options := Options{ options := Options{
Logger: logger.DefaultLogger, Logger: logger.DefaultLogger,
Meter: meter.DefaultMeter, Meter: meter.DefaultMeter,
@ -47,72 +64,156 @@ func NewOptions(opts ...Option) Options {
return options return options
} }
func AllowFail(b bool) Option { // LoadOptions struct
return func(o *Options) { type LoadOptions struct {
o.AllowFail = b Struct interface{}
Context context.Context
Override bool
Append bool
}
// NewLoadOptions create LoadOptions struct with provided opts
func NewLoadOptions(opts ...options.Option) LoadOptions {
options := LoadOptions{}
for _, o := range opts {
o(&options)
}
return options
}
// LoadOverride override values when load
func LoadOverride(b bool) options.Option {
return func(src interface{}) error {
return options.Set(src, b, ".Override")
} }
} }
func BeforeLoad(fn ...func(context.Context, Config) error) Option { // LoadAppend override values when load
return func(o *Options) { func LoadAppend(b bool) options.Option {
o.BeforeLoad = fn return func(src interface{}) error {
return options.Set(src, b, ".Append")
} }
} }
func AfterLoad(fn ...func(context.Context, Config) error) Option { // SaveOptions struct
return func(o *Options) { type SaveOptions struct {
o.AfterLoad = fn Struct interface{}
Context context.Context
}
// NewSaveOptions fill SaveOptions struct
func NewSaveOptions(opts ...options.Option) SaveOptions {
options := SaveOptions{}
for _, o := range opts {
o(&options)
}
return options
}
// AllowFail allows config source to fail
func AllowFail(b bool) options.Option {
return func(src interface{}) error {
return options.Set(src, b, ".AllowFail")
} }
} }
func BeforeSave(fn ...func(context.Context, Config) error) Option { // BeforeInit run funcs before config Init
return func(o *Options) { func BeforeInit(fn ...func(context.Context, Config) error) options.Option {
o.BeforeSave = fn return func(src interface{}) error {
return options.Set(src, fn, ".BeforeInit")
} }
} }
func AfterSave(fn ...func(context.Context, Config) error) Option { // AfterInit run funcs after config Init
return func(o *Options) { func AfterInit(fn ...func(context.Context, Config) error) options.Option {
o.AfterSave = fn return func(src interface{}) error {
return options.Set(src, fn, ".AfterInit")
} }
} }
func Context(ctx context.Context) Option { // BeforeLoad run funcs before config load
return func(o *Options) { func BeforeLoad(fn ...func(context.Context, Config) error) options.Option {
o.Context = ctx return func(src interface{}) error {
return options.Set(src, fn, ".BeforeLoad")
} }
} }
// Codec sets the source codec // AfterLoad run funcs after config load
func Codec(c codec.Codec) Option { func AfterLoad(fn ...func(context.Context, Config) error) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.Codec = c return options.Set(src, fn, ".AfterLoad")
} }
} }
func Logger(l logger.Logger) Option { // BeforeSave run funcs before save
return func(o *Options) { func BeforeSave(fn ...func(context.Context, Config) error) options.Option {
o.Logger = l return func(src interface{}) error {
return options.Set(src, fn, ".BeforeSave")
} }
} }
// Tracer to be used for tracing // AfterSave run fncs after save
func Tracer(t tracer.Tracer) Option { func AfterSave(fn ...func(context.Context, Config) error) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.Tracer = t return options.Set(src, fn, ".AfterSave")
} }
} }
// Struct used as config // Struct used as config
func Struct(v interface{}) Option { func Struct(v interface{}) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.Struct = v return options.Set(src, v, ".Struct")
} }
} }
// StructTag // StructTag sets the struct tag that used for filling
func StructTag(name string) Option { func StructTag(name string) options.Option {
return func(o *Options) { return func(src interface{}) error {
o.StructTag = name return options.Set(src, name, ".StructTag")
}
}
// WatchOptions struuct
type WatchOptions struct {
// Context used by non default options
Context context.Context
// Struct for filling
Struct interface{}
// MinInterval specifies the min time.Duration interval for poll changes
MinInterval time.Duration
// MaxInterval specifies the max time.Duration interval for poll changes
MaxInterval time.Duration
// Coalesce multiple events to one
Coalesce bool
}
// NewWatchOptions create WatchOptions struct with provided opts
func NewWatchOptions(opts ...options.Option) WatchOptions {
options := WatchOptions{
Context: context.Background(),
MinInterval: DefaultWatcherMinInterval,
MaxInterval: DefaultWatcherMaxInterval,
}
for _, o := range opts {
o(&options)
}
return options
}
// Coalesce controls watch event combining
func WatchCoalesce(b bool) options.Option {
return func(src interface{}) error {
return options.Set(src, b, ".Coalesce")
}
}
// WatchInterval specifies min and max time.Duration for pulling changes
func WatchInterval(min, max time.Duration) options.Option {
return func(src interface{}) error {
var err error
if err = options.Set(src, min, ".MinInterval"); err == nil {
err = options.Set(src, max, ".MaxInterval")
}
return err
} }
} }

View File

@ -1,26 +0,0 @@
package config_test
import (
"testing"
rutil "github.com/unistack-org/micro/v3/util/reflect"
)
type Config struct {
Value string
SubConfig *SubConfig
Config *Config
}
type SubConfig struct {
Value string
}
func TestReflect(t *testing.T) {
cfg1 := &Config{Value: "cfg1", Config: &Config{Value: "cfg1_1"}, SubConfig: &SubConfig{Value: "cfg1"}}
cfg2, err := rutil.Zero(cfg1)
if err != nil {
t.Fatal(err)
}
t.Logf("dst: %#+v\n", cfg2)
}

22
context.go Normal file
View File

@ -0,0 +1,22 @@
package micro
import "context"
type serviceKey struct{}
// FromContext retrieves a Service from the Context.
func FromContext(ctx context.Context) (Service, bool) {
if ctx == nil {
return nil, false
}
s, ok := ctx.Value(serviceKey{}).(Service)
return s, ok
}
// NewContext returns a new Context with the Service embedded within it.
func NewContext(ctx context.Context, s Service) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, serviceKey{}, s)
}

24
context_test.go Normal file
View File

@ -0,0 +1,24 @@
package micro
import (
"context"
"testing"
)
func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.TODO(), serviceKey{}, NewService())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("FromContext not works")
}
}
func TestNewContext(t *testing.T) {
ctx := NewContext(context.TODO(), NewService())
c, ok := FromContext(ctx)
if c == nil || !ok {
t.Fatal("NewContext not works")
}
}

159
database/dsn.go Normal file
View File

@ -0,0 +1,159 @@
package database
import (
"crypto/tls"
"errors"
"fmt"
"net/url"
"strings"
)
var (
ErrInvalidDSNAddr = errors.New("invalid dsn addr")
ErrInvalidDSNUnescaped = errors.New("dsn must be escaped")
ErrInvalidDSNNoSlash = errors.New("dsn must contains slash")
)
type Config struct {
TLSConfig *tls.Config
Username string
Password string
Scheme string
Host string
Port string
Database string
Params []string
}
func (cfg *Config) FormatDSN() string {
var s strings.Builder
if len(cfg.Scheme) > 0 {
s.WriteString(cfg.Scheme + "://")
}
// [username[:password]@]
if len(cfg.Username) > 0 {
s.WriteString(cfg.Username)
if len(cfg.Password) > 0 {
s.WriteByte(':')
s.WriteString(url.PathEscape(cfg.Password))
}
s.WriteByte('@')
}
// [host:port]
if len(cfg.Host) > 0 {
s.WriteString(cfg.Host)
if len(cfg.Port) > 0 {
s.WriteByte(':')
s.WriteString(cfg.Port)
}
}
// /dbname
s.WriteByte('/')
s.WriteString(url.PathEscape(cfg.Database))
for i := 0; i < len(cfg.Params); i += 2 {
if i == 0 {
s.WriteString("?")
} else {
s.WriteString("&")
}
s.WriteString(cfg.Params[i])
s.WriteString("=")
s.WriteString(cfg.Params[i+1])
}
return s.String()
}
func ParseDSN(dsn string) (*Config, error) {
cfg := &Config{}
// [user[:password]@][net[(addr)]]/dbname[?param1=value1&paramN=valueN]
// Find last '/' that goes before dbname
foundSlash := false
for i := len(dsn) - 1; i >= 0; i-- {
if dsn[i] != '/' {
continue
}
foundSlash = true
var j, k int
// left part is empty if i <= 0
if i > 0 {
// Find the first ':' in dsn
for j = i; j >= 0; j-- {
if dsn[j] == ':' {
cfg.Scheme = dsn[0:j]
}
}
// [username[:password]@][host]
// Find the last '@' in dsn[:i]
for j = i; j >= 0; j-- {
if dsn[j] == '@' {
// username[:password]
// Find the second ':' in dsn[:j]
for k = 0; k < j; k++ {
if dsn[k] == ':' {
if cfg.Scheme == dsn[:k] {
continue
}
var err error
cfg.Password, err = url.PathUnescape(dsn[k+1 : j])
if err != nil {
return nil, err
}
break
}
}
cfg.Username = dsn[len(cfg.Scheme)+3 : k]
break
}
}
for k = j + 1; k < i; k++ {
if dsn[k] == ':' {
cfg.Host = dsn[j+1 : k]
cfg.Port = dsn[k+1 : i]
break
}
}
}
// dbname[?param1=value1&...&paramN=valueN]
// Find the first '?' in dsn[i+1:]
for j = i + 1; j < len(dsn); j++ {
if dsn[j] == '?' {
parts := strings.Split(dsn[j+1:], "&")
cfg.Params = make([]string, 0, len(parts)*2)
for _, p := range parts {
k, v, found := strings.Cut(p, "=")
if !found {
continue
}
cfg.Params = append(cfg.Params, k, v)
}
break
}
}
var err error
dbname := dsn[i+1 : j]
if cfg.Database, err = url.PathUnescape(dbname); err != nil {
return nil, fmt.Errorf("invalid dbname %q: %w", dbname, err)
}
break
}
if !foundSlash && len(dsn) > 0 {
return nil, ErrInvalidDSNNoSlash
}
return cfg, nil
}

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