Compare commits

...

183 Commits

Author SHA1 Message Date
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
211 changed files with 6108 additions and 2875 deletions

View File

@@ -11,9 +11,16 @@ updates:
directory: "/" directory: "/"
schedule: schedule:
interval: "daily" interval: "daily"
commit-message:
prefix: "chore"
include: "scope"
# Maintain dependencies for Golang # Maintain dependencies for Golang
- package-ecosystem: "gomod" - package-ecosystem: "gomod"
directory: "/" directory: "/"
schedule: schedule:
interval: "daily" interval: "daily"
commit-message:
prefix: "chore"
include: "scope"

20
.github/workflows/autoapprove.yml vendored Normal file
View File

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

21
.github/workflows/automerge.yml vendored Normal file
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,8 +1,9 @@
name: build name: build
on: on:
push: push:
branches: branches:
- master - master
- v3
jobs: jobs:
test: test:
name: test name: test
@@ -11,49 +12,33 @@ jobs:
- name: setup - name: setup
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.16 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@v2 uses: golangci/golangci-lint-action@v3.1.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.
version: v1.39 version: v1.30
# Optional: working directory, useful for monorepos # Optional: working directory, useful for monorepos
# working-directory: somedir # working-directory: somedir
# Optional: golangci-lint command line arguments. # Optional: golangci-lint command line arguments.

78
.github/workflows/codeql-analysis.yml vendored Normal file
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@v2
with:
go-version: 1.17
# Initializes the CodeQL tools for scanning.
- name: init
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: analyze
uses: github/codeql-action/analyze@v1

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.0
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

@@ -3,6 +3,7 @@ on:
pull_request: pull_request:
branches: branches:
- master - master
- v3
jobs: jobs:
test: test:
name: test name: test
@@ -11,49 +12,33 @@ jobs:
- name: setup - name: setup
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.16 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 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@v2 uses: golangci/golangci-lint-action@v3.1.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.
version: v1.39 version: v1.30
# Optional: working directory, useful for monorepos # Optional: working directory, useful for monorepos
# working-directory: somedir # working-directory: somedir
# Optional: golangci-lint command line arguments. # Optional: golangci-lint command line arguments.

View File

@@ -30,7 +30,7 @@ linters:
- gofmt - gofmt
- gofumpt - gofumpt
- goimports - goimports
- golint - revive
- gosec - gosec
- makezero - makezero
- misspell - misspell

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/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/go.unistack.org/micro/v3)](https://goreportcard.com/report/go.unistack.org/micro/v3) [![Slack](https://img.shields.io/static/v1?label=micro&message=slack&color=blueviolet)](https://unistack-org.slack.com/messages/default)
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,15 +1,16 @@
package api package api // import "go.unistack.org/micro/v3/api"
import ( import (
"errors" "errors"
"regexp" "regexp"
"strings" "strings"
"github.com/unistack-org/micro/v3/metadata" "go.unistack.org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/register" "go.unistack.org/micro/v3/register"
"github.com/unistack-org/micro/v3/server" "go.unistack.org/micro/v3/server"
) )
// nolint: revive
// Api interface // Api interface
type Api interface { type Api interface {
// Initialise options // Initialise options
@@ -125,14 +126,14 @@ func Validate(e *Endpoint) error {
ps := p[0] ps := p[0]
pe := p[len(p)-1] pe := p[len(p)-1]
if ps == '^' && pe == '$' { switch {
_, err := regexp.CompilePOSIX(p) case ps == '^' && pe == '$':
if err != nil { if _, err := regexp.CompilePOSIX(p); err != nil {
return err return err
} }
} else if ps == '^' && pe != '$' { case ps == '^' && pe != '$':
return errors.New("invalid path") return errors.New("invalid path")
} else if ps != '^' && pe == '$' { case ps != '^' && pe == '$':
return errors.New("invalid path") return errors.New("invalid path")
} }
} }

View File

@@ -1,5 +1,5 @@
// Package handler provides http handlers // Package handler provides http handlers
package handler package handler // import "go.unistack.org/micro/v3/api/handler"
import ( import (
"net/http" "net/http"

View File

@@ -1,9 +1,9 @@
package handler package handler
import ( import (
"github.com/unistack-org/micro/v3/api/router" "go.unistack.org/micro/v3/api/router"
"github.com/unistack-org/micro/v3/client" "go.unistack.org/micro/v3/client"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/logger"
) )
// DefaultMaxRecvSize specifies max recv size for handler // DefaultMaxRecvSize specifies max recv size for handler

View File

@@ -1,12 +1,12 @@
// Package grpc resolves a grpc service like /greeter.Say/Hello to greeter service // Package grpc resolves a grpc service like /greeter.Say/Hello to greeter service
package grpc package grpc // import "go.unistack.org/micro/v3/api/resolver/grpc"
import ( import (
"errors" "errors"
"net/http" "net/http"
"strings" "strings"
"github.com/unistack-org/micro/v3/api/resolver" "go.unistack.org/micro/v3/api/resolver"
) )
// Resolver struct // Resolver struct

View File

@@ -1,10 +1,10 @@
// Package host resolves using http host // Package host resolves using http host
package host package host // import "go.unistack.org/micro/v3/api/resolver/host"
import ( import (
"net/http" "net/http"
"github.com/unistack-org/micro/v3/api/resolver" "go.unistack.org/micro/v3/api/resolver"
) )
type hostResolver struct { type hostResolver struct {

View File

@@ -3,7 +3,7 @@ package resolver
import ( import (
"context" "context"
"github.com/unistack-org/micro/v3/register" "go.unistack.org/micro/v3/register"
) )
// Options struct // Options struct

View File

@@ -1,11 +1,11 @@
// Package path resolves using http path // Package path resolves using http path
package path package path // import "go.unistack.org/micro/v3/api/resolver/path"
import ( import (
"net/http" "net/http"
"strings" "strings"
"github.com/unistack-org/micro/v3/api/resolver" "go.unistack.org/micro/v3/api/resolver"
) )
// Resolver the path resolver // Resolver the path resolver

View File

@@ -1,5 +1,5 @@
// Package resolver resolves a http request to an endpoint // Package resolver resolves a http request to an endpoint
package resolver package resolver // import "go.unistack.org/micro/v3/api/resolver"
import ( import (
"errors" "errors"

View File

@@ -1,14 +1,14 @@
// Package subdomain is a resolver which uses the subdomain to determine the domain to route to. It // 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. // offloads the endpoint resolution to a child resolver which is provided in New.
package subdomain package subdomain // import "go.unistack.org/micro/v3/api/resolver/subdomain"
import ( import (
"net" "net"
"net/http" "net/http"
"strings" "strings"
"github.com/unistack-org/micro/v3/api/resolver" "go.unistack.org/micro/v3/api/resolver"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/logger"
"golang.org/x/net/publicsuffix" "golang.org/x/net/publicsuffix"
) )

View File

@@ -5,7 +5,7 @@ import (
"net/url" "net/url"
"testing" "testing"
"github.com/unistack-org/micro/v3/api/resolver/vpath" "go.unistack.org/micro/v3/api/resolver/vpath"
) )
func TestResolve(t *testing.T) { func TestResolve(t *testing.T) {

View File

@@ -1,5 +1,5 @@
// Package vpath resolves using http path and recognised versioned urls // Package vpath resolves using http path and recognised versioned urls
package vpath package vpath // import "go.unistack.org/micro/v3/api/resolver/vpath"
import ( import (
"errors" "errors"
@@ -7,7 +7,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/unistack-org/micro/v3/api/resolver" "go.unistack.org/micro/v3/api/resolver"
) )
// NewResolver creates new vpath api resolver // NewResolver creates new vpath api resolver

View File

@@ -3,10 +3,10 @@ package router
import ( import (
"context" "context"
"github.com/unistack-org/micro/v3/api/resolver" "go.unistack.org/micro/v3/api/resolver"
"github.com/unistack-org/micro/v3/api/resolver/vpath" "go.unistack.org/micro/v3/api/resolver/vpath"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/logger"
"github.com/unistack-org/micro/v3/register" "go.unistack.org/micro/v3/register"
) )
// Options holds the options for api router // Options holds the options for api router

View File

@@ -1,10 +1,10 @@
// Package router provides api service routing // Package router provides api service routing
package router package router // import "go.unistack.org/micro/v3/api/router"
import ( import (
"net/http" "net/http"
"github.com/unistack-org/micro/v3/api" "go.unistack.org/micro/v3/api"
) )
// DefaultRouter contains default router implementation // DefaultRouter contains default router implementation

View File

@@ -1,12 +1,12 @@
// Package auth provides authentication and authorization capability // Package auth provides authentication and authorization capability
package auth package auth // import "go.unistack.org/micro/v3/auth"
import ( import (
"context" "context"
"errors" "errors"
"time" "time"
"github.com/unistack-org/micro/v3/metadata" "go.unistack.org/micro/v3/metadata"
) )
const ( const (
@@ -55,7 +55,7 @@ type Auth interface {
type Account struct { type Account struct {
// Metadata any other associated metadata // Metadata any other associated metadata
Metadata metadata.Metadata `json:"metadata"` Metadata metadata.Metadata `json:"metadata"`
// ID of the account e.g. email or uuid // ID of the account e.g. email or id
ID string `json:"id"` ID string `json:"id"`
// Type of the account, e.g. service // Type of the account, e.g. service
Type string `json:"type"` Type string `json:"type"`

View File

@@ -1,7 +1,7 @@
package auth package auth
import ( import (
"github.com/google/uuid" "go.unistack.org/micro/v3/util/id"
) )
type noopAuth struct { type noopAuth struct {
@@ -61,11 +61,11 @@ func (n *noopAuth) Verify(acc *Account, res *Resource, opts ...VerifyOption) err
// Inspect a token // Inspect a token
func (n *noopAuth) Inspect(token string) (*Account, error) { func (n *noopAuth) Inspect(token string) (*Account, error) {
uid, err := uuid.NewRandom() id, err := id.New()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Account{ID: uid.String(), Issuer: n.Options().Issuer}, nil return &Account{ID: id, Issuer: n.Options().Issuer}, nil
} }
// Token generation using an account id and secret // Token generation using an account id and secret

View File

@@ -4,11 +4,11 @@ import (
"context" "context"
"time" "time"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/logger"
"github.com/unistack-org/micro/v3/metadata" "go.unistack.org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/meter" "go.unistack.org/micro/v3/meter"
"github.com/unistack-org/micro/v3/store" "go.unistack.org/micro/v3/store"
"github.com/unistack-org/micro/v3/tracer" "go.unistack.org/micro/v3/tracer"
) )
// NewOptions creates Options struct from slice of options // NewOptions creates Options struct from slice of options

View File

@@ -1,48 +1,132 @@
// 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/v3/broker"
import ( import (
"context" "context"
"errors"
"github.com/unistack-org/micro/v3/metadata" "go.unistack.org/micro/v3/metadata"
) )
// DefaultBroker default broker // DefaultBroker default memory broker
var DefaultBroker Broker = NewBroker() var DefaultBroker Broker = NewBroker()
var (
// ErrNotConnected returns when broker used but not connected yet
ErrNotConnected = errors.New("broker not connected")
// ErrDisconnected returns when broker disconnected
ErrDisconnected = errors.New("broker disconnected")
)
// Broker is an interface used for asynchronous messaging. // Broker is an interface used for asynchronous messaging.
type Broker interface { type Broker interface {
// Name returns broker instance name
Name() string Name() string
Init(...Option) error // Init initilize broker
Init(opts ...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 to broker topic
Publish(ctx context.Context, topic string, msg *Message, opts ...PublishOption) error
// Subscribe subscribes to topic message via handler
Subscribe(ctx context.Context, topic string, h Handler, opts ...SubscribeOption) (Subscriber, error)
// BatchPublish messages to broker with multiple topics
BatchPublish(ctx context.Context, msgs []*Message, opts ...PublishOption) error
// BatchSubscribe subscribes to topic messages via handler
BatchSubscribe(ctx context.Context, topic string, h BatchHandler, opts ...SubscribeOption) (Subscriber, error)
// String type of broker
String() string String() string
} }
// Handler is used to process messages via a subscription of a topic. // Handler is used to process messages via a subscription of a topic.
type Handler func(Event) error type Handler func(Event) error
// Events contains multiple events
type Events []Event
// Ack try to ack all events and return
func (evs Events) Ack() error {
var err error
for _, ev := range evs {
if err = ev.Ack(); err != nil {
return err
}
}
return nil
}
// SetError sets error on event
func (evs Events) SetError(err error) {
for _, ev := range evs {
ev.SetError(err)
}
}
// BatchHandler is used to process messages in batches via a subscription of a topic.
type BatchHandler func(Events) error
// Event is given to a subscription handler for processing // Event is given to a subscription handler for processing
type Event interface { type Event interface {
// Topic returns event topic
Topic() string Topic() string
// Message returns broker message
Message() *Message Message() *Message
// Ack acknowledge message
Ack() error Ack() error
// Error returns message error (like decoding errors or some other)
Error() error Error() error
// SetError set event processing error
SetError(err error)
}
// 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
} }
// Message is used to transfer data // Message is used to transfer data
type Message struct { type Message struct {
Header metadata.Metadata // contains message metadata // Header contains message metadata
Body []byte // contains message body Header metadata.Metadata
// Body contains message body
Body RawMessage
}
// NewMessage create broker message with topic filled
func NewMessage(topic string) *Message {
m := &Message{Header: metadata.New(2)}
m.Header.Set(metadata.HeaderTopic, topic)
return m
} }
// 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
} }

View File

@@ -2,18 +2,18 @@ package broker
import ( import (
"context" "context"
"errors"
"sync" "sync"
"github.com/google/uuid" "go.unistack.org/micro/v3/logger"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/metadata"
maddr "github.com/unistack-org/micro/v3/util/addr" maddr "go.unistack.org/micro/v3/util/addr"
mnet "github.com/unistack-org/micro/v3/util/net" "go.unistack.org/micro/v3/util/id"
"github.com/unistack-org/micro/v3/util/rand" mnet "go.unistack.org/micro/v3/util/net"
"go.unistack.org/micro/v3/util/rand"
) )
type memoryBroker struct { type memoryBroker struct {
Subscribers map[string][]*memorySubscriber subscribers map[string][]*memorySubscriber
addr string addr string
opts Options opts Options
sync.RWMutex sync.RWMutex
@@ -28,12 +28,13 @@ type memoryEvent struct {
} }
type memorySubscriber struct { type memorySubscriber struct {
ctx context.Context ctx context.Context
exit chan bool exit chan bool
handler Handler handler Handler
id string batchhandler BatchHandler
topic string id string
opts SubscribeOptions topic string
opts SubscribeOptions
} }
func (m *memoryBroker) Options() Options { func (m *memoryBroker) Options() Options {
@@ -77,7 +78,6 @@ func (m *memoryBroker) Disconnect(ctx context.Context) error {
} }
m.connected = false m.connected = false
return nil return nil
} }
@@ -89,73 +89,168 @@ func (m *memoryBroker) Init(opts ...Option) error {
} }
func (m *memoryBroker) Publish(ctx context.Context, topic string, msg *Message, opts ...PublishOption) error { func (m *memoryBroker) Publish(ctx context.Context, topic string, msg *Message, opts ...PublishOption) error {
msg.Header.Set(metadata.HeaderTopic, topic)
return m.publish(ctx, []*Message{msg}, opts...)
}
func (m *memoryBroker) BatchPublish(ctx context.Context, msgs []*Message, opts ...PublishOption) error {
return m.publish(ctx, msgs, opts...)
}
func (m *memoryBroker) publish(ctx context.Context, msgs []*Message, opts ...PublishOption) error {
m.RLock() m.RLock()
if !m.connected { if !m.connected {
m.RUnlock() m.RUnlock()
return errors.New("not connected") return ErrNotConnected
} }
subs, ok := m.Subscribers[topic]
m.RUnlock() m.RUnlock()
if !ok {
return nil
}
var v interface{} var err error
if m.opts.Codec != nil {
buf, err := m.opts.Codec.Marshal(msg) select {
if err != nil { case <-ctx.Done():
return err return ctx.Err()
default:
options := NewPublishOptions(opts...)
msgTopicMap := make(map[string]Events)
for _, v := range msgs {
p := &memoryEvent{opts: m.opts}
if m.opts.Codec == nil || options.BodyOnly {
p.topic, _ = v.Header.Get(metadata.HeaderTopic)
p.message = v.Body
} else {
p.topic, _ = v.Header.Get(metadata.HeaderTopic)
p.message, err = m.opts.Codec.Marshal(v)
if err != nil {
return err
}
}
msgTopicMap[p.topic] = append(msgTopicMap[p.topic], p)
} }
v = buf
} else {
v = msg
}
p := &memoryEvent{ beh := m.opts.BatchErrorHandler
topic: topic, eh := m.opts.ErrorHandler
message: v,
opts: m.opts,
}
eh := m.opts.ErrorHandler for t, ms := range msgTopicMap {
m.RLock()
for _, sub := range subs { subs, ok := m.subscribers[t]
if err := sub.handler(p); err != nil { m.RUnlock()
p.err = err if !ok {
if sub.opts.ErrorHandler != nil { continue
eh = sub.opts.ErrorHandler
} }
if eh != nil {
eh(p) for _, sub := range subs {
} else if m.opts.Logger.V(logger.ErrorLevel) { if sub.opts.BatchErrorHandler != nil {
m.opts.Logger.Error(m.opts.Context, err.Error()) beh = sub.opts.BatchErrorHandler
}
if sub.opts.ErrorHandler != nil {
eh = sub.opts.ErrorHandler
}
switch {
// batch processing
case sub.batchhandler != nil:
if err = sub.batchhandler(ms); err != nil {
ms.SetError(err)
if beh != nil {
_ = beh(ms)
} else if m.opts.Logger.V(logger.ErrorLevel) {
m.opts.Logger.Error(m.opts.Context, err.Error())
}
} else if sub.opts.AutoAck {
if err = ms.Ack(); err != nil {
m.opts.Logger.Errorf(m.opts.Context, "ack failed: %v", err)
}
}
// single processing
case sub.handler != nil:
for _, p := range ms {
if err = sub.handler(p); err != nil {
p.SetError(err)
if eh != nil {
_ = eh(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.Errorf(m.opts.Context, "ack failed: %v", err)
}
}
}
}
} }
continue
} }
} }
return nil return nil
} }
func (m *memoryBroker) Subscribe(ctx context.Context, topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) { func (m *memoryBroker) BatchSubscribe(ctx context.Context, topic string, handler BatchHandler, opts ...SubscribeOption) (Subscriber, error) {
m.RLock() m.RLock()
if !m.connected { if !m.connected {
m.RUnlock() m.RUnlock()
return nil, errors.New("not connected") return nil, ErrNotConnected
} }
m.RUnlock() m.RUnlock()
options := NewSubscribeOptions(opts...) sid, err := id.New()
id, err := uuid.NewRandom()
if err != nil { if err != nil {
return nil, err return nil, err
} }
options := NewSubscribeOptions(opts...)
sub := &memorySubscriber{
exit: make(chan bool, 1),
id: sid,
topic: topic,
batchhandler: 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) Subscribe(ctx context.Context, topic string, handler Handler, opts ...SubscribeOption) (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{ sub := &memorySubscriber{
exit: make(chan bool, 1), exit: make(chan bool, 1),
id: id.String(), id: sid,
topic: topic, topic: topic,
handler: handler, handler: handler,
opts: options, opts: options,
@@ -163,20 +258,20 @@ func (m *memoryBroker) Subscribe(ctx context.Context, topic string, handler Hand
} }
m.Lock() m.Lock()
m.Subscribers[topic] = append(m.Subscribers[topic], sub) m.subscribers[topic] = append(m.subscribers[topic], sub)
m.Unlock() m.Unlock()
go func() { go func() {
<-sub.exit <-sub.exit
m.Lock() m.Lock()
var newSubscribers []*memorySubscriber newSubscribers := make([]*memorySubscriber, 0, len(m.subscribers)-1)
for _, sb := range m.Subscribers[topic] { for _, sb := range m.subscribers[topic] {
if sb.id == sub.id { if sb.id == sub.id {
continue continue
} }
newSubscribers = append(newSubscribers, sb) newSubscribers = append(newSubscribers, sb)
} }
m.Subscribers[topic] = newSubscribers m.subscribers[topic] = newSubscribers
m.Unlock() m.Unlock()
}() }()
@@ -221,6 +316,10 @@ func (m *memoryEvent) Error() error {
return m.err return m.err
} }
func (m *memoryEvent) SetError(err error) {
m.err = err
}
func (m *memorySubscriber) Options() SubscribeOptions { func (m *memorySubscriber) Options() SubscribeOptions {
return m.opts return m.opts
} }
@@ -238,6 +337,6 @@ func (m *memorySubscriber) Unsubscribe(ctx context.Context) error {
func NewBroker(opts ...Option) Broker { func NewBroker(opts ...Option) Broker {
return &memoryBroker{ return &memoryBroker{
opts: NewOptions(opts...), opts: NewOptions(opts...),
Subscribers: make(map[string][]*memorySubscriber), subscribers: make(map[string][]*memorySubscriber),
} }
} }

View File

@@ -4,8 +4,56 @@ import (
"context" "context"
"fmt" "fmt"
"testing" "testing"
"go.unistack.org/micro/v3/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 Events) error {
return evts.Ack()
}
sub, err := b.BatchSubscribe(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 := &Message{
Header: map[string]string{
metadata.HeaderTopic: topic,
"foo": "bar",
"id": fmt.Sprintf("%d", i),
},
Body: []byte(`"hello world"`),
}
msgs = append(msgs, message)
}
if err := b.BatchPublish(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) { func TestMemoryBroker(t *testing.T) {
b := NewBroker() b := NewBroker()
ctx := context.Background() ctx := context.Background()
@@ -26,20 +74,27 @@ func TestMemoryBroker(t *testing.T) {
t.Fatalf("Unexpected error subscribing %v", err) t.Fatalf("Unexpected error subscribing %v", err)
} }
msgs := make([]*Message, 0, count)
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
message := &Message{ message := &Message{
Header: map[string]string{ Header: map[string]string{
"foo": "bar", metadata.HeaderTopic: topic,
"id": fmt.Sprintf("%d", i), "foo": "bar",
"id": fmt.Sprintf("%d", i),
}, },
Body: []byte(`hello world`), Body: []byte(`"hello world"`),
} }
msgs = append(msgs, message)
if err := b.Publish(ctx, topic, message); err != nil { if err := b.Publish(ctx, topic, message); err != nil {
t.Fatalf("Unexpected error publishing %d", i) t.Fatalf("Unexpected error publishing %d err: %v", i, err)
} }
} }
if err := b.BatchPublish(ctx, msgs); err != nil {
t.Fatalf("Unexpected error publishing %v", err)
}
if err := sub.Unsubscribe(ctx); err != nil { if err := sub.Unsubscribe(ctx); err != nil {
t.Fatalf("Unexpected error unsubscribing from %s: %v", topic, err) t.Fatalf("Unexpected error unsubscribing from %s: %v", topic, err)
} }

View File

@@ -3,12 +3,13 @@ package broker
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
"time"
"github.com/unistack-org/micro/v3/codec" "go.unistack.org/micro/v3/codec"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/logger"
"github.com/unistack-org/micro/v3/meter" "go.unistack.org/micro/v3/meter"
"github.com/unistack-org/micro/v3/register" "go.unistack.org/micro/v3/register"
"github.com/unistack-org/micro/v3/tracer" "go.unistack.org/micro/v3/tracer"
) )
// Options struct // Options struct
@@ -29,6 +30,8 @@ type Options struct {
TLSConfig *tls.Config TLSConfig *tls.Config
// ErrorHandler used when broker can't unmarshal incoming message // ErrorHandler used when broker can't unmarshal incoming message
ErrorHandler Handler ErrorHandler Handler
// BatchErrorHandler used when broker can't unmashal incoming messages
BatchErrorHandler BatchHandler
// Name holds the broker name // Name holds the broker name
Name string Name string
// Addrs holds the broker address // Addrs holds the broker address
@@ -71,11 +74,9 @@ func NewPublishOptions(opts ...PublishOption) 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
} }
@@ -85,12 +86,18 @@ type SubscribeOptions struct {
Context context.Context Context context.Context
// ErrorHandler used when broker can't unmarshal incoming message // ErrorHandler used when broker can't unmarshal incoming message
ErrorHandler Handler ErrorHandler Handler
// BatchErrorHandler used when broker can't unmashal incoming messages
BatchErrorHandler BatchHandler
// Group holds consumer group // Group holds consumer group
Group string Group string
// AutoAck flag specifies auto ack of incoming message when no error happens // AutoAck flag specifies auto ack of incoming message when no error happens
AutoAck bool AutoAck bool
// BodyOnly flag specifies that message contains only body bytes without header // BodyOnly flag specifies that message contains only body bytes without header
BodyOnly bool BodyOnly bool
// BatchSize flag specifies max batch size
BatchSize int
// BatchWait flag specifies max wait time for batch filling
BatchWait time.Duration
} }
// Option func // Option func
@@ -113,23 +120,6 @@ func PublishContext(ctx context.Context) PublishOption {
} }
} }
// 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 // Addrs sets the host addresses to be used by the broker
func Addrs(addrs ...string) Option { func Addrs(addrs ...string) Option {
return func(o *Options) { return func(o *Options) {
@@ -145,28 +135,6 @@ func Codec(c codec.Codec) Option {
} }
} }
// 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 Handler) Option {
@@ -175,6 +143,14 @@ func ErrorHandler(h Handler) Option {
} }
} }
// BatchErrorHandler will catch all broker errors that cant be handled
// in normal way, for example Codec errors
func BatchErrorHandler(h BatchHandler) Option {
return func(o *Options) {
o.BatchErrorHandler = h
}
}
// SubscribeErrorHandler will catch all broker errors that cant be handled // SubscribeErrorHandler will catch all broker errors that cant be handled
// in normal way, for example Codec errors // in normal way, for example Codec errors
func SubscribeErrorHandler(h Handler) SubscribeOption { func SubscribeErrorHandler(h Handler) SubscribeOption {
@@ -183,6 +159,14 @@ func SubscribeErrorHandler(h Handler) SubscribeOption {
} }
} }
// SubscribeBatchErrorHandler will catch all broker errors that cant be handled
// in normal way, for example Codec errors
func SubscribeBatchErrorHandler(h BatchHandler) SubscribeOption {
return func(o *SubscribeOptions) {
o.BatchErrorHandler = h
}
}
// Queue sets the subscribers queue // Queue sets the subscribers queue
// Deprecated // Deprecated
func Queue(name string) SubscribeOption { func Queue(name string) SubscribeOption {
@@ -246,3 +230,55 @@ func SubscribeContext(ctx context.Context) SubscribeOption {
o.Context = ctx o.Context = ctx
} }
} }
// DisableAutoAck disables auto ack
// Deprecated
func DisableAutoAck() SubscribeOption {
return func(o *SubscribeOptions) {
o.AutoAck = false
}
}
// SubscribeAutoAck contol 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
}
}
// SubscribeBatchSize specifies max batch size
func SubscribeBatchSize(n int) SubscribeOption {
return func(o *SubscribeOptions) {
o.BatchSize = n
}
}
// SubscribeBatchWait specifies max batch wait time
func SubscribeBatchWait(td time.Duration) SubscribeOption {
return func(o *SubscribeOptions) {
o.BatchWait = td
}
}
// 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
}

View File

@@ -1,5 +1,5 @@
// Package build is for building source into a package // Package build is for building source into a package
package build package build // import "go.unistack.org/micro/v3/build"
// Build is an interface for building packages // Build is an interface for building packages
type Build interface { type Build interface {

View File

@@ -4,7 +4,7 @@ import (
"context" "context"
"time" "time"
"github.com/unistack-org/micro/v3/util/backoff" "go.unistack.org/micro/v3/util/backoff"
) )
// BackoffFunc is the backoff call func // BackoffFunc is the backoff call func

View File

@@ -1,12 +1,12 @@
// 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/v3/client"
import ( import (
"context" "context"
"time" "time"
"github.com/unistack-org/micro/v3/codec" "go.unistack.org/micro/v3/codec"
"github.com/unistack-org/micro/v3/metadata" "go.unistack.org/micro/v3/metadata"
) )
var ( var (
@@ -40,6 +40,7 @@ type Client interface {
Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error
Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error)
Publish(ctx context.Context, msg Message, opts ...PublishOption) error Publish(ctx context.Context, msg Message, opts ...PublishOption) error
BatchPublish(ctx context.Context, msg []Message, opts ...PublishOption) error
String() string String() string
} }
@@ -48,6 +49,7 @@ type Message interface {
Topic() string Topic() string
Payload() interface{} Payload() interface{}
ContentType() string ContentType() string
Metadata() metadata.Metadata
} }
// 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
@@ -90,10 +92,16 @@ type Stream interface {
Send(msg interface{}) error Send(msg interface{}) error
// Recv will decode and read a response // Recv will decode and read a response
Recv(msg 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 // Option used by the Client

View File

@@ -4,15 +4,15 @@ import (
"context" "context"
"sort" "sort"
"github.com/unistack-org/micro/v3/errors" "go.unistack.org/micro/v3/errors"
"github.com/unistack-org/micro/v3/router" "go.unistack.org/micro/v3/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

@@ -3,10 +3,10 @@ package client
import ( import (
"context" "context"
"github.com/unistack-org/micro/v3/broker" "go.unistack.org/micro/v3/broker"
"github.com/unistack-org/micro/v3/codec" "go.unistack.org/micro/v3/codec"
"github.com/unistack-org/micro/v3/errors" "go.unistack.org/micro/v3/errors"
"github.com/unistack-org/micro/v3/metadata" "go.unistack.org/micro/v3/metadata"
) )
// DefaultCodecs will be used to encode/decode data // DefaultCodecs will be used to encode/decode data
@@ -119,6 +119,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
} }
@@ -127,6 +135,10 @@ func (n *noopStream) Close() error {
return nil return nil
} }
func (n *noopStream) CloseSend() error {
return nil
}
func (n *noopMessage) Topic() string { func (n *noopMessage) Topic() string {
return n.topic return n.topic
} }
@@ -139,6 +151,10 @@ func (n *noopMessage) ContentType() string {
return n.opts.ContentType return n.opts.ContentType
} }
func (n *noopMessage) Metadata() metadata.Metadata {
return n.opts.Metadata
}
func (n *noopClient) newCodec(contentType string) (codec.Codec, error) { func (n *noopClient) newCodec(contentType string) (codec.Codec, error) {
if cf, ok := n.opts.Codecs[contentType]; ok { if cf, ok := n.opts.Codecs[contentType]; ok {
return cf, nil return cf, nil
@@ -173,7 +189,7 @@ func (n *noopClient) NewRequest(service, endpoint string, req interface{}, opts
} }
func (n *noopClient) NewMessage(topic string, msg interface{}, opts ...MessageOption) Message { func (n *noopClient) NewMessage(topic string, msg interface{}, opts ...MessageOption) Message {
options := NewMessageOptions(opts...) options := NewMessageOptions(append([]MessageOption{MessageContentType(n.opts.ContentType)}, opts...)...)
return &noopMessage{topic: topic, payload: msg, opts: options} return &noopMessage{topic: topic, payload: msg, opts: options}
} }
@@ -181,47 +197,59 @@ func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption
return &noopStream{}, nil return &noopStream{}, nil
} }
func (n *noopClient) Publish(ctx context.Context, p Message, opts ...PublishOption) error { func (n *noopClient) BatchPublish(ctx context.Context, ps []Message, opts ...PublishOption) error {
var body []byte return n.publish(ctx, ps, opts...)
}
func (n *noopClient) Publish(ctx context.Context, p Message, opts ...PublishOption) error {
return n.publish(ctx, []Message{p}, opts...)
}
func (n *noopClient) publish(ctx context.Context, ps []Message, opts ...PublishOption) error {
options := NewPublishOptions(opts...) options := NewPublishOptions(opts...)
md, ok := metadata.FromOutgoingContext(ctx) msgs := make([]*broker.Message, 0, len(ps))
if !ok {
md = metadata.New(0)
}
md["Content-Type"] = p.ContentType()
md["Micro-Topic"] = p.Topic()
// passed in raw data for _, p := range ps {
if d, ok := p.Payload().(*codec.Frame); ok { md, ok := metadata.FromOutgoingContext(ctx)
body = d.Data if !ok {
} else { md = metadata.New(0)
// use codec for payload }
cf, err := n.newCodec(p.ContentType()) md[metadata.HeaderContentType] = p.ContentType()
if err != nil {
return errors.InternalServerError("go.micro.client", err.Error()) topic := p.Topic()
// get the exchange
if len(options.Exchange) > 0 {
topic = options.Exchange
} }
// set the body md[metadata.HeaderTopic] = topic
b, err := cf.Marshal(p.Payload())
if err != nil { var body []byte
return errors.InternalServerError("go.micro.client", err.Error())
// 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
} }
body = b
msgs = append(msgs, &broker.Message{Header: md, Body: body})
} }
topic := p.Topic() return n.opts.Broker.BatchPublish(ctx, msgs,
// 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), broker.PublishContext(options.Context),
broker.PublishBodyOnly(options.BodyOnly), broker.PublishBodyOnly(options.BodyOnly),
) )

View File

@@ -5,20 +5,23 @@ import (
"crypto/tls" "crypto/tls"
"time" "time"
"github.com/unistack-org/micro/v3/broker" "go.unistack.org/micro/v3/broker"
"github.com/unistack-org/micro/v3/codec" "go.unistack.org/micro/v3/codec"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/logger"
"github.com/unistack-org/micro/v3/meter" "go.unistack.org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/network/transport" "go.unistack.org/micro/v3/meter"
"github.com/unistack-org/micro/v3/register" "go.unistack.org/micro/v3/network/transport"
"github.com/unistack-org/micro/v3/router" "go.unistack.org/micro/v3/register"
"github.com/unistack-org/micro/v3/selector" "go.unistack.org/micro/v3/router"
"github.com/unistack-org/micro/v3/selector/random" "go.unistack.org/micro/v3/selector"
"github.com/unistack-org/micro/v3/tracer" "go.unistack.org/micro/v3/selector/random"
"go.unistack.org/micro/v3/tracer"
) )
// Options holds client options // Options holds client options
type Options struct { type Options struct {
// Transport used for transfer messages
Transport transport.Transport
// Selector used to select needed address // Selector used to select needed address
Selector selector.Selector Selector selector.Selector
// Logger used to log messages // Logger used to log messages
@@ -29,18 +32,16 @@ type Options struct {
Broker broker.Broker Broker broker.Broker
// Meter used for metrics // Meter used for metrics
Meter meter.Meter Meter meter.Meter
// Router used to get route
Router router.Router
// Transport used for transfer messages
Transport transport.Transport
// Context is used for external options // Context is used for external options
Context context.Context Context context.Context
// Lookup func used to get destination addr // Router used to get route
Lookup LookupFunc Router router.Router
// Codecs map
Codecs map[string]codec.Codec
// TLSConfig specifies tls.Config for secure connection // TLSConfig specifies tls.Config for secure connection
TLSConfig *tls.Config 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 is used for proxy requests
Proxy string Proxy string
// ContentType is used to select codec // ContentType is used to select codec
@@ -68,12 +69,12 @@ func NewCallOptions(opts ...CallOption) CallOptions {
// CallOptions holds client call options // CallOptions holds client call options
type CallOptions struct { type CallOptions struct {
// Router used for route
Router router.Router
// Selector selects addr // Selector selects addr
Selector selector.Selector Selector selector.Selector
// Context used for deadline // Context used for deadline
Context context.Context Context context.Context
// Router used for route
Router router.Router
// Retry func used for retries // Retry func used for retries
Retry RetryFunc Retry RetryFunc
// Backoff func used for backoff when retry // Backoff func used for backoff when retry
@@ -82,22 +83,22 @@ type CallOptions struct {
Network string Network string
// Content-Type // Content-Type
ContentType string ContentType string
// CallWrappers call wrappers // AuthToken string
CallWrappers []CallWrapper AuthToken string
// SelectOptions selector options
SelectOptions []selector.SelectOption
// Address specifies static addr list // Address specifies static addr list
Address []string Address []string
// Retries specifies retries num // SelectOptions selector options
Retries int SelectOptions []selector.SelectOption
// CallWrappers call wrappers
CallWrappers []CallWrapper
// StreamTimeout stream timeout // StreamTimeout stream timeout
StreamTimeout time.Duration StreamTimeout time.Duration
// RequestTimeout request timeout // RequestTimeout request timeout
RequestTimeout time.Duration RequestTimeout time.Duration
// DialTimeout dial timeout // DialTimeout dial timeout
DialTimeout time.Duration DialTimeout time.Duration
// AuthToken flag // Retries specifies retries num
AuthToken bool Retries int
} }
// Context pass context to client // Context pass context to client
@@ -118,17 +119,17 @@ func NewPublishOptions(opts ...PublishOption) PublishOptions {
// PublishOptions holds publish options // PublishOptions holds publish options
type PublishOptions struct { type PublishOptions struct {
// BodyOnly will publish only message body
BodyOnly bool
// Context used for external options // Context used for external options
Context context.Context Context context.Context
// Exchange topic exchange name // Exchange topic exchange name
Exchange string Exchange string
// BodyOnly will publish only message body
BodyOnly bool
} }
// NewMessageOptions creates message options struct // NewMessageOptions creates message options struct
func NewMessageOptions(opts ...MessageOption) MessageOptions { func NewMessageOptions(opts ...MessageOption) MessageOptions {
options := MessageOptions{} options := MessageOptions{Metadata: metadata.New(1)}
for _, o := range opts { for _, o := range opts {
o(&options) o(&options)
} }
@@ -137,7 +138,10 @@ func NewMessageOptions(opts ...MessageOption) MessageOptions {
// MessageOptions holds client message options // MessageOptions holds client message options
type MessageOptions struct { type MessageOptions struct {
// Metadata additional metadata
Metadata metadata.Metadata
// ContentType specify content-type of message // ContentType specify content-type of message
// deprecated
ContentType string ContentType string
} }
@@ -267,7 +271,7 @@ func Transport(t transport.Transport) Option {
func Register(r register.Register) Option { func Register(r register.Register) Option {
return func(o *Options) { return func(o *Options) {
if o.Router != nil { if o.Router != nil {
o.Router.Init(router.Register(r)) _ = o.Router.Init(router.Register(r))
} }
} }
} }
@@ -331,7 +335,7 @@ func TLSConfig(t *tls.Config) Option {
// already set. Required for Init call below. // already set. Required for Init call below.
// set the transport tls // set the transport tls
o.Transport.Init( _ = o.Transport.Init(
transport.TLSConfig(t), transport.TLSConfig(t),
) )
} }
@@ -373,19 +377,35 @@ func DialTimeout(d time.Duration) Option {
} }
// WithExchange sets the exchange to route a message through // WithExchange sets the exchange to route a message through
// Deprecated
func WithExchange(e string) PublishOption { func WithExchange(e string) PublishOption {
return func(o *PublishOptions) { return func(o *PublishOptions) {
o.Exchange = e o.Exchange = e
} }
} }
// PublishExchange sets the exchange to route a message through
func PublishExchange(e string) PublishOption {
return func(o *PublishOptions) {
o.Exchange = e
}
}
// WithBodyOnly publish only message body // WithBodyOnly publish only message body
// DERECATED
func WithBodyOnly(b bool) PublishOption { func WithBodyOnly(b bool) PublishOption {
return func(o *PublishOptions) { return func(o *PublishOptions) {
o.BodyOnly = b o.BodyOnly = b
} }
} }
// PublishBodyOnly publish only message body
func PublishBodyOnly(b bool) PublishOption {
return func(o *PublishOptions) {
o.BodyOnly = b
}
}
// PublishContext sets the context in publish options // PublishContext sets the context in publish options
func PublishContext(ctx context.Context) PublishOption { func PublishContext(ctx context.Context) PublishOption {
return func(o *PublishOptions) { return func(o *PublishOptions) {
@@ -463,9 +483,9 @@ func WithDialTimeout(d time.Duration) CallOption {
// WithAuthToken is a CallOption which overrides the // 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 WithAuthToken(t string) CallOption {
return func(o *CallOptions) { return func(o *CallOptions) {
o.AuthToken = true o.AuthToken = t
} }
} }
@@ -498,12 +518,29 @@ func WithSelectOptions(sops ...selector.SelectOption) CallOption {
} }
// WithMessageContentType sets the message content type // WithMessageContentType sets the message content type
// Deprecated
func WithMessageContentType(ct string) MessageOption { func WithMessageContentType(ct string) MessageOption {
return func(o *MessageOptions) { return func(o *MessageOptions) {
o.Metadata.Set(metadata.HeaderContentType, ct)
o.ContentType = ct o.ContentType = ct
} }
} }
// MessageContentType sets the message content type
func MessageContentType(ct string) MessageOption {
return func(o *MessageOptions) {
o.Metadata.Set(metadata.HeaderContentType, ct)
o.ContentType = ct
}
}
// MessageMetadata sets the message metadata
func MessageMetadata(k, v string) MessageOption {
return func(o *MessageOptions) {
o.Metadata.Set(k, v)
}
}
// StreamingRequest specifies that request is streaming // StreamingRequest specifies that request is streaming
func StreamingRequest(b bool) RequestOption { func StreamingRequest(b bool) RequestOption {
return func(o *RequestOptions) { return func(o *RequestOptions) {

View File

@@ -3,7 +3,7 @@ package client
import ( import (
"context" "context"
"github.com/unistack-org/micro/v3/errors" "go.unistack.org/micro/v3/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
@@ -20,7 +20,7 @@ func RetryNever(ctx context.Context, req Request, retryCount int, err error) (bo
} }
// RetryOnError retries a request on a 500 or timeout error // RetryOnError retries a request on a 500 or 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
} }

View File

@@ -1,7 +1,7 @@
package client package client
import ( import (
"github.com/unistack-org/micro/v3/codec" "go.unistack.org/micro/v3/codec"
) )
type testRequest struct { type testRequest struct {

View File

@@ -1,11 +1,11 @@
// 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/v3/codec"
import ( import (
"errors" "errors"
"io" "io"
"github.com/unistack-org/micro/v3/metadata" "go.unistack.org/micro/v3/metadata"
) )
// Message types // Message types
@@ -25,7 +25,7 @@ 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 is the global default codec // DefaultCodec is the global default codec
DefaultCodec Codec = NewCodec() DefaultCodec Codec = NewCodec()
// DefaultTagName specifies struct tag name to control codec Marshal/Unmarshal // DefaultTagName specifies struct tag name to control codec Marshal/Unmarshal
@@ -41,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
} }
@@ -58,7 +58,7 @@ type Message struct {
Method string Method string
Endpoint string Endpoint string
Error string Error string
Id string ID string
Body []byte Body []byte
Type MessageType Type MessageType
} }
@@ -67,3 +67,20 @@ type Message struct {
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
}

34
codec/context.go Normal file
View File

@@ -0,0 +1,34 @@
package codec
import (
"context"
)
type codecKey struct{}
// FromContext returns codec from context
func FromContext(ctx context.Context) (Codec, bool) {
if ctx == nil {
return nil, false
}
c, ok := ctx.Value(codecKey{}).(Codec)
return c, ok
}
// NewContext put codec in context
func NewContext(ctx context.Context, c Codec) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, codecKey{}, 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)
}
}

View File

@@ -4,3 +4,42 @@ package codec
type Frame struct { type Frame struct {
Data []byte 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
}

View File

@@ -17,7 +17,7 @@ syntax = "proto3";
package micro.codec; package micro.codec;
option cc_enable_arenas = true; option cc_enable_arenas = true;
option go_package = "github.com/unistack-org/micro/v3/codec;codec"; option go_package = "go.unistack.org/micro/v3/codec;codec";
option java_multiple_files = true; option java_multiple_files = true;
option java_outer_classname = "MicroCodec"; option java_outer_classname = "MicroCodec";
option java_package = "micro.codec"; option java_package = "micro.codec";

View File

@@ -5,7 +5,9 @@ import (
"io" "io"
) )
type noopCodec struct{} type noopCodec struct {
opts Options
}
func (c *noopCodec) ReadHeader(conn io.Reader, m *Message, t MessageType) error { func (c *noopCodec) ReadHeader(conn io.Reader, m *Message, t MessageType) error {
return nil return nil
@@ -69,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
} }
@@ -96,7 +98,7 @@ func (c *noopCodec) Marshal(v interface{}) ([]byte, error) {
return json.Marshal(v) return json.Marshal(v)
} }
func (c *noopCodec) Unmarshal(d []byte, v interface{}) error { func (c *noopCodec) Unmarshal(d []byte, v interface{}, opts ...Option) error {
if v == nil { if v == nil {
return nil return nil
} }

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/v3/logger"
"go.unistack.org/micro/v3/meter"
"go.unistack.org/micro/v3/tracer"
) )
// Option func // Option func
@@ -17,6 +19,10 @@ type Options struct {
Logger logger.Logger Logger logger.Logger
// Tracer used for tracing // Tracer used for tracing
Tracer tracer.Tracer 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 specifies max messages size that reads by codec
MaxMsgSize int MaxMsgSize int
} }
@@ -28,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) {
@@ -52,10 +65,12 @@ func Meter(m meter.Meter) Option {
// NewOptions returns new options // 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,13 +1,25 @@
// 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/v3/config"
import ( import (
"context" "context"
"errors" "errors"
"reflect"
"time"
) )
type Validator interface {
Validate() error
}
// DefaultConfig default config // DefaultConfig default config
var DefaultConfig Config = NewConfig() 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
@@ -16,6 +28,8 @@ 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
@@ -30,28 +44,132 @@ type Config interface {
Load(context.Context, ...LoadOption) error Load(context.Context, ...LoadOption) error
// Save config to sources // Save config to sources
Save(context.Context, ...SaveOption) error Save(context.Context, ...SaveOption) error
// Watch a value for changes // Watch a config for changes
//Watch(context.Context) (Watcher, error) Watch(context.Context, ...WatchOption) (Watcher, error)
// String returns config type name // 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
Next() (map[string]interface{}, error)
// Stop stops watcher
Stop() error 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 ...LoadOption) 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 (
// DefaultAfterLoad default func that runs after config load
DefaultAfterLoad = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().AfterLoad {
if err := fn(ctx, c); err != nil {
c.Options().Logger.Errorf(ctx, "%s AfterLoad err: %v", c.String(), err)
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 err := fn(ctx, c); err != nil {
c.Options().Logger.Errorf(ctx, "%s AfterSave err: %v", c.String(), err)
if !c.Options().AllowFail {
return err
}
}
}
return nil
}
// DefaultBeforeLoad default func that runs before config load
DefaultBeforeLoad = func(ctx context.Context, c Config) error {
for _, fn := range c.Options().BeforeLoad {
if err := fn(ctx, c); err != nil {
c.Options().Logger.Errorf(ctx, "%s BeforeLoad err: %v", c.String(), err)
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 err := fn(ctx, c); err != nil {
c.Options().Logger.Errorf(ctx, "%s BeforeSave err: %v", c.String(), err)
if !c.Options().AllowFail {
return err
}
}
}
return nil
}
)

View File

@@ -32,3 +32,33 @@ func SetOption(k, v interface{}) Option {
o.Context = context.WithValue(o.Context, k, v) o.Context = context.WithValue(o.Context, k, v)
} }
} }
// SetSaveOption returns a function to setup a context with given value
func SetSaveOption(k, v interface{}) SaveOption {
return func(o *SaveOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}
// SetLoadOption returns a function to setup a context with given value
func SetLoadOption(k, v interface{}) LoadOption {
return func(o *LoadOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}
// SetWatchOption returns a function to setup a context with given value
func SetWatchOption(k, v interface{}) WatchOption {
return func(o *WatchOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}

View File

@@ -7,7 +7,7 @@ import (
"strings" "strings"
"github.com/imdario/mergo" "github.com/imdario/mergo"
rutil "github.com/unistack-org/micro/v3/util/reflect" rutil "go.unistack.org/micro/v3/util/reflect"
) )
type defaultConfig struct { type defaultConfig struct {
@@ -26,10 +26,8 @@ func (c *defaultConfig) Init(opts ...Option) error {
} }
func (c *defaultConfig) Load(ctx context.Context, opts ...LoadOption) error { func (c *defaultConfig) Load(ctx context.Context, opts ...LoadOption) error {
for _, fn := range c.opts.BeforeLoad { if err := DefaultBeforeLoad(ctx, c); err != nil {
if err := fn(ctx, c); err != nil && !c.opts.AllowFail { return err
return err
}
} }
options := NewLoadOptions(opts...) options := NewLoadOptions(opts...)
@@ -41,29 +39,39 @@ func (c *defaultConfig) Load(ctx context.Context, opts ...LoadOption) error {
mopts = append(mopts, mergo.WithAppendSlice) mopts = append(mopts, mergo.WithAppendSlice)
} }
src, err := rutil.Zero(c.opts.Struct) dst := c.opts.Struct
if err == nil { if options.Struct != nil {
valueOf := reflect.ValueOf(src) dst = options.Struct
if err = c.fillValues(valueOf); err == nil {
err = mergo.Merge(c.opts.Struct, src, mopts...)
}
} }
if err != nil && !c.opts.AllowFail { src, err := rutil.Zero(dst)
return err if err != nil {
} if !c.opts.AllowFail {
for _, fn := range c.opts.AfterLoad {
if err := fn(ctx, c); err != nil && !c.opts.AllowFail {
return err return err
} }
return DefaultAfterLoad(ctx, c)
}
if err = fillValues(reflect.ValueOf(src), c.opts.StructTag); err == nil {
err = mergo.Merge(dst, src, mopts...)
}
if err != nil {
c.opts.Logger.Errorf(ctx, "default load error: %v", err)
if !c.opts.AllowFail {
return err
}
}
if err := DefaultAfterLoad(ctx, c); err != nil {
return err
} }
return nil return nil
} }
//nolint:gocyclo //nolint:gocyclo
func (c *defaultConfig) fillValue(value reflect.Value, val string) error { func fillValue(value reflect.Value, val string) error {
if !rutil.IsEmpty(value) { if !rutil.IsEmpty(value) {
return nil return nil
} }
@@ -80,10 +88,10 @@ func (c *defaultConfig) fillValue(value reflect.Value, val string) error {
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(mkey, kv[0]); err != nil { if err := fillValue(mkey, kv[0]); err != nil {
return err return err
} }
if err := c.fillValue(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)
@@ -93,7 +101,7 @@ func (c *defaultConfig) fillValue(value reflect.Value, val string) error {
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(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)
@@ -182,7 +190,7 @@ func (c *defaultConfig) fillValue(value reflect.Value, val string) error {
return nil return nil
} }
func (c *defaultConfig) fillValues(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 {
@@ -209,7 +217,7 @@ func (c *defaultConfig) fillValues(valueOf reflect.Value) error {
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(value); err != nil { if err := fillValues(value, tname); err != nil {
return err return err
} }
continue continue
@@ -223,17 +231,17 @@ func (c *defaultConfig) fillValues(valueOf reflect.Value) error {
value.Set(reflect.New(value.Type().Elem())) value.Set(reflect.New(value.Type().Elem()))
} }
value = value.Elem() value = value.Elem()
if err := c.fillValues(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(value, tag); err != nil { if err := fillValue(value, tag); err != nil {
return err return err
} }
} }
@@ -242,16 +250,12 @@ func (c *defaultConfig) fillValues(valueOf reflect.Value) error {
} }
func (c *defaultConfig) Save(ctx context.Context, opts ...SaveOption) error { func (c *defaultConfig) Save(ctx context.Context, opts ...SaveOption) error {
for _, fn := range c.opts.BeforeSave { if err := DefaultBeforeSave(ctx, c); err != nil {
if err := fn(ctx, c); err != nil && !c.opts.AllowFail { return err
return err
}
} }
for _, fn := range c.opts.AfterSave { if err := DefaultAfterSave(ctx, c); err != nil {
if err := fn(ctx, c); err != nil && !c.opts.AllowFail { return err
return err
}
} }
return nil return nil
@@ -265,6 +269,10 @@ func (c *defaultConfig) Name() string {
return c.opts.Name return c.opts.Name
} }
func (c *defaultConfig) Watch(ctx context.Context, opts ...WatchOption) (Watcher, error) {
return nil, ErrWatcherNotImplemented
}
// NewConfig returns new default config source // NewConfig returns new default config source
func NewConfig(opts ...Option) Config { func NewConfig(opts ...Option) Config {
options := NewOptions(opts...) options := NewOptions(opts...)

View File

@@ -5,33 +5,49 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/unistack-org/micro/v3/config" "go.unistack.org/micro/v3/config"
) )
type Cfg struct { type cfg struct {
StringValue string `default:"string_value"` StringValue string `default:"string_value"`
IgnoreValue string `json:"-"` IgnoreValue string `json:"-"`
StructValue struct { StructValue *cfgStructValue
StringValue string `default:"string_value"` IntValue int `default:"99"`
}
type cfgStructValue struct {
StringValue string `default:"string_value"`
}
func (c *cfg) Validate() error {
if c.IntValue != 10 {
return fmt.Errorf("invalid IntValue %d != %d", 10, c.IntValue)
} }
IntValue int `default:"99"` 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 {
nconf, 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())
} }
nconf.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 {
nconf, 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())
} }
nconf.StringValue = "after_load" nconf.StringValue = "after_load"
return nil return nil
@@ -47,6 +63,22 @@ 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")
} }
_ = conf
t.Logf("%#+v\n", 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)
}
} }

View File

@@ -2,11 +2,12 @@ package config
import ( import (
"context" "context"
"time"
"github.com/unistack-org/micro/v3/codec" "go.unistack.org/micro/v3/codec"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/logger"
"github.com/unistack-org/micro/v3/meter" "go.unistack.org/micro/v3/meter"
"github.com/unistack-org/micro/v3/tracer" "go.unistack.org/micro/v3/tracer"
) )
// Options hold the config options // Options hold the config options
@@ -62,10 +63,13 @@ type LoadOption func(o *LoadOptions)
// LoadOptions struct // LoadOptions struct
type LoadOptions struct { type LoadOptions struct {
Struct interface{}
Override bool Override bool
Append bool Append bool
Context context.Context
} }
// NewLoadOptions create LoadOptions struct with provided opts
func NewLoadOptions(opts ...LoadOption) LoadOptions { func NewLoadOptions(opts ...LoadOption) LoadOptions {
options := LoadOptions{} options := LoadOptions{}
for _, o := range opts { for _, o := range opts {
@@ -88,13 +92,30 @@ func LoadAppend(b bool) LoadOption {
} }
} }
// LoadStruct override struct for loading
func LoadStruct(src interface{}) LoadOption {
return func(o *LoadOptions) {
o.Struct = src
}
}
// SaveOption function signature // SaveOption function signature
type SaveOption func(o *SaveOptions) type SaveOption func(o *SaveOptions)
// SaveOptions struct // SaveOptions struct
type SaveOptions struct { type SaveOptions struct {
Struct interface{}
Context context.Context
} }
// SaveStruct override struct for save to config
func SaveStruct(src interface{}) SaveOption {
return func(o *SaveOptions) {
o.Struct = src
}
}
// NewSaveOptions fill SaveOptions struct
func NewSaveOptions(opts ...SaveOption) SaveOptions { func NewSaveOptions(opts ...SaveOption) SaveOptions {
options := SaveOptions{} options := SaveOptions{}
for _, o := range opts { for _, o := range opts {
@@ -186,3 +207,62 @@ func Name(n string) Option {
o.Name = n o.Name = n
} }
} }
// 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
}
// WatchOption func signature
type WatchOption func(*WatchOptions)
// NewWatchOptions create WatchOptions struct with provided opts
func NewWatchOptions(opts ...WatchOption) WatchOptions {
options := WatchOptions{
Context: context.Background(),
MinInterval: DefaultWatcherMinInterval,
MaxInterval: DefaultWatcherMaxInterval,
}
for _, o := range opts {
o(&options)
}
return options
}
// WatchContext pass context
func WatchContext(ctx context.Context) WatchOption {
return func(o *WatchOptions) {
o.Context = ctx
}
}
// WatchCoalesce controls watch event combining
func WatchCoalesce(b bool) WatchOption {
return func(o *WatchOptions) {
o.Coalesce = b
}
}
// WatchInterval specifies min and max time.Duration for pulling changes
func WatchInterval(min, max time.Duration) WatchOption {
return func(o *WatchOptions) {
o.MinInterval = min
o.MaxInterval = max
}
}
// WatchStruct overrides struct for fill
func WatchStruct(src interface{}) WatchOption {
return func(o *WatchOptions) {
o.Struct = src
}
}

View File

@@ -1,26 +0,0 @@
package config_test
import (
"testing"
rutil "github.com/unistack-org/micro/v3/util/reflect"
)
type Config struct {
SubConfig *SubConfig
Config *Config
Value string
}
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)
}

View File

@@ -1,11 +1,14 @@
// Package errors provides a way to return detailed information // Package errors provides a way to return detailed information
// for an RPC request error. The error is normally JSON encoded. // for an RPC request error. The error is normally JSON encoded.
package errors package errors // import "go.unistack.org/micro/v3/errors"
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"strconv"
"strings"
) )
var ( var (
@@ -37,8 +40,8 @@ var (
// Error type // Error type
type Error struct { type Error struct {
// Id holds error id or service, usually someting like my_service or uuid // ID holds error id or service, usually someting like my_service or id
Id string ID string
// Detail holds some useful details about error // Detail holds some useful details about error
Detail string Detail string
// Status usually holds text of http status // Status usually holds text of http status
@@ -53,10 +56,26 @@ func (e *Error) Error() string {
return string(b) return string(b)
} }
/*
// Generator struct holds id of error
type Generator struct {
id string
}
// Generator can emit new error with static id
func NewGenerator(id string) *Generator {
return &Generator{id: id}
}
func (g *Generator) BadRequest(format string, args ...interface{}) error {
return BadRequest(g.id, format, args...)
}
*/
// New generates a custom error // New generates a custom error
func New(id, detail string, code int32) error { func New(id, detail string, code int32) error {
return &Error{ return &Error{
Id: id, ID: id,
Code: code, Code: code,
Detail: detail, Detail: detail,
Status: http.StatusText(int(code)), Status: http.StatusText(int(code)),
@@ -66,130 +85,130 @@ func New(id, detail string, code int32) error {
// Parse tries to parse a JSON string into an error. If that // Parse tries to parse a JSON string into an error. If that
// fails, it will set the given string as the error detail. // fails, it will set the given string as the error detail.
func Parse(err string) *Error { func Parse(err string) *Error {
e := new(Error) e := &Error{}
errr := json.Unmarshal([]byte(err), e) nerr := json.Unmarshal([]byte(err), e)
if errr != nil { if nerr != nil {
e.Detail = err e.Detail = err
} }
return e return e
} }
// BadRequest generates a 400 error. // BadRequest generates a 400 error.
func BadRequest(id, format string, a ...interface{}) error { func BadRequest(id, format string, args ...interface{}) error {
return &Error{ return &Error{
Id: id, ID: id,
Code: 400, Code: 400,
Detail: fmt.Sprintf(format, a...), Detail: fmt.Sprintf(format, args...),
Status: http.StatusText(400), Status: http.StatusText(400),
} }
} }
// Unauthorized generates a 401 error. // Unauthorized generates a 401 error.
func Unauthorized(id, format string, a ...interface{}) error { func Unauthorized(id, format string, args ...interface{}) error {
return &Error{ return &Error{
Id: id, ID: id,
Code: 401, Code: 401,
Detail: fmt.Sprintf(format, a...), Detail: fmt.Sprintf(format, args...),
Status: http.StatusText(401), Status: http.StatusText(401),
} }
} }
// Forbidden generates a 403 error. // Forbidden generates a 403 error.
func Forbidden(id, format string, a ...interface{}) error { func Forbidden(id, format string, args ...interface{}) error {
return &Error{ return &Error{
Id: id, ID: id,
Code: 403, Code: 403,
Detail: fmt.Sprintf(format, a...), Detail: fmt.Sprintf(format, args...),
Status: http.StatusText(403), Status: http.StatusText(403),
} }
} }
// NotFound generates a 404 error. // NotFound generates a 404 error.
func NotFound(id, format string, a ...interface{}) error { func NotFound(id, format string, args ...interface{}) error {
return &Error{ return &Error{
Id: id, ID: id,
Code: 404, Code: 404,
Detail: fmt.Sprintf(format, a...), Detail: fmt.Sprintf(format, args...),
Status: http.StatusText(404), Status: http.StatusText(404),
} }
} }
// MethodNotAllowed generates a 405 error. // MethodNotAllowed generates a 405 error.
func MethodNotAllowed(id, format string, a ...interface{}) error { func MethodNotAllowed(id, format string, args ...interface{}) error {
return &Error{ return &Error{
Id: id, ID: id,
Code: 405, Code: 405,
Detail: fmt.Sprintf(format, a...), Detail: fmt.Sprintf(format, args...),
Status: http.StatusText(405), Status: http.StatusText(405),
} }
} }
// Timeout generates a 408 error. // Timeout generates a 408 error.
func Timeout(id, format string, a ...interface{}) error { func Timeout(id, format string, args ...interface{}) error {
return &Error{ return &Error{
Id: id, ID: id,
Code: 408, Code: 408,
Detail: fmt.Sprintf(format, a...), Detail: fmt.Sprintf(format, args...),
Status: http.StatusText(408), Status: http.StatusText(408),
} }
} }
// Conflict generates a 409 error. // Conflict generates a 409 error.
func Conflict(id, format string, a ...interface{}) error { func Conflict(id, format string, args ...interface{}) error {
return &Error{ return &Error{
Id: id, ID: id,
Code: 409, Code: 409,
Detail: fmt.Sprintf(format, a...), Detail: fmt.Sprintf(format, args...),
Status: http.StatusText(409), Status: http.StatusText(409),
} }
} }
// InternalServerError generates a 500 error. // InternalServerError generates a 500 error.
func InternalServerError(id, format string, a ...interface{}) error { func InternalServerError(id, format string, args ...interface{}) error {
return &Error{ return &Error{
Id: id, ID: id,
Code: 500, Code: 500,
Detail: fmt.Sprintf(format, a...), Detail: fmt.Sprintf(format, args...),
Status: http.StatusText(500), Status: http.StatusText(500),
} }
} }
// NotImplemented generates a 501 error // NotImplemented generates a 501 error
func NotImplemented(id, format string, a ...interface{}) error { func NotImplemented(id, format string, args ...interface{}) error {
return &Error{ return &Error{
Id: id, ID: id,
Code: 501, Code: 501,
Detail: fmt.Sprintf(format, a...), Detail: fmt.Sprintf(format, args...),
Status: http.StatusText(501), Status: http.StatusText(501),
} }
} }
// BadGateway generates a 502 error // BadGateway generates a 502 error
func BadGateway(id, format string, a ...interface{}) error { func BadGateway(id, format string, args ...interface{}) error {
return &Error{ return &Error{
Id: id, ID: id,
Code: 502, Code: 502,
Detail: fmt.Sprintf(format, a...), Detail: fmt.Sprintf(format, args...),
Status: http.StatusText(502), Status: http.StatusText(502),
} }
} }
// ServiceUnavailable generates a 503 error // ServiceUnavailable generates a 503 error
func ServiceUnavailable(id, format string, a ...interface{}) error { func ServiceUnavailable(id, format string, args ...interface{}) error {
return &Error{ return &Error{
Id: id, ID: id,
Code: 503, Code: 503,
Detail: fmt.Sprintf(format, a...), Detail: fmt.Sprintf(format, args...),
Status: http.StatusText(503), Status: http.StatusText(503),
} }
} }
// GatewayTimeout generates a 504 error // GatewayTimeout generates a 504 error
func GatewayTimeout(id, format string, a ...interface{}) error { func GatewayTimeout(id, format string, args ...interface{}) error {
return &Error{ return &Error{
Id: id, ID: id,
Code: 504, Code: 504,
Detail: fmt.Sprintf(format, a...), Detail: fmt.Sprintf(format, args...),
Status: http.StatusText(504), Status: http.StatusText(504),
} }
} }
@@ -222,3 +241,81 @@ func FromError(err error) *Error {
return Parse(err.Error()) return Parse(err.Error())
} }
// MarshalJSON returns error data
func (e *Error) MarshalJSON() ([]byte, error) {
return e.Marshal()
}
// UnmarshalJSON set error data
func (e *Error) UnmarshalJSON(data []byte) error {
return e.Unmarshal(data)
}
// ProtoMessage noop func
func (e *Error) ProtoMessage() {}
// Reset resets error
func (e *Error) Reset() {
*e = Error{}
}
// String returns error as string
func (e *Error) String() string {
return fmt.Sprintf(`{"id":"%s","detail":"%s","status":"%s","code":%d}`, addslashes(e.ID), addslashes(e.Detail), addslashes(e.Status), e.Code)
}
// Marshal returns error data
func (e *Error) Marshal() ([]byte, error) {
return []byte(e.String()), nil
}
// Unmarshal set error data
func (e *Error) Unmarshal(data []byte) error {
str := string(data)
if len(data) < 41 {
return fmt.Errorf("invalid data")
}
parts := strings.FieldsFunc(str[1:len(str)-1], func(r rune) bool {
return r == ','
})
for _, part := range parts {
nparts := strings.FieldsFunc(part, func(r rune) bool {
return r == ':'
})
for idx := 0; idx < len(nparts)/2; idx += 2 {
val := strings.Trim(nparts[idx+1], `"`)
if len(val) == 0 {
continue
}
switch {
case nparts[idx] == `"id"`:
e.ID = val
case nparts[idx] == `"detail"`:
e.Detail = val
case nparts[idx] == `"status"`:
e.Status = val
case nparts[idx] == `"code"`:
c, err := strconv.ParseInt(val, 10, 32)
if err != nil {
return err
}
e.Code = int32(c)
}
idx++
}
}
return nil
}
func addslashes(str string) string {
var buf bytes.Buffer
for _, char := range str {
switch char {
case '\'', '"', '\\':
buf.WriteRune('\\')
}
buf.WriteRune(char)
}
return buf.String()
}

31
errors/errors.proto Normal file
View File

@@ -0,0 +1,31 @@
// Copyright 2021 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.errors;
option cc_enable_arenas = true;
option go_package = "go.unistack.org/micro/v3/errors;errors";
option java_multiple_files = true;
option java_outer_classname = "MicroErrors";
option java_package = "micro.errors";
option objc_class_prefix = "MERRORS";
message Error {
string id = 1;
string detail = 2;
string status = 3;
uint32 code = 4;
}

View File

@@ -1,20 +1,43 @@
package errors package errors
import ( import (
"encoding/json"
er "errors" er "errors"
"fmt"
"net/http" "net/http"
"testing" "testing"
) )
func TestMarshalJSON(t *testing.T) {
e := InternalServerError("id", "err: %v", fmt.Errorf("err: %v", `xxx: "UNIX_TIMESTAMP": invalid identifier`))
_, err := json.Marshal(e)
if err != nil {
t.Fatal(err)
}
}
func TestEmpty(t *testing.T) {
msg := "test"
var err *Error
err = FromError(fmt.Errorf(msg))
if err.Detail != msg {
t.Fatalf("invalid error %v", err)
}
err = FromError(fmt.Errorf(`{"id":"","detail":"%s","status":"%s","code":0}`, msg, msg))
if err.Detail != msg || err.Status != msg {
t.Fatalf("invalid error %#+v", err)
}
}
func TestFromError(t *testing.T) { func TestFromError(t *testing.T) {
err := NotFound("go.micro.test", "%s", "example") err := NotFound("go.micro.test", "%s", "example")
merr := FromError(err) merr := FromError(err)
if merr.Id != "go.micro.test" || merr.Code != 404 { if merr.ID != "go.micro.test" || merr.Code != 404 {
t.Fatalf("invalid conversation %v != %v", err, merr) t.Fatalf("invalid conversation %v != %v", err, merr)
} }
err = er.New(err.Error()) err = er.New(err.Error())
merr = FromError(err) merr = FromError(err)
if merr.Id != "go.micro.test" || merr.Code != 404 { if merr.ID != "go.micro.test" || merr.Code != 404 {
t.Fatalf("invalid conversation %v != %v", err, merr) t.Fatalf("invalid conversation %v != %v", err, merr)
} }
} }
@@ -36,7 +59,7 @@ func TestEqual(t *testing.T) {
func TestErrors(t *testing.T) { func TestErrors(t *testing.T) {
testData := []*Error{ testData := []*Error{
{ {
Id: "test", ID: "test",
Code: 500, Code: 500,
Detail: "Internal server error", Detail: "Internal server error",
Status: http.StatusText(500), Status: http.StatusText(500),
@@ -44,7 +67,7 @@ func TestErrors(t *testing.T) {
} }
for _, e := range testData { for _, e := range testData {
ne := New(e.Id, e.Detail, e.Code) ne := New(e.ID, e.Detail, e.Code)
if e.Error() != ne.Error() { if e.Error() != ne.Error() {
t.Fatalf("Expected %s got %s", e.Error(), ne.Error()) t.Fatalf("Expected %s got %s", e.Error(), ne.Error())
@@ -56,8 +79,8 @@ func TestErrors(t *testing.T) {
t.Fatalf("Expected error got nil %v", pe) t.Fatalf("Expected error got nil %v", pe)
} }
if pe.Id != e.Id { if pe.ID != e.ID {
t.Fatalf("Expected %s got %s", e.Id, pe.Id) t.Fatalf("Expected %s got %s", e.ID, pe.ID)
} }
if pe.Detail != e.Detail { if pe.Detail != e.Detail {

View File

@@ -3,7 +3,7 @@ package micro
import ( import (
"context" "context"
"github.com/unistack-org/micro/v3/client" "go.unistack.org/micro/v3/client"
) )
// Event is used to publish messages to a topic // Event is used to publish messages to a topic

34
flow/context.go Normal file
View File

@@ -0,0 +1,34 @@
package flow
import (
"context"
)
type flowKey struct{}
// FromContext returns Flow from context
func FromContext(ctx context.Context) (Flow, bool) {
if ctx == nil {
return nil, false
}
c, ok := ctx.Value(flowKey{}).(Flow)
return c, ok
}
// NewContext stores Flow to context
func NewContext(ctx context.Context, f Flow) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, flowKey{}, f)
}
// 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)
}
}

588
flow/default.go Normal file
View File

@@ -0,0 +1,588 @@
package flow
import (
"context"
"fmt"
"path/filepath"
"sync"
"github.com/silas/dag"
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/codec"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v3/store"
"go.unistack.org/micro/v3/util/id"
)
type microFlow struct {
opts Options
}
type microWorkflow struct {
opts Options
g *dag.AcyclicGraph
steps map[string]Step
id string
status Status
sync.RWMutex
init bool
}
func (w *microWorkflow) ID() string {
return w.id
}
func (w *microWorkflow) Steps() ([][]Step, error) {
return w.getSteps("", false)
}
func (w *microWorkflow) Status() Status {
return w.status
}
func (w *microWorkflow) AppendSteps(steps ...Step) error {
w.Lock()
for _, s := range steps {
w.steps[s.String()] = s
w.g.Add(s)
}
for _, dst := range steps {
for _, req := range dst.Requires() {
src, ok := w.steps[req]
if !ok {
return ErrStepNotExists
}
w.g.Connect(dag.BasicEdge(src, dst))
}
}
if err := w.g.Validate(); err != nil {
w.Unlock()
return err
}
w.g.TransitiveReduction()
w.Unlock()
return nil
}
func (w *microWorkflow) RemoveSteps(steps ...Step) error {
// TODO: handle case when some step requires or required by removed step
w.Lock()
for _, s := range steps {
delete(w.steps, s.String())
w.g.Remove(s)
}
for _, dst := range steps {
for _, req := range dst.Requires() {
src, ok := w.steps[req]
if !ok {
return ErrStepNotExists
}
w.g.Connect(dag.BasicEdge(src, dst))
}
}
if err := w.g.Validate(); err != nil {
w.Unlock()
return err
}
w.g.TransitiveReduction()
w.Unlock()
return nil
}
func (w *microWorkflow) getSteps(start string, reverse bool) ([][]Step, error) {
var steps [][]Step
var root dag.Vertex
var err error
fn := func(n dag.Vertex, idx int) error {
if idx == 0 {
steps = make([][]Step, 1)
steps[0] = make([]Step, 0, 1)
} else if idx >= len(steps) {
tsteps := make([][]Step, idx+1)
copy(tsteps, steps)
steps = tsteps
steps[idx] = make([]Step, 0, 1)
}
steps[idx] = append(steps[idx], n.(Step))
return nil
}
if start != "" {
var ok bool
w.RLock()
root, ok = w.steps[start]
w.RUnlock()
if !ok {
return nil, ErrStepNotExists
}
} else {
root, err = w.g.Root()
if err != nil {
return nil, err
}
}
if reverse {
err = w.g.SortedReverseDepthFirstWalk([]dag.Vertex{root}, fn)
} else {
err = w.g.SortedDepthFirstWalk([]dag.Vertex{root}, fn)
}
if err != nil {
return nil, err
}
return steps, nil
}
func (w *microWorkflow) Abort(ctx context.Context, id string) error {
workflowStore := store.NewNamespaceStore(w.opts.Store, filepath.Join("workflows", id))
return workflowStore.Write(ctx, "status", &codec.Frame{Data: []byte(StatusAborted.String())})
}
func (w *microWorkflow) Suspend(ctx context.Context, id string) error {
workflowStore := store.NewNamespaceStore(w.opts.Store, filepath.Join("workflows", id))
return workflowStore.Write(ctx, "status", &codec.Frame{Data: []byte(StatusSuspend.String())})
}
func (w *microWorkflow) Resume(ctx context.Context, id string) error {
workflowStore := store.NewNamespaceStore(w.opts.Store, filepath.Join("workflows", id))
return workflowStore.Write(ctx, "status", &codec.Frame{Data: []byte(StatusRunning.String())})
}
func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...ExecuteOption) (string, error) {
w.Lock()
if !w.init {
if err := w.g.Validate(); err != nil {
w.Unlock()
return "", err
}
w.g.TransitiveReduction()
w.init = true
}
w.Unlock()
eid, err := id.New()
if err != nil {
return "", err
}
stepStore := store.NewNamespaceStore(w.opts.Store, filepath.Join("steps", eid))
workflowStore := store.NewNamespaceStore(w.opts.Store, filepath.Join("workflows", eid))
options := NewExecuteOptions(opts...)
steps, err := w.getSteps(options.Start, options.Reverse)
if err != nil {
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusPending.String())}); werr != nil {
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
}
return "", err
}
var wg sync.WaitGroup
cherr := make(chan error, 1)
chstatus := make(chan Status, 1)
nctx, cancel := context.WithCancel(ctx)
defer cancel()
nopts := make([]ExecuteOption, 0, len(opts)+5)
nopts = append(nopts,
ExecuteClient(w.opts.Client),
ExecuteTracer(w.opts.Tracer),
ExecuteLogger(w.opts.Logger),
ExecuteMeter(w.opts.Meter),
)
nopts = append(nopts, opts...)
done := make(chan struct{})
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusRunning.String())}); werr != nil {
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
return eid, werr
}
for idx := range steps {
for nidx := range steps[idx] {
cstep := steps[idx][nidx]
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "status"), &codec.Frame{Data: []byte(StatusPending.String())}); werr != nil {
return eid, werr
}
}
}
go func() {
for idx := range steps {
for nidx := range steps[idx] {
wStatus := &codec.Frame{}
if werr := workflowStore.Read(w.opts.Context, "status", wStatus); werr != nil {
cherr <- werr
return
}
if status := StringStatus[string(wStatus.Data)]; status != StatusRunning {
chstatus <- status
return
}
if w.opts.Logger.V(logger.TraceLevel) {
w.opts.Logger.Tracef(nctx, "will be executed %v", steps[idx][nidx])
}
cstep := steps[idx][nidx]
// nolint: nestif
if len(cstep.Requires()) == 0 {
wg.Add(1)
go func(step Step) {
defer wg.Done()
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "req"), req); werr != nil {
cherr <- werr
return
}
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "status"), &codec.Frame{Data: []byte(StatusRunning.String())}); werr != nil {
cherr <- werr
return
}
rsp, serr := step.Execute(nctx, req, nopts...)
if serr != nil {
step.SetStatus(StatusFailure)
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "rsp"), serr); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
}
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "status"), &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
}
cherr <- serr
return
}
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "rsp"), rsp); werr != nil {
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
cherr <- werr
return
}
if werr := stepStore.Write(ctx, filepath.Join(step.ID(), "status"), &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
cherr <- werr
return
}
}(cstep)
wg.Wait()
} else {
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "req"), req); werr != nil {
cherr <- werr
return
}
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "status"), &codec.Frame{Data: []byte(StatusRunning.String())}); werr != nil {
cherr <- werr
return
}
rsp, serr := cstep.Execute(nctx, req, nopts...)
if serr != nil {
cstep.SetStatus(StatusFailure)
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "rsp"), serr); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
}
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "status"), &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
}
cherr <- serr
return
}
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "rsp"), rsp); werr != nil {
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
cherr <- werr
return
}
if werr := stepStore.Write(ctx, filepath.Join(cstep.ID(), "status"), &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
cherr <- werr
return
}
}
}
}
close(done)
}()
if options.Async {
return eid, nil
}
logger.Tracef(ctx, "wait for finish or error")
select {
case <-nctx.Done():
err = nctx.Err()
case cerr := <-cherr:
err = cerr
case <-done:
close(cherr)
case <-chstatus:
close(chstatus)
return eid, nil
}
switch {
case nctx.Err() != nil:
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusAborted.String())}); werr != nil {
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
}
case err == nil:
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
}
case err != nil:
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil {
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
}
}
return eid, err
}
// NewFlow create new flow
func NewFlow(opts ...Option) Flow {
options := NewOptions(opts...)
return &microFlow{opts: options}
}
func (f *microFlow) Options() Options {
return f.opts
}
func (f *microFlow) Init(opts ...Option) error {
for _, o := range opts {
o(&f.opts)
}
if err := f.opts.Client.Init(); err != nil {
return err
}
if err := f.opts.Tracer.Init(); err != nil {
return err
}
if err := f.opts.Logger.Init(); err != nil {
return err
}
if err := f.opts.Meter.Init(); err != nil {
return err
}
if err := f.opts.Store.Init(); err != nil {
return err
}
return nil
}
func (f *microFlow) WorkflowList(ctx context.Context) ([]Workflow, error) {
return nil, nil
}
func (f *microFlow) WorkflowCreate(ctx context.Context, id string, steps ...Step) (Workflow, error) {
w := &microWorkflow{opts: f.opts, id: id, g: &dag.AcyclicGraph{}, steps: make(map[string]Step, len(steps))}
for _, s := range steps {
w.steps[s.String()] = s
w.g.Add(s)
}
for _, dst := range steps {
for _, req := range dst.Requires() {
src, ok := w.steps[req]
if !ok {
return nil, ErrStepNotExists
}
w.g.Connect(dag.BasicEdge(src, dst))
}
}
if err := w.g.Validate(); err != nil {
return nil, err
}
w.g.TransitiveReduction()
w.init = true
return w, nil
}
func (f *microFlow) WorkflowRemove(ctx context.Context, id string) error {
return nil
}
func (f *microFlow) WorkflowSave(ctx context.Context, w Workflow) error {
return nil
}
func (f *microFlow) WorkflowLoad(ctx context.Context, id string) (Workflow, error) {
return nil, nil
}
type microCallStep struct {
rsp *Message
req *Message
service string
method string
opts StepOptions
status Status
}
func (s *microCallStep) Request() *Message {
return s.req
}
func (s *microCallStep) Response() *Message {
return s.rsp
}
func (s *microCallStep) ID() string {
return s.String()
}
func (s *microCallStep) Options() StepOptions {
return s.opts
}
func (s *microCallStep) Endpoint() string {
return s.method
}
func (s *microCallStep) Requires() []string {
return s.opts.Requires
}
func (s *microCallStep) Require(steps ...Step) error {
for _, step := range steps {
s.opts.Requires = append(s.opts.Requires, step.String())
}
return nil
}
func (s *microCallStep) String() string {
if s.opts.ID != "" {
return s.opts.ID
}
return fmt.Sprintf("%s.%s", s.service, s.method)
}
func (s *microCallStep) Name() string {
return s.String()
}
func (s *microCallStep) Hashcode() interface{} {
return s.String()
}
func (s *microCallStep) GetStatus() Status {
return s.status
}
func (s *microCallStep) SetStatus(status Status) {
s.status = status
}
func (s *microCallStep) Execute(ctx context.Context, req *Message, opts ...ExecuteOption) (*Message, error) {
options := NewExecuteOptions(opts...)
if options.Client == nil {
return nil, ErrMissingClient
}
rsp := &codec.Frame{}
copts := []client.CallOption{client.WithRetries(0)}
if options.Timeout > 0 {
copts = append(copts,
client.WithRequestTimeout(options.Timeout),
client.WithDialTimeout(options.Timeout))
}
nctx := metadata.NewOutgoingContext(ctx, req.Header)
err := options.Client.Call(nctx, options.Client.NewRequest(s.service, s.method, &codec.Frame{Data: req.Body}), rsp, copts...)
if err != nil {
return nil, err
}
md, _ := metadata.FromOutgoingContext(nctx)
return &Message{Header: md, Body: rsp.Data}, err
}
type microPublishStep struct {
req *Message
rsp *Message
topic string
opts StepOptions
status Status
}
func (s *microPublishStep) Request() *Message {
return s.req
}
func (s *microPublishStep) Response() *Message {
return s.rsp
}
func (s *microPublishStep) ID() string {
return s.String()
}
func (s *microPublishStep) Options() StepOptions {
return s.opts
}
func (s *microPublishStep) Endpoint() string {
return s.topic
}
func (s *microPublishStep) Requires() []string {
return s.opts.Requires
}
func (s *microPublishStep) Require(steps ...Step) error {
for _, step := range steps {
s.opts.Requires = append(s.opts.Requires, step.String())
}
return nil
}
func (s *microPublishStep) String() string {
if s.opts.ID != "" {
return s.opts.ID
}
return s.topic
}
func (s *microPublishStep) Name() string {
return s.String()
}
func (s *microPublishStep) Hashcode() interface{} {
return s.String()
}
func (s *microPublishStep) GetStatus() Status {
return s.status
}
func (s *microPublishStep) SetStatus(status Status) {
s.status = status
}
func (s *microPublishStep) Execute(ctx context.Context, req *Message, opts ...ExecuteOption) (*Message, error) {
return nil, nil
}
// NewCallStep create new step with client.Call
func NewCallStep(service string, name string, method string, opts ...StepOption) Step {
options := NewStepOptions(opts...)
return &microCallStep{service: service, method: name + "." + method, opts: options}
}
// NewPublishStep create new step with client.Publish
func NewPublishStep(topic string, opts ...StepOption) Step {
options := NewStepOptions(opts...)
return &microPublishStep{topic: topic, opts: options}
}

View File

@@ -1,17 +1,165 @@
// Package flow is an interface used for saga pattern microservice workflow // Package flow is an interface used for saga pattern microservice workflow
package flow package flow // import "go.unistack.org/micro/v3/flow"
import (
"context"
"errors"
"sync"
"sync/atomic"
"go.unistack.org/micro/v3/metadata"
)
var (
// ErrStepNotExists returns when step not found
ErrStepNotExists = errors.New("step not exists")
// ErrMissingClient returns when client.Client is missing
ErrMissingClient = errors.New("client not set")
)
// 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
}
// Message used to transfer data between steps
type Message struct {
Header metadata.Metadata
Body RawMessage
}
// Step represents dedicated workflow step
type Step interface { type Step interface {
// ID returns step id
ID() string
// Endpoint returns rpc endpoint service_name.service_method or broker topic // Endpoint returns rpc endpoint service_name.service_method or broker topic
Endpoint() string Endpoint() string
// Execute step run
Execute(ctx context.Context, req *Message, opts ...ExecuteOption) (*Message, error)
// Requires returns dependent steps
Requires() []string
// Options returns step options
Options() StepOptions
// Require add required steps
Require(steps ...Step) error
// String
String() string
// GetStatus returns step status
GetStatus() Status
// SetStatus sets the step status
SetStatus(Status)
// Request returns step request message
Request() *Message
// Response returns step response message
Response() *Message
} }
// Status contains step current status
type Status int
func (status Status) String() string {
return StatusString[status]
}
const (
// StatusPending step waiting to start
StatusPending Status = iota
// StatusRunning step is running
StatusRunning
// StatusFailure step competed with error
StatusFailure
// StatusSuccess step completed without error
StatusSuccess
// StatusAborted step aborted while it running
StatusAborted
// StatusSuspend step suspended
StatusSuspend
)
var (
// StatusString contains map status => string
StatusString = map[Status]string{
StatusPending: "StatusPending",
StatusRunning: "StatusRunning",
StatusFailure: "StatusFailure",
StatusSuccess: "StatusSuccess",
StatusAborted: "StatusAborted",
StatusSuspend: "StatusSuspend",
}
// StringStatus contains map string => status
StringStatus = map[string]Status{
"StatusPending": StatusPending,
"StatusRunning": StatusRunning,
"StatusFailure": StatusFailure,
"StatusSuccess": StatusSuccess,
"StatusAborted": StatusAborted,
"StatusSuspend": StatusSuspend,
}
)
// Workflow contains all steps to execute
type Workflow interface { type Workflow interface {
Steps() [][]Step // ID returns id of the workflow
Stop() error ID() string
// Execute workflow with args, return execution id and error
Execute(ctx context.Context, req *Message, opts ...ExecuteOption) (string, error)
// RemoveSteps remove steps from workflow
RemoveSteps(steps ...Step) error
// AppendSteps append steps to workflow
AppendSteps(steps ...Step) error
// Status returns workflow status
Status() Status
// Steps returns steps slice where parallel steps returned on the same level
Steps() ([][]Step, error)
// Suspend suspends execution
Suspend(ctx context.Context, id string) error
// Resume resumes execution
Resume(ctx context.Context, id string) error
// Abort abort execution
Abort(ctx context.Context, id string) error
} }
// Flow the base interface to interact with workflows
type Flow interface { type Flow interface {
Start(Workflow) error // Options returns options
Stop(Workflow) Options() Options
// Init initialize
Init(...Option) error
// WorkflowCreate creates new workflow with specific id and steps
WorkflowCreate(ctx context.Context, id string, steps ...Step) (Workflow, error)
// WorkflowSave saves workflow
WorkflowSave(ctx context.Context, w Workflow) error
// WorkflowLoad loads workflow with specific id
WorkflowLoad(ctx context.Context, id string) (Workflow, error)
// WorkflowList lists all workflows
WorkflowList(ctx context.Context) ([]Workflow, error)
}
var (
flowMu sync.Mutex
atomicSteps atomic.Value
)
// RegisterStep register own step with workflow
func RegisterStep(step Step) {
flowMu.Lock()
steps, _ := atomicSteps.Load().([]Step)
atomicSteps.Store(append(steps, step))
flowMu.Unlock()
} }

247
flow/options.go Normal file
View File

@@ -0,0 +1,247 @@
package flow
import (
"context"
"time"
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/meter"
"go.unistack.org/micro/v3/store"
"go.unistack.org/micro/v3/tracer"
)
// Option func
type Option func(*Options)
// Options server struct
type Options struct {
// Context holds the external options and can be used for flow shutdown
Context context.Context
// Client holds the client.Client
Client client.Client
// Tracer holds the tracer
Tracer tracer.Tracer
// Logger holds the logger
Logger logger.Logger
// Meter holds the meter
Meter meter.Meter
// Store used for intermediate results
Store store.Store
}
// NewOptions returns new options struct with default or passed values
func NewOptions(opts ...Option) Options {
options := Options{
Context: context.Background(),
Logger: logger.DefaultLogger,
Meter: meter.DefaultMeter,
Tracer: tracer.DefaultTracer,
Client: client.DefaultClient,
}
for _, o := range opts {
o(&options)
}
return options
}
// Logger sets the logger option
func Logger(l logger.Logger) Option {
return func(o *Options) {
o.Logger = l
}
}
// Meter sets the meter option
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
// Client to use for sync/async communication
func Client(c client.Client) Option {
return func(o *Options) {
o.Client = c
}
}
// Context specifies a context for the service.
// Can be used to signal shutdown of the flow
// or can be used for extra option values.
func Context(ctx context.Context) Option {
return func(o *Options) {
o.Context = ctx
}
}
// Tracer mechanism for distributed tracking
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}
// Store used for intermediate results
func Store(s store.Store) Option {
return func(o *Options) {
o.Store = s
}
}
// WorkflowOption func signature
type WorkflowOption func(*WorkflowOptions)
// WorkflowOptions holds workflow options
type WorkflowOptions struct {
Context context.Context
ID string
}
// WorkflowID set workflow id
func WorkflowID(id string) WorkflowOption {
return func(o *WorkflowOptions) {
o.ID = id
}
}
// ExecuteOptions holds execute options
type ExecuteOptions struct {
// Client holds the client.Client
Client client.Client
// Tracer holds the tracer
Tracer tracer.Tracer
// Logger holds the logger
Logger logger.Logger
// Meter holds the meter
Meter meter.Meter
// Context can be used to abort execution or pass additional opts
Context context.Context
// Start step
Start string
// Timeout for execution
Timeout time.Duration
// Reverse execution
Reverse bool
// Async enables async execution
Async bool
}
// ExecuteOption func signature
type ExecuteOption func(*ExecuteOptions)
// ExecuteClient pass client.Client to ExecuteOption
func ExecuteClient(c client.Client) ExecuteOption {
return func(o *ExecuteOptions) {
o.Client = c
}
}
// ExecuteTracer pass tracer.Tracer to ExecuteOption
func ExecuteTracer(t tracer.Tracer) ExecuteOption {
return func(o *ExecuteOptions) {
o.Tracer = t
}
}
// ExecuteLogger pass logger.Logger to ExecuteOption
func ExecuteLogger(l logger.Logger) ExecuteOption {
return func(o *ExecuteOptions) {
o.Logger = l
}
}
// ExecuteMeter pass meter.Meter to ExecuteOption
func ExecuteMeter(m meter.Meter) ExecuteOption {
return func(o *ExecuteOptions) {
o.Meter = m
}
}
// ExecuteContext pass context.Context ot ExecuteOption
func ExecuteContext(ctx context.Context) ExecuteOption {
return func(o *ExecuteOptions) {
o.Context = ctx
}
}
// ExecuteReverse says that dag must be run in reverse order
func ExecuteReverse(b bool) ExecuteOption {
return func(o *ExecuteOptions) {
o.Reverse = b
}
}
// ExecuteTimeout pass timeout time.Duration for execution
func ExecuteTimeout(td time.Duration) ExecuteOption {
return func(o *ExecuteOptions) {
o.Timeout = td
}
}
// ExecuteAsync says that caller does not wait for execution complete
func ExecuteAsync(b bool) ExecuteOption {
return func(o *ExecuteOptions) {
o.Async = b
}
}
// NewExecuteOptions create new ExecuteOptions struct
func NewExecuteOptions(opts ...ExecuteOption) ExecuteOptions {
options := ExecuteOptions{
Client: client.DefaultClient,
Logger: logger.DefaultLogger,
Tracer: tracer.DefaultTracer,
Meter: meter.DefaultMeter,
Context: context.Background(),
}
for _, o := range opts {
o(&options)
}
return options
}
// StepOptions holds step options
type StepOptions struct {
Context context.Context
Fallback string
ID string
Requires []string
}
// StepOption func signature
type StepOption func(*StepOptions)
// NewStepOptions create new StepOptions struct
func NewStepOptions(opts ...StepOption) StepOptions {
options := StepOptions{
Context: context.Background(),
}
for _, o := range opts {
o(&options)
}
return options
}
// StepID sets the step id for dag
func StepID(id string) StepOption {
return func(o *StepOptions) {
o.ID = id
}
}
// StepRequires specifies required steps
func StepRequires(steps ...string) StepOption {
return func(o *StepOptions) {
o.Requires = steps
}
}
// StepFallback set the step to run on error
func StepFallback(step string) StepOption {
return func(o *StepOptions) {
o.Fallback = step
}
}

View File

@@ -1,3 +1,4 @@
//go:build ignore
// +build ignore // +build ignore
package micro package micro
@@ -6,7 +7,7 @@ import (
"context" "context"
"time" "time"
"github.com/unistack-org/micro/v3/server" "go.unistack.org/micro/v3/server"
) )
// Function is a one time executing Service // Function is a one time executing Service

View File

@@ -1,3 +1,4 @@
//go:build ignore
// +build ignore // +build ignore
package micro package micro
@@ -8,7 +9,7 @@ import (
"sync" "sync"
"testing" "testing"
"github.com/unistack-org/micro/v3/register" "go.unistack.org/micro/v3/register"
) )
func TestFunction(t *testing.T) { func TestFunction(t *testing.T) {

10
go.mod
View File

@@ -1,13 +1,13 @@
module github.com/unistack-org/micro/v3 module go.unistack.org/micro/v3
go 1.16 go 1.16
require ( require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/ef-ds/deque v1.0.4 github.com/ef-ds/deque v1.0.4
github.com/google/uuid v1.2.0 github.com/golang-jwt/jwt/v4 v4.4.0
github.com/imdario/mergo v0.3.12 github.com/imdario/mergo v0.3.12
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34 github.com/silas/dag v0.0.0-20211117232152-9d50aa809f35
golang.org/x/net v0.0.0-20210510120150-4163338589ed go.unistack.org/micro-proto/v3 v3.2.7
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b
) )

156
go.sum
View File

@@ -1,22 +1,162 @@
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/ef-ds/deque v1.0.4 h1:iFAZNmveMT9WERAkqLJ+oaABF9AcVQ5AjXem/hroniI= github.com/ef-ds/deque v1.0.4 h1:iFAZNmveMT9WERAkqLJ+oaABF9AcVQ5AjXem/hroniI=
github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg= github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/golang-jwt/jwt/v4 v4.4.0 h1:EmVIxB5jzbllGIjiCV5JG4VylbK3KE400tLGLI1cdfU=
github.com/golang-jwt/jwt/v4 v4.4.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/gnostic v0.6.6 h1:MVSM2r2j9aRUvYNym66JGW96Ddd5MN4sTi59yktb6yk=
github.com/google/gnostic v0.6.6/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34 h1:vBfVmA5mZhsQa2jr1FOL9nfA37N/jnbBmi5XUfviVTI= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= github.com/silas/dag v0.0.0-20211117232152-9d50aa809f35 h1:4mohWoM/UGg1BvFFiqSPRl5uwJY3rVV0HQX0ETqauqQ=
github.com/silas/dag v0.0.0-20211117232152-9d50aa809f35/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.unistack.org/micro-proto/v3 v3.2.7 h1:zG6d69kHc+oij2lwQ3AfrCgdjiEVRG2A7TlsxjusWs4=
go.unistack.org/micro-proto/v3 v3.2.7/go.mod h1:ZltVWNECD5yK+40+OCONzGw4OtmSdTpVi8/KFgo9dqM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b h1:eB48h3HiRycXNy8E0Gf5e0hv7YT6Kt14L/D73G1fuwo=
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -11,19 +11,12 @@ import (
"time" "time"
) )
func init() {
lvl, err := GetLevel(os.Getenv("MICRO_LOG_LEVEL"))
if err != nil {
lvl = InfoLevel
}
DefaultLogger = NewLogger(WithLevel(lvl))
}
type defaultLogger struct { type defaultLogger struct {
enc *json.Encoder
opts Options
sync.RWMutex sync.RWMutex
enc *json.Encoder
logFunc LogFunc
logfFunc LogfFunc
opts Options
} }
// Init(opts...) should only overwrite provided options // Init(opts...) should only overwrite provided options
@@ -33,6 +26,11 @@ func (l *defaultLogger) Init(opts ...Option) error {
o(&l.opts) o(&l.opts)
} }
l.enc = json.NewEncoder(l.opts.Out) l.enc = json.NewEncoder(l.opts.Out)
// wrap the Log func
for i := len(l.opts.Wrappers); i > 0; i-- {
l.logFunc = l.opts.Wrappers[i-1].Log(l.logFunc)
l.logfFunc = l.opts.Wrappers[i-1].Logf(l.logfFunc)
}
l.Unlock() l.Unlock()
return nil return nil
} }
@@ -41,6 +39,28 @@ func (l *defaultLogger) String() string {
return "micro" return "micro"
} }
func (l *defaultLogger) Clone(opts ...Option) Logger {
newopts := NewOptions(opts...)
oldopts := l.opts
for _, o := range opts {
o(&newopts)
o(&oldopts)
}
oldopts.Wrappers = newopts.Wrappers
l.Lock()
cl := &defaultLogger{opts: oldopts, logFunc: l.logFunc, logfFunc: l.logfFunc}
l.Unlock()
// wrap the Log func
for i := len(newopts.Wrappers); i > 0; i-- {
cl.logFunc = newopts.Wrappers[i-1].Log(cl.logFunc)
cl.logfFunc = newopts.Wrappers[i-1].Logf(cl.logfFunc)
}
return cl
}
func (l *defaultLogger) V(level Level) bool { func (l *defaultLogger) V(level Level) bool {
l.RLock() l.RLock()
ok := l.opts.Level.Enabled(level) ok := l.opts.Level.Enabled(level)
@@ -48,26 +68,28 @@ func (l *defaultLogger) V(level Level) bool {
return ok return ok
} }
func (l *defaultLogger) Fields(fields map[string]interface{}) Logger { func (l *defaultLogger) Level(level Level) {
nl := &defaultLogger{opts: l.opts, enc: l.enc} l.Lock()
nl.opts.Fields = make(map[string]interface{}, len(l.opts.Fields)+len(fields)) l.opts.Level = level
l.RLock() l.Unlock()
for k, v := range l.opts.Fields { }
nl.opts.Fields[k] = v
}
l.RUnlock()
for k, v := range fields { func (l *defaultLogger) Fields(fields ...interface{}) Logger {
nl.opts.Fields[k] = v nl := &defaultLogger{opts: l.opts, enc: l.enc}
if len(fields) == 0 {
return nl
} else if len(fields)%2 != 0 {
fields = fields[:len(fields)-1]
} }
nl.logFunc = l.logFunc
nl.logfFunc = l.logfFunc
nl.opts.Fields = append(nl.opts.Fields, fields...)
return nl return nl
} }
func copyFields(src map[string]interface{}) map[string]interface{} { func copyFields(src []interface{}) []interface{} {
dst := make(map[string]interface{}, len(src)) dst := make([]interface{}, len(src))
for k, v := range src { copy(dst, src)
dst[k] = v
}
return dst return dst
} }
@@ -121,27 +143,27 @@ func (l *defaultLogger) Fatal(ctx context.Context, args ...interface{}) {
} }
func (l *defaultLogger) Infof(ctx context.Context, msg string, args ...interface{}) { func (l *defaultLogger) Infof(ctx context.Context, msg string, args ...interface{}) {
l.Logf(ctx, InfoLevel, msg, args...) l.logfFunc(ctx, InfoLevel, msg, args...)
} }
func (l *defaultLogger) Errorf(ctx context.Context, msg string, args ...interface{}) { func (l *defaultLogger) Errorf(ctx context.Context, msg string, args ...interface{}) {
l.Logf(ctx, ErrorLevel, msg, args...) l.logfFunc(ctx, ErrorLevel, msg, args...)
} }
func (l *defaultLogger) Debugf(ctx context.Context, msg string, args ...interface{}) { func (l *defaultLogger) Debugf(ctx context.Context, msg string, args ...interface{}) {
l.Logf(ctx, DebugLevel, msg, args...) l.logfFunc(ctx, DebugLevel, msg, args...)
} }
func (l *defaultLogger) Warnf(ctx context.Context, msg string, args ...interface{}) { func (l *defaultLogger) Warnf(ctx context.Context, msg string, args ...interface{}) {
l.Logf(ctx, WarnLevel, msg, args...) l.logfFunc(ctx, WarnLevel, msg, args...)
} }
func (l *defaultLogger) Tracef(ctx context.Context, msg string, args ...interface{}) { func (l *defaultLogger) Tracef(ctx context.Context, msg string, args ...interface{}) {
l.Logf(ctx, TraceLevel, msg, args...) l.logfFunc(ctx, TraceLevel, msg, args...)
} }
func (l *defaultLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) { func (l *defaultLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) {
l.Logf(ctx, FatalLevel, msg, args...) l.logfFunc(ctx, FatalLevel, msg, args...)
os.Exit(1) os.Exit(1)
} }
@@ -154,19 +176,23 @@ func (l *defaultLogger) Log(ctx context.Context, level Level, args ...interface{
fields := copyFields(l.opts.Fields) fields := copyFields(l.opts.Fields)
l.RUnlock() l.RUnlock()
fields["level"] = level.String() fields = append(fields, "level", level.String())
if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok { if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok {
fields["caller"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line) fields = append(fields, "caller", fmt.Sprintf("%s:%d", logCallerfilePath(file), line))
} }
fields = append(fields, "timestamp", time.Now().Format("2006-01-02 15:04:05"))
fields["timestamp"] = time.Now().Format("2006-01-02 15:04:05")
if len(args) > 0 { if len(args) > 0 {
fields["msg"] = fmt.Sprint(args...) fields = append(fields, "msg", fmt.Sprint(args...))
} }
out := make(map[string]interface{}, len(fields)/2)
for i := 0; i < len(fields); i += 2 {
out[fields[i].(string)] = fields[i+1]
}
l.RLock() l.RLock()
_ = l.enc.Encode(fields) _ = l.enc.Encode(out)
l.RUnlock() l.RUnlock()
} }
@@ -179,35 +205,39 @@ func (l *defaultLogger) Logf(ctx context.Context, level Level, msg string, args
fields := copyFields(l.opts.Fields) fields := copyFields(l.opts.Fields)
l.RUnlock() l.RUnlock()
fields["level"] = level.String() fields = append(fields, "level", level.String())
if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok { if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok {
fields["caller"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line) fields = append(fields, "caller", fmt.Sprintf("%s:%d", logCallerfilePath(file), line))
} }
fields["timestamp"] = time.Now().Format("2006-01-02 15:04:05") fields = append(fields, "timestamp", time.Now().Format("2006-01-02 15:04:05"))
if len(args) > 0 { if len(args) > 0 {
fields["msg"] = fmt.Sprintf(msg, args...) fields = append(fields, "msg", fmt.Sprintf(msg, args...))
} else if msg != "" { } else if msg != "" {
fields["msg"] = msg fields = append(fields, "msg", msg)
}
out := make(map[string]interface{}, len(fields)/2)
for i := 0; i < len(fields); i += 2 {
out[fields[i].(string)] = fields[i+1]
} }
l.RLock() l.RLock()
_ = l.enc.Encode(fields) _ = l.enc.Encode(out)
l.RUnlock() l.RUnlock()
} }
func (l *defaultLogger) Options() Options { func (l *defaultLogger) Options() Options {
// not guard against options Context values return l.opts
l.RLock()
opts := l.opts
opts.Fields = copyFields(l.opts.Fields)
l.RUnlock()
return opts
} }
// NewLogger builds a new logger based on options // NewLogger builds a new logger based on options
func NewLogger(opts ...Option) Logger { func NewLogger(opts ...Option) Logger {
l := &defaultLogger{opts: NewOptions(opts...)} l := &defaultLogger{
opts: NewOptions(opts...),
}
l.logFunc = l.Log
l.logfFunc = l.Logf
l.enc = json.NewEncoder(l.opts.Out) l.enc = json.NewEncoder(l.opts.Out)
return l return l
} }

View File

@@ -1,24 +1,20 @@
package logger package logger
import (
"fmt"
)
// Level means logger level // Level means logger level
type Level int8 type Level int8
const ( const (
// TraceLevel level. Designates finer-grained informational events than the Debug. // TraceLevel level usually used to find bugs, very verbose
TraceLevel Level = iota - 2 TraceLevel Level = iota - 2
// DebugLevel level. Usually only enabled when debugging. Very verbose logging. // DebugLevel level used only when enabled debugging
DebugLevel DebugLevel
// InfoLevel level. General operational entries about what's going on inside the application. // InfoLevel level used for general info about what's going on inside the application
InfoLevel InfoLevel
// WarnLevel level. Non-critical entries that deserve eyes. // WarnLevel level used for non-critical entries
WarnLevel WarnLevel
// ErrorLevel level. Used for errors that should definitely be noted. // ErrorLevel level used for errors that should definitely be noted
ErrorLevel ErrorLevel
// FatalLevel level. Logs and then calls `os.Exit(1)`. highest level of severity. // FatalLevel level used for critical errors and then calls `os.Exit(1)`
FatalLevel FatalLevel
) )
@@ -38,7 +34,7 @@ func (l Level) String() string {
case FatalLevel: case FatalLevel:
return "fatal" return "fatal"
} }
return "" return "info"
} }
// Enabled returns true if the given level is at or above this level. // Enabled returns true if the given level is at or above this level.
@@ -46,22 +42,22 @@ func (l Level) Enabled(lvl Level) bool {
return lvl >= l return lvl >= l
} }
// GetLevel converts a level string into a logger Level value. // ParseLevel converts a level string into a logger Level value.
// returns an error if the input string does not match known values. // returns an InfoLevel if the input string does not match known values.
func GetLevel(levelStr string) (Level, error) { func ParseLevel(lvl string) Level {
switch levelStr { switch lvl {
case TraceLevel.String(): case TraceLevel.String():
return TraceLevel, nil return TraceLevel
case DebugLevel.String(): case DebugLevel.String():
return DebugLevel, nil return DebugLevel
case InfoLevel.String(): case InfoLevel.String():
return InfoLevel, nil return InfoLevel
case WarnLevel.String(): case WarnLevel.String():
return WarnLevel, nil return WarnLevel
case ErrorLevel.String(): case ErrorLevel.String():
return ErrorLevel, nil return ErrorLevel
case FatalLevel.String(): case FatalLevel.String():
return FatalLevel, nil return FatalLevel
} }
return InfoLevel, fmt.Errorf("unknown Level String: '%s', use InfoLevel", levelStr) return InfoLevel
} }

View File

@@ -1,25 +1,34 @@
// Package logger provides a log interface // Package logger provides a log interface
package logger package logger // import "go.unistack.org/micro/v3/logger"
import "context" import (
"context"
"os"
)
var ( var (
// DefaultLogger variable // DefaultLogger variable
DefaultLogger Logger = NewLogger() DefaultLogger Logger = NewLogger(WithLevel(ParseLevel(os.Getenv("MICRO_LOG_LEVEL"))))
// DefaultLevel used by logger // DefaultLevel used by logger
DefaultLevel Level = InfoLevel DefaultLevel Level = InfoLevel
// DefaultCallerSkipCount used by logger
DefaultCallerSkipCount = 2
) )
// Logger is a generic logging interface // Logger is a generic logging interface
type Logger interface { type Logger interface {
// Init initialises options // Init initialises options
Init(opts ...Option) error Init(opts ...Option) error
// Clone create logger copy with new options
Clone(opts ...Option) Logger
// V compare provided verbosity level with current log level // V compare provided verbosity level with current log level
V(level Level) bool V(level Level) bool
// Level sets the log level for logger
Level(level Level)
// The Logger options // The Logger options
Options() Options Options() Options
// Fields set fields to always be logged // Fields set fields to always be logged with keyval pairs
Fields(fields map[string]interface{}) Logger Fields(fields ...interface{}) Logger
// Info level message // Info level message
Info(ctx context.Context, args ...interface{}) Info(ctx context.Context, args ...interface{})
// Trace level message // Trace level message
@@ -52,6 +61,9 @@ type Logger interface {
String() string String() string
} }
// Field contains keyval pair
type Field interface{}
// Info writes msg to default logger on info level // Info writes msg to default logger on info level
func Info(ctx context.Context, args ...interface{}) { func Info(ctx context.Context, args ...interface{}) {
DefaultLogger.Info(ctx, args...) DefaultLogger.Info(ctx, args...)
@@ -123,6 +135,6 @@ func Init(opts ...Option) error {
} }
// Fields create logger with specific fields // Fields create logger with specific fields
func Fields(fields map[string]interface{}) Logger { func Fields(fields ...interface{}) Logger {
return DefaultLogger.Fields(fields) return DefaultLogger.Fields(fields...)
} }

View File

@@ -1,18 +1,148 @@
package logger package logger
import ( import (
"bytes"
"context" "context"
"log"
"testing" "testing"
) )
func TestContext(t *testing.T) {
ctx := context.TODO()
buf := bytes.NewBuffer(nil)
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
if err := l.Init(); err != nil {
t.Fatal(err)
}
nl, ok := FromContext(NewContext(ctx, l.Fields("key", "val")))
if !ok {
t.Fatal("context without logger")
}
nl.Info(ctx, "message")
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
}
}
func TestFields(t *testing.T) {
ctx := context.TODO()
buf := bytes.NewBuffer(nil)
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
if err := l.Init(); err != nil {
t.Fatal(err)
}
l.Fields("key", "val").Info(ctx, "message")
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
}
}
func TestClone(t *testing.T) {
ctx := context.TODO()
buf := bytes.NewBuffer(nil)
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
if err := l.Init(); err != nil {
t.Fatal(err)
}
nl := l.Clone(WithLevel(ErrorLevel))
if err := nl.Init(); err != nil {
t.Fatal(err)
}
nl.Info(ctx, "info message")
if len(buf.Bytes()) != 0 {
t.Fatal("message must not be logged")
}
l.Info(ctx, "info message")
if len(buf.Bytes()) == 0 {
t.Fatal("message must be logged")
}
}
func TestRedirectStdLogger(t *testing.T) {
buf := bytes.NewBuffer(nil)
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
if err := l.Init(); err != nil {
t.Fatal(err)
}
fn := RedirectStdLogger(l, ErrorLevel)
defer fn()
log.Print("test")
if !bytes.Contains(buf.Bytes(), []byte(`"level":"error","msg":"test","timestamp"`)) {
t.Fatalf("logger error, buf %s", buf.Bytes())
}
}
func TestStdLogger(t *testing.T) {
buf := bytes.NewBuffer(nil)
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
if err := l.Init(); err != nil {
t.Fatal(err)
}
lg := NewStdLogger(l, ErrorLevel)
lg.Print("test")
if !bytes.Contains(buf.Bytes(), []byte(`"level":"error","msg":"test","timestamp"`)) {
t.Fatalf("logger error, buf %s", buf.Bytes())
}
}
func TestLogger(t *testing.T) { func TestLogger(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
l := NewLogger(WithLevel(TraceLevel)) buf := bytes.NewBuffer(nil)
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
if err := l.Init(); err != nil { if err := l.Init(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
l.Trace(ctx, "trace_msg1") l.Trace(ctx, "trace_msg1")
l.Warn(ctx, "warn_msg1") l.Warn(ctx, "warn_msg1")
l.Fields(map[string]interface{}{"error": "test"}).Info(ctx, "error message") l.Fields("error", "test").Info(ctx, "error message")
l.Warn(ctx, "first", " ", "second") l.Warn(ctx, "first", " ", "second")
if !bytes.Contains(buf.Bytes(), []byte(`"level":"trace","msg":"trace_msg1"`)) {
t.Fatalf("logger error, buf %s", buf.Bytes())
}
if !bytes.Contains(buf.Bytes(), []byte(`"warn","msg":"warn_msg1"`)) {
t.Fatalf("logger error, buf %s", buf.Bytes())
}
if !bytes.Contains(buf.Bytes(), []byte(`"error":"test","level":"info","msg":"error message"`)) {
t.Fatalf("logger error, buf %s", buf.Bytes())
}
if !bytes.Contains(buf.Bytes(), []byte(`"level":"warn","msg":"first second"`)) {
t.Fatalf("logger error, buf %s", buf.Bytes())
}
}
func TestLoggerWrapper(t *testing.T) {
ctx := context.TODO()
buf := bytes.NewBuffer(nil)
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
if err := l.Init(WrapLogger(NewOmitWrapper())); err != nil {
t.Fatal(err)
}
type secret struct {
Name string
Passw string `logger:"omit"`
}
s := &secret{Name: "name", Passw: "secret"}
l.Errorf(ctx, "test %#+v", s)
if !bytes.Contains(buf.Bytes(), []byte(`logger.secret{Name:\"name\", Passw:\"\"}"`)) {
t.Fatalf("omit not works, struct: %v, output: %s", s, buf.Bytes())
}
}
func TestOmitLoggerWrapper(t *testing.T) {
ctx := context.TODO()
buf := bytes.NewBuffer(nil)
l := NewOmitLogger(NewLogger(WithLevel(TraceLevel), WithOutput(buf)))
if err := l.Init(); err != nil {
t.Fatal(err)
}
type secret struct {
Name string
Passw string `logger:"omit"`
}
s := &secret{Name: "name", Passw: "secret"}
l.Errorf(ctx, "test %#+v", s)
if !bytes.Contains(buf.Bytes(), []byte(`logger.secret{Name:\"name\", Passw:\"\"}"`)) {
t.Fatalf("omit not works, struct: %v, output: %s", s, buf.Bytes())
}
} }

View File

@@ -16,22 +16,24 @@ type Options struct {
// Context holds exernal options // Context holds exernal options
Context context.Context Context context.Context
// Fields holds additional metadata // Fields holds additional metadata
Fields map[string]interface{} Fields []interface{}
// Name holds the logger name // Name holds the logger name
Name string Name string
// CallerSkipCount number of frmaes to skip // Wrappers logger wrapper that called before actual Log/Logf function
CallerSkipCount int Wrappers []Wrapper
// The logging level the logger should log // The logging level the logger should log
Level Level Level Level
// CallerSkipCount number of frmaes to skip
CallerSkipCount int
} }
// NewOptions creates new options struct // NewOptions creates new options struct
func NewOptions(opts ...Option) Options { func NewOptions(opts ...Option) Options {
options := Options{ options := Options{
Level: DefaultLevel, Level: DefaultLevel,
Fields: make(map[string]interface{}), Fields: make([]interface{}, 0, 6),
Out: os.Stderr, Out: os.Stderr,
CallerSkipCount: 0, CallerSkipCount: DefaultCallerSkipCount,
Context: context.Background(), Context: context.Background(),
} }
for _, o := range opts { for _, o := range opts {
@@ -41,7 +43,7 @@ func NewOptions(opts ...Option) Options {
} }
// WithFields set default fields for the logger // WithFields set default fields for the logger
func WithFields(fields map[string]interface{}) Option { func WithFields(fields ...interface{}) Option {
return func(o *Options) { return func(o *Options) {
o.Fields = fields o.Fields = fields
} }
@@ -81,3 +83,10 @@ func WithName(n string) Option {
o.Name = n o.Name = n
} }
} }
// WrapLogger adds a logger Wrapper to a list of options passed into the logger
func WrapLogger(w Wrapper) Option {
return func(o *Options) {
o.Wrappers = append(o.Wrappers, w)
}
}

37
logger/stdlogger.go Normal file
View File

@@ -0,0 +1,37 @@
package logger
import (
"bytes"
"log"
)
type stdLogger struct {
l Logger
level Level
}
// NewStdLogger returns new *log.Logger baked by logger.Logger implementation
func NewStdLogger(l Logger, level Level) *log.Logger {
return log.New(&stdLogger{l: l, level: level}, "" /* prefix */, 0 /* flags */)
}
func (sl *stdLogger) Write(p []byte) (int, error) {
p = bytes.TrimSpace(p)
sl.l.Log(sl.l.Options().Context, sl.level, string(p))
return len(p), nil
}
// RedirectStdLogger replace *log.Logger with logger.Logger implementation
func RedirectStdLogger(l Logger, level Level) func() {
flags := log.Flags()
prefix := log.Prefix()
writer := log.Writer()
log.SetFlags(0)
log.SetPrefix("")
log.SetOutput(&stdLogger{l: l, level: level})
return func() {
log.SetFlags(flags)
log.SetPrefix(prefix)
log.SetOutput(writer)
}
}

166
logger/wrapper.go Normal file
View File

@@ -0,0 +1,166 @@
package logger // import "go.unistack.org/micro/v3/logger/wrapper"
import (
"context"
"reflect"
rutil "go.unistack.org/micro/v3/util/reflect"
)
// LogFunc function used for Log method
type LogFunc func(ctx context.Context, level Level, args ...interface{})
// LogfFunc function used for Logf method
type LogfFunc func(ctx context.Context, level Level, msg string, args ...interface{})
type Wrapper interface {
// Log logs message with needed level
Log(LogFunc) LogFunc
// Logf logs message with needed level
Logf(LogfFunc) LogfFunc
}
var _ Logger = &omitLogger{}
type omitLogger struct {
l Logger
}
func NewOmitLogger(l Logger) Logger {
return &omitLogger{l: l}
}
func (w *omitLogger) Init(opts ...Option) error {
return w.l.Init(append(opts, WrapLogger(NewOmitWrapper()))...)
}
func (w *omitLogger) V(level Level) bool {
return w.l.V(level)
}
func (w *omitLogger) Level(level Level) {
w.l.Level(level)
}
func (w *omitLogger) Clone(opts ...Option) Logger {
return w.l.Clone(opts...)
}
func (w *omitLogger) Options() Options {
return w.l.Options()
}
func (w *omitLogger) Fields(fields ...interface{}) Logger {
return w.l.Fields(fields...)
}
func (w *omitLogger) Info(ctx context.Context, args ...interface{}) {
w.l.Info(ctx, args...)
}
func (w *omitLogger) Trace(ctx context.Context, args ...interface{}) {
w.l.Trace(ctx, args...)
}
func (w *omitLogger) Debug(ctx context.Context, args ...interface{}) {
w.l.Debug(ctx, args...)
}
func (w *omitLogger) Warn(ctx context.Context, args ...interface{}) {
w.l.Warn(ctx, args...)
}
func (w *omitLogger) Error(ctx context.Context, args ...interface{}) {
w.l.Error(ctx, args...)
}
func (w *omitLogger) Fatal(ctx context.Context, args ...interface{}) {
w.l.Fatal(ctx, args...)
}
func (w *omitLogger) Infof(ctx context.Context, msg string, args ...interface{}) {
w.l.Infof(ctx, msg, args...)
}
func (w *omitLogger) Tracef(ctx context.Context, msg string, args ...interface{}) {
w.l.Tracef(ctx, msg, args...)
}
func (w *omitLogger) Debugf(ctx context.Context, msg string, args ...interface{}) {
w.l.Debugf(ctx, msg, args...)
}
func (w *omitLogger) Warnf(ctx context.Context, msg string, args ...interface{}) {
w.l.Warnf(ctx, msg, args...)
}
func (w *omitLogger) Errorf(ctx context.Context, msg string, args ...interface{}) {
w.l.Errorf(ctx, msg, args...)
}
func (w *omitLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) {
w.l.Fatalf(ctx, msg, args...)
}
func (w *omitLogger) Log(ctx context.Context, level Level, args ...interface{}) {
w.l.Log(ctx, level, args...)
}
func (w *omitLogger) Logf(ctx context.Context, level Level, msg string, args ...interface{}) {
w.l.Logf(ctx, level, msg, args...)
}
func (w *omitLogger) String() string {
return w.l.String()
}
type omitWrapper struct{}
func NewOmitWrapper() Wrapper {
return &omitWrapper{}
}
func getArgs(args []interface{}) []interface{} {
nargs := make([]interface{}, 0, len(args))
var err error
for _, arg := range args {
val := reflect.ValueOf(arg)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
narg := arg
if val.Kind() != reflect.Struct {
nargs = append(nargs, narg)
continue
}
if narg, err = rutil.Zero(arg); err != nil {
nargs = append(nargs, narg)
continue
}
rutil.CopyDefaults(narg, arg)
if flds, ferr := rutil.StructFields(narg); ferr == nil {
for _, fld := range flds {
if tv, ok := fld.Field.Tag.Lookup("logger"); ok && tv == "omit" {
fld.Value.Set(reflect.Zero(fld.Value.Type()))
}
}
}
nargs = append(nargs, narg)
}
return nargs
}
func (w *omitWrapper) Log(fn LogFunc) LogFunc {
return func(ctx context.Context, level Level, args ...interface{}) {
fn(ctx, level, getArgs(args)...)
}
}
func (w *omitWrapper) Logf(fn LogfFunc) LogfFunc {
return func(ctx context.Context, level Level, msg string, args ...interface{}) {
fn(ctx, level, msg, getArgs(args)...)
}
}

View File

@@ -5,12 +5,13 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/unistack-org/micro/v3/client" "go.unistack.org/micro/v3/client"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/logger"
"github.com/unistack-org/micro/v3/server" "go.unistack.org/micro/v3/server"
) )
var ( var (
// DefaultClientCallObserver called by wrapper in client Call
DefaultClientCallObserver = func(ctx context.Context, req client.Request, rsp interface{}, opts []client.CallOption, err error) []string { DefaultClientCallObserver = func(ctx context.Context, req client.Request, rsp interface{}, opts []client.CallOption, err error) []string {
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()} labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
if err != nil { if err != nil {
@@ -19,6 +20,7 @@ var (
return labels return labels
} }
// DefaultClientStreamObserver called by wrapper in client Stream
DefaultClientStreamObserver = func(ctx context.Context, req client.Request, opts []client.CallOption, stream client.Stream, err error) []string { DefaultClientStreamObserver = func(ctx context.Context, req client.Request, opts []client.CallOption, stream client.Stream, err error) []string {
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()} labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
if err != nil { if err != nil {
@@ -27,6 +29,7 @@ var (
return labels return labels
} }
// DefaultClientPublishObserver called by wrapper in client Publish
DefaultClientPublishObserver = func(ctx context.Context, msg client.Message, opts []client.PublishOption, err error) []string { DefaultClientPublishObserver = func(ctx context.Context, msg client.Message, opts []client.PublishOption, err error) []string {
labels := []string{"endpoint", msg.Topic()} labels := []string{"endpoint", msg.Topic()}
if err != nil { if err != nil {
@@ -35,6 +38,7 @@ var (
return labels return labels
} }
// DefaultServerHandlerObserver called by wrapper in server Handler
DefaultServerHandlerObserver = func(ctx context.Context, req server.Request, rsp interface{}, err error) []string { DefaultServerHandlerObserver = func(ctx context.Context, req server.Request, rsp interface{}, err error) []string {
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()} labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
if err != nil { if err != nil {
@@ -43,6 +47,7 @@ var (
return labels return labels
} }
// DefaultServerSubscriberObserver called by wrapper in server Subscriber
DefaultServerSubscriberObserver = func(ctx context.Context, msg server.Message, err error) []string { DefaultServerSubscriberObserver = func(ctx context.Context, msg server.Message, err error) []string {
labels := []string{"endpoint", msg.Topic()} labels := []string{"endpoint", msg.Topic()}
if err != nil { if err != nil {
@@ -51,6 +56,7 @@ var (
return labels return labels
} }
// DefaultClientCallFuncObserver called by wrapper in client CallFunc
DefaultClientCallFuncObserver = func(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions, err error) []string { DefaultClientCallFuncObserver = func(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions, err error) []string {
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()} labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
if err != nil { if err != nil {
@@ -59,6 +65,7 @@ var (
return labels return labels
} }
// DefaultSkipEndpoints wrapper not called for this endpoints
DefaultSkipEndpoints = []string{"Meter.Metrics"} DefaultSkipEndpoints = []string{"Meter.Metrics"}
) )
@@ -71,11 +78,17 @@ type lWrapper struct {
} }
type ( type (
ClientCallObserver func(context.Context, client.Request, interface{}, []client.CallOption, error) []string // ClientCallObserver func signature
ClientStreamObserver func(context.Context, client.Request, []client.CallOption, client.Stream, error) []string ClientCallObserver func(context.Context, client.Request, interface{}, []client.CallOption, error) []string
ClientPublishObserver func(context.Context, client.Message, []client.PublishOption, error) []string // ClientStreamObserver func signature
ClientCallFuncObserver func(context.Context, string, client.Request, interface{}, client.CallOptions, error) []string ClientStreamObserver func(context.Context, client.Request, []client.CallOption, client.Stream, error) []string
ServerHandlerObserver func(context.Context, server.Request, interface{}, error) []string // ClientPublishObserver func signature
ClientPublishObserver func(context.Context, client.Message, []client.PublishOption, error) []string
// ClientCallFuncObserver func signature
ClientCallFuncObserver func(context.Context, string, client.Request, interface{}, client.CallOptions, error) []string
// ServerHandlerObserver func signature
ServerHandlerObserver func(context.Context, server.Request, interface{}, error) []string
// ServerSubscriberObserver func signature
ServerSubscriberObserver func(context.Context, server.Message, error) []string ServerSubscriberObserver func(context.Context, server.Message, error) []string
) )

View File

@@ -1,13 +1,25 @@
// Package metadata is a way of defining message headers // Package metadata is a way of defining message headers
package metadata package metadata // import "go.unistack.org/micro/v3/metadata"
import ( import (
"net/textproto" "net/textproto"
"sort" "sort"
) )
// HeaderPrefix for all headers passed var (
var HeaderPrefix = "Micro-" // HeaderTopic is the header name that contains topic name
HeaderTopic = "Micro-Topic"
// HeaderContentType specifies content type of message
HeaderContentType = "Content-Type"
// HeaderEndpoint specifies endpoint in service
HeaderEndpoint = "Micro-Endpoint"
// HeaderService specifies service
HeaderService = "Micro-Service"
// HeaderTimeout specifies timeout of operation
HeaderTimeout = "Micro-Timeout"
// HeaderAuthorization specifies Authorization header
HeaderAuthorization = "Authorization"
)
// Metadata is our way of representing request headers internally. // Metadata is our way of representing request headers internally.
// They're used at the RPC level and translate back and forth // They're used at the RPC level and translate back and forth
@@ -95,13 +107,16 @@ func New(size int) Metadata {
// Merge merges metadata to existing metadata, overwriting if specified // Merge merges metadata to existing metadata, overwriting if specified
func Merge(omd Metadata, mmd Metadata, overwrite bool) Metadata { func Merge(omd Metadata, mmd Metadata, overwrite bool) Metadata {
var ok bool
nmd := Copy(omd) nmd := Copy(omd)
for key, val := range mmd { for key, val := range mmd {
if _, ok := nmd[key]; ok && !overwrite { _, ok = nmd[key]
// skip switch {
} else if val != "" { case ok && !overwrite:
continue
case val != "":
nmd.Set(key, val) nmd.Set(key, val)
} else { case ok && val == "":
nmd.Del(key) nmd.Del(key)
} }
} }

View File

@@ -1,3 +1,12 @@
package meter package meter
//go:generate protoc -I./handler -I../ -I/home/vtolstov/.cache/go-path/pkg/mod/github.com/unistack-org/micro-proto@v0.0.1 --micro_out=components=micro|http|server,standalone=false,debug=true,paths=source_relative:./handler handler/handler.proto //go:generate sh -c "protoc -I./handler -I../ -I$(go list -f '{{ .Dir }}' -m go.unistack.org/micro-proto/v3) --go-micro_out='components=micro|http|server',standalone=false,debug=true,paths=source_relative:./handler handler/handler.proto"
import (
// import required packages
_ "go.unistack.org/micro-proto/v3/api"
// import required packages
_ "go.unistack.org/micro-proto/v3/openapiv3"
)

View File

@@ -1,12 +1,12 @@
package handler package handler // import "go.unistack.org/micro/v3/meter/handler"
import ( import (
"bytes" "bytes"
"context" "context"
"github.com/unistack-org/micro/v3/codec" "go.unistack.org/micro/v3/codec"
"github.com/unistack-org/micro/v3/errors" "go.unistack.org/micro/v3/errors"
"github.com/unistack-org/micro/v3/meter" "go.unistack.org/micro/v3/meter"
) )
// guard to fail early // guard to fail early

View File

@@ -1,27 +1,23 @@
syntax = "proto3"; syntax = "proto3";
package micro.meter.handler; package micro.meter.handler;
option go_package = "github.com/unistack-org/micro/v3/meter/handler;handler"; option go_package = "go.unistack.org/micro/v3/meter/handler;handler";
import "api/annotations.proto"; import "api/annotations.proto";
import "openapiv2/annotations.proto"; import "openapiv3/annotations.proto";
import "codec/frame.proto"; import "codec/frame.proto";
service Meter { service Meter {
rpc Metrics(micro.codec.Frame) returns (micro.codec.Frame) { rpc Metrics(micro.codec.Frame) returns (micro.codec.Frame) {
option (micro.openapiv2.openapiv2_operation) = { option (micro.openapiv3.openapiv3_operation) = {
operation_id: "Metrics"; operation_id: "Metrics";
responses: { responses: {
key: "default"; default: {
value: { reference: {
description: "Error response"; _ref: "micro.codec.Frame";
schema: { };
json_schema: { };
ref: "micro.codec.Frame"; };
}
}
}
}
}; };
option (micro.api.http) = { get: "/metrics"; }; option (micro.api.http) = { get: "/metrics"; };
}; };

View File

@@ -1,22 +1,30 @@
// Code generated by protoc-gen-micro // Code generated by protoc-gen-go-micro. DO NOT EDIT.
// protoc-gen-go-micro version: v3.5.3
// source: handler.proto // source: handler.proto
package handler package handler
import ( import (
context "context" context "context"
api "github.com/unistack-org/micro/v3/api" api "go.unistack.org/micro/v3/api"
codec "github.com/unistack-org/micro/v3/codec" codec "go.unistack.org/micro/v3/codec"
) )
func NewMeterEndpoints() []*api.Endpoint { var (
return []*api.Endpoint{ MeterName = "Meter"
&api.Endpoint{
MeterEndpoints = []api.Endpoint{
{
Name: "Meter.Metrics", Name: "Meter.Metrics",
Path: []string{"/metrics"}, Path: []string{"/metrics"},
Method: []string{"GET"}, Method: []string{"GET"},
Handler: "rpc", Handler: "rpc",
}, },
} }
)
func NewMeterEndpoints() []api.Endpoint {
return MeterEndpoints
} }
type MeterServer interface { type MeterServer interface {

View File

@@ -1,12 +1,14 @@
// Code generated by protoc-gen-micro // Code generated by protoc-gen-go-micro. DO NOT EDIT.
// protoc-gen-go-micro version: v3.5.3
// source: handler.proto // source: handler.proto
package handler package handler
import ( import (
context "context" context "context"
api "github.com/unistack-org/micro/v3/api" api "go.unistack.org/micro/v3/api"
codec "github.com/unistack-org/micro/v3/codec" codec "go.unistack.org/micro/v3/codec"
server "github.com/unistack-org/micro/v3/server" server "go.unistack.org/micro/v3/server"
) )
type meterServer struct { type meterServer struct {
@@ -26,8 +28,8 @@ func RegisterMeterServer(s server.Server, sh MeterServer, opts ...server.Handler
} }
h := &meterServer{sh} h := &meterServer{sh}
var nopts []server.HandlerOption var nopts []server.HandlerOption
for _, endpoint := range NewMeterEndpoints() { for _, endpoint := range MeterEndpoints {
nopts = append(nopts, api.WithEndpoint(endpoint)) nopts = append(nopts, api.WithEndpoint(&endpoint))
} }
return s.Handle(s.NewHandler(&Meter{h}, append(nopts, opts...)...)) return s.Handle(s.NewHandler(&Meter{h}, append(nopts, opts...)...))
} }

View File

@@ -1,10 +1,11 @@
// Package meter is for instrumentation // Package meter is for instrumentation
package meter package meter // import "go.unistack.org/micro/v3/meter"
import ( import (
"io" "io"
"reflect"
"sort" "sort"
"strconv"
"strings"
"time" "time"
) )
@@ -27,17 +28,31 @@ var (
// Meter is an interface for collecting and instrumenting metrics // Meter is an interface for collecting and instrumenting metrics
type Meter interface { type Meter interface {
// Name returns meter name
Name() string Name() string
// Init initialize meter
Init(opts ...Option) error Init(opts ...Option) error
// Clone create meter copy with new options
Clone(opts ...Option) Meter
// Counter get or create counter
Counter(name string, labels ...string) Counter Counter(name string, labels ...string) Counter
// FloatCounter get or create float counter
FloatCounter(name string, labels ...string) FloatCounter FloatCounter(name string, labels ...string) FloatCounter
// Gauge get or create gauge
Gauge(name string, fn func() float64, labels ...string) Gauge Gauge(name string, fn func() float64, labels ...string) Gauge
// Set create new meter metrics set
Set(opts ...Option) Meter Set(opts ...Option) Meter
// Histogram get or create histogram
Histogram(name string, labels ...string) Histogram Histogram(name string, labels ...string) Histogram
// Summary get or create summary
Summary(name string, labels ...string) Summary Summary(name string, labels ...string) Summary
// SummaryExt get or create summary with spcified quantiles and window time
SummaryExt(name string, window time.Duration, quantiles []float64, labels ...string) Summary SummaryExt(name string, window time.Duration, quantiles []float64, labels ...string) Summary
// Write writes metrics to io.Writer
Write(w io.Writer, opts ...Option) error Write(w io.Writer, opts ...Option) error
// Options returns meter options
Options() Options Options() Options
// String return meter type
String() string String() string
} }
@@ -77,36 +92,62 @@ type Summary interface {
UpdateDuration(time.Time) UpdateDuration(time.Time)
} }
// sort labels alphabeticaly by label name
type byKey []string type byKey []string
func (k byKey) Len() int { return len(k) / 2 } func (k byKey) Len() int { return len(k) / 2 }
func (k byKey) Less(i, j int) bool { return k[i*2] < k[j*2] } func (k byKey) Less(i, j int) bool { return k[i*2] < k[j*2] }
func (k byKey) Swap(i, j int) { func (k byKey) Swap(i, j int) {
k[i*2], k[i*2+1], k[j*2], k[j*2+1] = k[j*2], k[j*2+1], k[i*2], k[i*2+1] k[i*2], k[j*2] = k[j*2], k[i*2]
k[i*2+1], k[j*2+1] = k[j*2+1], k[i*2+1]
} }
func Sort(slice *[]string) { // BuildLabels used to sort labels and delete duplicates.
bk := byKey(*slice) // Last value wins in case of duplicate label keys.
if bk.Len() <= 1 { func BuildLabels(labels ...string) []string {
return if len(labels)%2 == 1 {
labels = labels[:len(labels)-1]
} }
sort.Sort(bk) sort.Sort(byKey(labels))
v := reflect.ValueOf(slice).Elem() return labels
cnt := 0 }
key := 0
val := 1 // BuildName used to combine metric with labels.
for key < v.Len() { // If labels count is odd, drop last element
if len(bk) > key+2 && bk[key] == bk[key+2] { func BuildName(name string, labels ...string) string {
key += 2 if len(labels)%2 == 1 {
val += 2 labels = labels[:len(labels)-1]
continue }
}
v.Index(cnt).Set(v.Index(key)) if len(labels) > 2 {
cnt++ sort.Sort(byKey(labels))
v.Index(cnt).Set(v.Index(val))
cnt++ idx := 0
key += 2 for {
val += 2 if labels[idx] == labels[idx+2] {
} copy(labels[idx:], labels[idx+2:])
v.SetLen(cnt) labels = labels[:len(labels)-2]
} else {
idx += 2
}
if idx+2 >= len(labels) {
break
}
}
}
var b strings.Builder
_, _ = b.WriteString(name)
_, _ = b.WriteRune('{')
for idx := 0; idx < len(labels); idx += 2 {
if idx > 0 {
_, _ = b.WriteRune(',')
}
_, _ = b.WriteString(labels[idx])
_, _ = b.WriteString(`=`)
_, _ = b.WriteString(strconv.Quote(labels[idx+1]))
}
_, _ = b.WriteRune('}')
return b.String()
} }

View File

@@ -14,11 +14,57 @@ func TestNoopMeter(t *testing.T) {
cnt.Inc() cnt.Inc()
} }
func TestLabelsSort(t *testing.T) { func testEq(a, b []string) bool {
ls := []string{"server", "http", "register", "mdns", "broker", "broker1", "broker", "broker2", "server", "tcp"} if len(a) != len(b) {
Sort(&ls) return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
if ls[0] != "broker" || ls[1] != "broker2" { func TestBuildLabels(t *testing.T) {
t.Fatalf("sort error: %v", ls) type testData struct {
src []string
dst []string
}
data := []testData{
{
src: []string{"zerolabel", "value3", "firstlabel", "value2"},
dst: []string{"firstlabel", "value2", "zerolabel", "value3"},
},
}
for _, d := range data {
if !testEq(d.dst, BuildLabels(d.src...)) {
t.Fatalf("slices not properly sorted: %v %v", d.dst, d.src)
}
}
}
func TestBuildName(t *testing.T) {
data := map[string][]string{
`my_metric{firstlabel="value2",zerolabel="value3"}`: {
"my_metric",
"zerolabel", "value3", "firstlabel", "value2",
},
`my_metric{broker="broker2",register="mdns",server="tcp"}`: {
"my_metric",
"broker", "broker1", "broker", "broker2", "server", "http", "server", "tcp", "register", "mdns",
},
`my_metric{aaa="aaa"}`: {
"my_metric",
"aaa", "aaa",
},
}
for e, d := range data {
if x := BuildName(d[0], d[1:]...); x != e {
t.Fatalf("expect: %s, result: %s", e, x)
}
} }
} }

View File

@@ -15,6 +15,15 @@ func NewMeter(opts ...Option) Meter {
return &noopMeter{opts: NewOptions(opts...)} return &noopMeter{opts: NewOptions(opts...)}
} }
// Clone return old meter with new options
func (r *noopMeter) Clone(opts ...Option) Meter {
options := r.opts
for _, o := range opts {
o(&options)
}
return &noopMeter{opts: options}
}
func (r *noopMeter) Name() string { func (r *noopMeter) Name() string {
return r.opts.Name return r.opts.Name
} }

View File

@@ -3,13 +3,13 @@ package meter
import ( import (
"context" "context"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/logger"
) )
// Option powers the configuration for metrics implementations: // Option powers the configuration for metrics implementations:
type Option func(*Options) type Option func(*Options)
// Options for metrics implementations: // Options for metrics implementations
type Options struct { type Options struct {
// Logger used for logging // Logger used for logging
Logger logger.Logger Logger logger.Logger
@@ -51,6 +51,20 @@ func NewOptions(opt ...Option) Options {
return opts return opts
} }
// LabelPrefix sets the labels prefix
func LabelPrefix(pref string) Option {
return func(o *Options) {
o.LabelPrefix = pref
}
}
// MetricPrefix sets the metric prefix
func MetricPrefix(pref string) Option {
return func(o *Options) {
o.MetricPrefix = pref
}
}
// Context sets the metrics context // Context sets the metrics context
func Context(ctx context.Context) Option { func Context(ctx context.Context) Option {
return func(o *Options) { return func(o *Options) {
@@ -88,6 +102,7 @@ func Logger(l logger.Logger) Option {
} }
} }
// Labels sets the meter labels
func Labels(ls ...string) Option { func Labels(ls ...string) Option {
return func(o *Options) { return func(o *Options) {
o.Labels = append(o.Labels, ls...) o.Labels = append(o.Labels, ls...)

View File

@@ -1,35 +1,48 @@
package wrapper package wrapper // import "go.unistack.org/micro/v3/meter/wrapper"
import ( import (
"context" "context"
"fmt" "fmt"
"time" "time"
"github.com/unistack-org/micro/v3/client" "go.unistack.org/micro/v3/client"
"github.com/unistack-org/micro/v3/meter" "go.unistack.org/micro/v3/meter"
"github.com/unistack-org/micro/v3/server" "go.unistack.org/micro/v3/server"
) )
var ( var (
ClientRequestDurationSeconds = "client_request_duration_seconds" // ClientRequestDurationSeconds specifies meter metric name
ClientRequestDurationSeconds = "client_request_duration_seconds"
// ClientRequestLatencyMicroseconds specifies meter metric name
ClientRequestLatencyMicroseconds = "client_request_latency_microseconds" ClientRequestLatencyMicroseconds = "client_request_latency_microseconds"
ClientRequestTotal = "client_request_total" // ClientRequestTotal specifies meter metric name
ClientRequestInflight = "client_request_inflight" ClientRequestTotal = "client_request_total"
// ClientRequestInflight specifies meter metric name
ServerRequestDurationSeconds = "server_request_duration_seconds" ClientRequestInflight = "client_request_inflight"
// ServerRequestDurationSeconds specifies meter metric name
ServerRequestDurationSeconds = "server_request_duration_seconds"
// ServerRequestLatencyMicroseconds specifies meter metric name
ServerRequestLatencyMicroseconds = "server_request_latency_microseconds" ServerRequestLatencyMicroseconds = "server_request_latency_microseconds"
ServerRequestTotal = "server_request_total" // ServerRequestTotal specifies meter metric name
ServerRequestInflight = "server_request_inflight" ServerRequestTotal = "server_request_total"
// ServerRequestInflight specifies meter metric name
PublishMessageDurationSeconds = "publish_message_duration_seconds" ServerRequestInflight = "server_request_inflight"
// PublishMessageDurationSeconds specifies meter metric name
PublishMessageDurationSeconds = "publish_message_duration_seconds"
// PublishMessageLatencyMicroseconds specifies meter metric name
PublishMessageLatencyMicroseconds = "publish_message_latency_microseconds" PublishMessageLatencyMicroseconds = "publish_message_latency_microseconds"
PublishMessageTotal = "publish_message_total" // PublishMessageTotal specifies meter metric name
PublishMessageInflight = "publish_message_inflight" PublishMessageTotal = "publish_message_total"
// PublishMessageInflight specifies meter metric name
SubscribeMessageDurationSeconds = "subscribe_message_duration_seconds" PublishMessageInflight = "publish_message_inflight"
// SubscribeMessageDurationSeconds specifies meter metric name
SubscribeMessageDurationSeconds = "subscribe_message_duration_seconds"
// SubscribeMessageLatencyMicroseconds specifies meter metric name
SubscribeMessageLatencyMicroseconds = "subscribe_message_latency_microseconds" SubscribeMessageLatencyMicroseconds = "subscribe_message_latency_microseconds"
SubscribeMessageTotal = "subscribe_message_total" // SubscribeMessageTotal specifies meter metric name
SubscribeMessageInflight = "subscribe_message_inflight" SubscribeMessageTotal = "subscribe_message_total"
// SubscribeMessageInflight specifies meter metric name
SubscribeMessageInflight = "subscribe_message_inflight"
labelSuccess = "success" labelSuccess = "success"
labelFailure = "failure" labelFailure = "failure"
@@ -40,14 +53,17 @@ var (
DefaultSkipEndpoints = []string{"Meter.Metrics"} DefaultSkipEndpoints = []string{"Meter.Metrics"}
) )
// Options struct
type Options struct { type Options struct {
Meter meter.Meter Meter meter.Meter
lopts []meter.Option lopts []meter.Option
SkipEndpoints []string SkipEndpoints []string
} }
// Option func signature
type Option func(*Options) type Option func(*Options)
// NewOptions creates new Options struct
func NewOptions(opts ...Option) Options { func NewOptions(opts ...Option) Options {
options := Options{ options := Options{
Meter: meter.DefaultMeter, Meter: meter.DefaultMeter,
@@ -60,30 +76,35 @@ func NewOptions(opts ...Option) Options {
return options return options
} }
// ServiceName passes service name to meter label
func ServiceName(name string) Option { func ServiceName(name string) Option {
return func(o *Options) { return func(o *Options) {
o.lopts = append(o.lopts, meter.Labels("name", name)) o.lopts = append(o.lopts, meter.Labels("name", name))
} }
} }
// ServiceVersion passes service version to meter label
func ServiceVersion(version string) Option { func ServiceVersion(version string) Option {
return func(o *Options) { return func(o *Options) {
o.lopts = append(o.lopts, meter.Labels("version", version)) o.lopts = append(o.lopts, meter.Labels("version", version))
} }
} }
// ServiceID passes service id to meter label
func ServiceID(id string) Option { func ServiceID(id string) Option {
return func(o *Options) { return func(o *Options) {
o.lopts = append(o.lopts, meter.Labels("id", id)) o.lopts = append(o.lopts, meter.Labels("id", id))
} }
} }
// Meter passes meter
func Meter(m meter.Meter) Option { func Meter(m meter.Meter) Option {
return func(o *Options) { return func(o *Options) {
o.Meter = m o.Meter = m
} }
} }
// SkipEndoints add endpoint to skip
func SkipEndoints(eps ...string) Option { func SkipEndoints(eps ...string) Option {
return func(o *Options) { return func(o *Options) {
o.SkipEndpoints = append(o.SkipEndpoints, eps...) o.SkipEndpoints = append(o.SkipEndpoints, eps...)
@@ -96,6 +117,7 @@ type wrapper struct {
opts Options opts Options
} }
// NewClientWrapper create new client wrapper
func NewClientWrapper(opts ...Option) client.Wrapper { func NewClientWrapper(opts ...Option) client.Wrapper {
return func(c client.Client) client.Client { return func(c client.Client) client.Client {
handler := &wrapper{ handler := &wrapper{
@@ -106,6 +128,7 @@ func NewClientWrapper(opts ...Option) client.Wrapper {
} }
} }
// NewCallWrapper create new call wrapper
func NewCallWrapper(opts ...Option) client.CallWrapper { func NewCallWrapper(opts ...Option) client.CallWrapper {
return func(fn client.CallFunc) client.CallFunc { return func(fn client.CallFunc) client.CallFunc {
handler := &wrapper{ handler := &wrapper{
@@ -231,6 +254,7 @@ func (w *wrapper) Publish(ctx context.Context, p client.Message, opts ...client.
return err return err
} }
// NewHandlerWrapper create new server handler wrapper
func NewHandlerWrapper(opts ...Option) server.HandlerWrapper { func NewHandlerWrapper(opts ...Option) server.HandlerWrapper {
handler := &wrapper{ handler := &wrapper{
opts: NewOptions(opts...), opts: NewOptions(opts...),
@@ -240,7 +264,7 @@ func NewHandlerWrapper(opts ...Option) server.HandlerWrapper {
func (w *wrapper) HandlerFunc(fn server.HandlerFunc) server.HandlerFunc { func (w *wrapper) HandlerFunc(fn server.HandlerFunc) server.HandlerFunc {
return func(ctx context.Context, req server.Request, rsp interface{}) error { return func(ctx context.Context, req server.Request, rsp interface{}) error {
endpoint := req.Endpoint() endpoint := req.Service() + "." + req.Endpoint()
for _, ep := range w.opts.SkipEndpoints { for _, ep := range w.opts.SkipEndpoints {
if ep == endpoint { if ep == endpoint {
return fn(ctx, req, rsp) return fn(ctx, req, rsp)
@@ -270,6 +294,7 @@ func (w *wrapper) HandlerFunc(fn server.HandlerFunc) server.HandlerFunc {
} }
} }
// NewSubscriberWrapper create server subscribe wrapper
func NewSubscriberWrapper(opts ...Option) server.SubscriberWrapper { func NewSubscriberWrapper(opts ...Option) server.SubscriberWrapper {
handler := &wrapper{ handler := &wrapper{
opts: NewOptions(opts...), opts: NewOptions(opts...),

1
micro.go Normal file
View File

@@ -0,0 +1 @@
package micro // import "go.unistack.org/micro/v3"

View File

@@ -1,35 +0,0 @@
// +build ignore
// Package model is an interface for data modelling
package model
// Model provides an interface for data modelling
type Model interface {
// Initialise options
Init(...Option) error
// NewEntity creates a new entity to store or access
NewEntity(name string, value interface{}) Entity
// Create a value
Create(Entity) error
// Read values
Read(...ReadOption) ([]Entity, error)
// Update the value
Update(Entity) error
// Delete an entity
Delete(...DeleteOption) error
// Implementation of the model
String() string
}
type Entity interface {
// Unique id of the entity
Id() string
// Name of the entity
Name() string
// The value associated with the entity
Value() interface{}
// Attributes of the entity
Attributes() map[string]interface{}
// Read a value as a concrete type
Read(v interface{}) error
}

View File

@@ -1,41 +0,0 @@
// +build ignore
// Package model is an interface for data modelling
package model
import (
"github.com/unistack-org/micro/v3/codec"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/store"
"github.com/unistack-org/micro/v3/sync"
)
type Options struct {
// Database to write to
Database string
// for serialising
Codec codec.Codec
// for locking
Sync sync.Sync
// for storage
Store store.Store
// for logger
Logger logger.Logger
}
type Option func(o *Options)
// Logger sets the logger
func Logger(l logger.Logger) Option {
return func(o *Options) {
o.Logger = l
}
}
type ReadOptions struct{}
type ReadOption func(o *ReadOptions)
type DeleteOptions struct{}
type DeleteOption func(o *DeleteOptions)

View File

@@ -1,9 +1,9 @@
// Package network is for creating internetworks // Package network is for creating internetworks
package network package network // import "go.unistack.org/micro/v3/network"
import ( import (
"github.com/unistack-org/micro/v3/client" "go.unistack.org/micro/v3/client"
"github.com/unistack-org/micro/v3/server" "go.unistack.org/micro/v3/server"
) )
// Error is network node errors // Error is network node errors

View File

@@ -1,13 +1,13 @@
package network package network
import ( import (
"github.com/google/uuid" "go.unistack.org/micro/v3/logger"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/meter"
"github.com/unistack-org/micro/v3/meter" "go.unistack.org/micro/v3/network/tunnel"
"github.com/unistack-org/micro/v3/network/tunnel" "go.unistack.org/micro/v3/proxy"
"github.com/unistack-org/micro/v3/proxy" "go.unistack.org/micro/v3/router"
"github.com/unistack-org/micro/v3/router" "go.unistack.org/micro/v3/tracer"
"github.com/unistack-org/micro/v3/tracer" "go.unistack.org/micro/v3/util/id"
) )
// Option func // Option func
@@ -27,8 +27,8 @@ type Options struct {
Tracer tracer.Tracer Tracer tracer.Tracer
// Tunnel used for transfer data // Tunnel used for transfer data
Tunnel tunnel.Tunnel Tunnel tunnel.Tunnel
// Id of the node // ID of the node
Id string ID string
// Name of the network // Name of the network
Name string Name string
// Address to bind to // Address to bind to
@@ -39,10 +39,10 @@ type Options struct {
Nodes []string Nodes []string
} }
// Id sets the id of the network node // ID sets the id of the network node
func Id(id string) Option { func ID(id string) Option {
return func(o *Options) { return func(o *Options) {
o.Id = id o.ID = id
} }
} }
@@ -119,7 +119,7 @@ func Tracer(t tracer.Tracer) Option {
// NewOptions returns network default options // NewOptions returns network default options
func NewOptions(opts ...Option) Options { func NewOptions(opts ...Option) Options {
options := Options{ options := Options{
Id: uuid.New().String(), ID: id.Must(),
Name: "go.micro", Name: "go.micro",
Address: ":0", Address: ":0",
Logger: logger.DefaultLogger, Logger: logger.DefaultLogger,

View File

@@ -8,9 +8,9 @@ import (
"sync" "sync"
"time" "time"
maddr "github.com/unistack-org/micro/v3/util/addr" maddr "go.unistack.org/micro/v3/util/addr"
mnet "github.com/unistack-org/micro/v3/util/net" mnet "go.unistack.org/micro/v3/util/net"
"github.com/unistack-org/micro/v3/util/rand" "go.unistack.org/micro/v3/util/rand"
) )
type memorySocket struct { type memorySocket struct {

View File

@@ -16,12 +16,14 @@ func TestMemoryTransport(t *testing.T) {
} }
defer l.Close() defer l.Close()
cherr := make(chan error, 1)
// accept // accept
go func() { go func() {
if err := l.Accept(func(sock Socket) { if nerr := l.Accept(func(sock Socket) {
for { for {
var m Message var m Message
if err := sock.Recv(&m); err != nil { if rerr := sock.Recv(&m); rerr != nil {
cherr <- rerr
return return
} }
if len(os.Getenv("INTEGRATION_TESTS")) == 0 { if len(os.Getenv("INTEGRATION_TESTS")) == 0 {
@@ -30,11 +32,12 @@ func TestMemoryTransport(t *testing.T) {
if cerr := sock.Send(&Message{ if cerr := sock.Send(&Message{
Body: []byte(`pong`), Body: []byte(`pong`),
}); cerr != nil { }); cerr != nil {
cherr <- cerr
return return
} }
} }
}); err != nil { }); nerr != nil {
t.Fatalf("Unexpected error accepting %v", err) cherr <- err
} }
}() }()
@@ -45,19 +48,24 @@ func TestMemoryTransport(t *testing.T) {
} }
defer c.Close() defer c.Close()
// send <=> receive select {
for i := 0; i < 3; i++ { case err := <-cherr:
if err := c.Send(&Message{ t.Fatal(err)
Body: []byte(`ping`), default:
}); err != nil { // send <=> receive
return for i := 0; i < 3; i++ {
} if err := c.Send(&Message{
var m Message Body: []byte(`ping`),
if err := c.Recv(&m); err != nil { }); err != nil {
return return
} }
if len(os.Getenv("INTEGRATION_TESTS")) == 0 { var m Message
t.Logf("Client Received %s", string(m.Body)) if err := c.Recv(&m); err != nil {
return
}
if len(os.Getenv("INTEGRATION_TESTS")) == 0 {
t.Logf("Client Received %s", string(m.Body))
}
} }
} }
} }

View File

@@ -5,10 +5,10 @@ import (
"crypto/tls" "crypto/tls"
"time" "time"
"github.com/unistack-org/micro/v3/codec" "go.unistack.org/micro/v3/codec"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/logger"
"github.com/unistack-org/micro/v3/meter" "go.unistack.org/micro/v3/meter"
"github.com/unistack-org/micro/v3/tracer" "go.unistack.org/micro/v3/tracer"
) )
// Options struct holds the transport options // Options struct holds the transport options

View File

@@ -1,11 +1,11 @@
// Package transport is an interface for synchronous connection based communication // Package transport is an interface for synchronous connection based communication
package transport package transport // import "go.unistack.org/micro/v3/network/transport"
import ( import (
"context" "context"
"time" "time"
"github.com/unistack-org/micro/v3/metadata" "go.unistack.org/micro/v3/metadata"
) )
var ( var (

View File

@@ -1,14 +1,15 @@
// Package broker is a tunnel broker // Package broker is a tunnel broker
package broker package broker // import "go.unistack.org/micro/v3/network/tunnel/broker"
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/unistack-org/micro/v3/broker" "go.unistack.org/micro/v3/broker"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/logger"
"github.com/unistack-org/micro/v3/network/transport" "go.unistack.org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/network/tunnel" "go.unistack.org/micro/v3/network/transport"
"go.unistack.org/micro/v3/network/tunnel"
) )
type tunBroker struct { type tunBroker struct {
@@ -24,7 +25,16 @@ type tunSubscriber struct {
opts broker.SubscribeOptions opts broker.SubscribeOptions
} }
type tunBatchSubscriber struct {
listener tunnel.Listener
handler broker.BatchHandler
closed chan bool
topic string
opts broker.SubscribeOptions
}
type tunEvent struct { type tunEvent struct {
err error
message *broker.Message message *broker.Message
topic string topic string
} }
@@ -62,6 +72,36 @@ func (t *tunBroker) Disconnect(ctx context.Context) error {
return t.tunnel.Close(ctx) return t.tunnel.Close(ctx)
} }
func (t *tunBroker) BatchPublish(ctx context.Context, msgs []*broker.Message, opts ...broker.PublishOption) error {
// TODO: this is probably inefficient, we might want to just maintain an open connection
// it may be easier to add broadcast to the tunnel
topicMap := make(map[string]tunnel.Session)
var err error
for _, msg := range msgs {
topic, _ := msg.Header.Get(metadata.HeaderTopic)
c, ok := topicMap[topic]
if !ok {
c, err = t.tunnel.Dial(ctx, topic, tunnel.DialMode(tunnel.Multicast))
if err != nil {
return err
}
defer c.Close()
topicMap[topic] = c
}
if err = c.Send(&transport.Message{
Header: msg.Header,
Body: msg.Body,
}); err != nil {
// msg.SetError(err)
return err
}
}
return nil
}
func (t *tunBroker) Publish(ctx context.Context, topic string, m *broker.Message, opts ...broker.PublishOption) error { func (t *tunBroker) Publish(ctx context.Context, topic string, m *broker.Message, opts ...broker.PublishOption) error {
// TODO: this is probably inefficient, we might want to just maintain an open connection // TODO: this is probably inefficient, we might want to just maintain an open connection
// it may be easier to add broadcast to the tunnel // it may be easier to add broadcast to the tunnel
@@ -77,6 +117,26 @@ func (t *tunBroker) Publish(ctx context.Context, topic string, m *broker.Message
}) })
} }
func (t *tunBroker) BatchSubscribe(ctx context.Context, topic string, h broker.BatchHandler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
l, err := t.tunnel.Listen(ctx, topic, tunnel.ListenMode(tunnel.Multicast))
if err != nil {
return nil, err
}
tunSub := &tunBatchSubscriber{
topic: topic,
handler: h,
opts: broker.NewSubscribeOptions(opts...),
closed: make(chan bool),
listener: l,
}
// start processing
go tunSub.run()
return tunSub, nil
}
func (t *tunBroker) Subscribe(ctx context.Context, topic string, h broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { func (t *tunBroker) Subscribe(ctx context.Context, topic string, h broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
l, err := t.tunnel.Listen(ctx, topic, tunnel.ListenMode(tunnel.Multicast)) l, err := t.tunnel.Listen(ctx, topic, tunnel.ListenMode(tunnel.Multicast))
if err != nil { if err != nil {
@@ -101,6 +161,51 @@ func (t *tunBroker) String() string {
return "tunnel" return "tunnel"
} }
func (t *tunBatchSubscriber) run() {
for {
// accept a new connection
c, err := t.listener.Accept()
if err != nil {
select {
case <-t.closed:
return
default:
continue
}
}
// receive message
m := new(transport.Message)
if err := c.Recv(m); err != nil {
if logger.V(logger.ErrorLevel) {
logger.Error(t.opts.Context, err.Error())
}
if err = c.Close(); err != nil {
if logger.V(logger.ErrorLevel) {
logger.Error(t.opts.Context, err.Error())
}
}
continue
}
// close the connection
c.Close()
evts := broker.Events{&tunEvent{
topic: t.topic,
message: &broker.Message{
Header: m.Header,
Body: m.Body,
},
}}
// handle the message
go func() {
_ = t.handler(evts)
}()
}
}
func (t *tunSubscriber) run() { func (t *tunSubscriber) run() {
for { for {
// accept a new connection // accept a new connection
@@ -132,13 +237,33 @@ func (t *tunSubscriber) run() {
c.Close() c.Close()
// handle the message // handle the message
go t.handler(&tunEvent{ go func() {
topic: t.topic, _ = t.handler(&tunEvent{
message: &broker.Message{ topic: t.topic,
Header: m.Header, message: &broker.Message{
Body: m.Body, Header: m.Header,
}, Body: m.Body,
}) },
})
}()
}
}
func (t *tunBatchSubscriber) Options() broker.SubscribeOptions {
return t.opts
}
func (t *tunBatchSubscriber) Topic() string {
return t.topic
}
func (t *tunBatchSubscriber) Unsubscribe(ctx context.Context) error {
select {
case <-t.closed:
return nil
default:
close(t.closed)
return t.listener.Close()
} }
} }
@@ -173,7 +298,11 @@ func (t *tunEvent) Ack() error {
} }
func (t *tunEvent) Error() error { func (t *tunEvent) Error() error {
return nil return t.err
}
func (t *tunEvent) SetError(err error) {
t.err = err
} }
// NewBroker returns new tunnel broker // NewBroker returns new tunnel broker

View File

@@ -3,11 +3,11 @@ package tunnel
import ( import (
"time" "time"
"github.com/google/uuid" "go.unistack.org/micro/v3/logger"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/meter"
"github.com/unistack-org/micro/v3/meter" "go.unistack.org/micro/v3/network/transport"
"github.com/unistack-org/micro/v3/network/transport" "go.unistack.org/micro/v3/tracer"
"github.com/unistack-org/micro/v3/tracer" "go.unistack.org/micro/v3/util/id"
) )
var ( var (
@@ -164,7 +164,7 @@ func DialWait(b bool) DialOption {
// NewOptions returns router default options with filled values // NewOptions returns router default options with filled values
func NewOptions(opts ...Option) Options { func NewOptions(opts ...Option) Options {
options := Options{ options := Options{
ID: uuid.New().String(), ID: id.Must(),
Address: DefaultAddress, Address: DefaultAddress,
Token: DefaultToken, Token: DefaultToken,
Logger: logger.DefaultLogger, Logger: logger.DefaultLogger,

View File

@@ -1,8 +1,8 @@
package transport package transport
import ( import (
"github.com/unistack-org/micro/v3/network/transport" "go.unistack.org/micro/v3/network/transport"
"github.com/unistack-org/micro/v3/network/tunnel" "go.unistack.org/micro/v3/network/tunnel"
) )
type tunListener struct { type tunListener struct {

View File

@@ -1,12 +1,12 @@
// Package transport provides a tunnel transport // Package transport provides a tunnel transport
package transport package transport // import "go.unistack.org/micro/v3/network/tunnel/transport"
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/unistack-org/micro/v3/network/transport" "go.unistack.org/micro/v3/network/transport"
"github.com/unistack-org/micro/v3/network/tunnel" "go.unistack.org/micro/v3/network/tunnel"
) )
type tunTransport struct { type tunTransport struct {
@@ -37,7 +37,7 @@ func (t *tunTransport) Init(opts ...transport.Option) error {
// get the transport // get the transport
tr, ok := t.options.Context.Value(transportKey{}).(transport.Transport) tr, ok := t.options.Context.Value(transportKey{}).(transport.Transport)
if ok { if ok {
tun.Init(tunnel.Transport(tr)) _ = tun.Init(tunnel.Transport(tr))
} }
// set the tunnel // set the tunnel

View File

@@ -1,12 +1,12 @@
// Package tunnel provides gre network tunnelling // Package tunnel provides gre network tunnelling
package tunnel package tunnel // import "go.unistack.org/micro/v3/network/transport/tunnel"
import ( import (
"context" "context"
"errors" "errors"
"time" "time"
"github.com/unistack-org/micro/v3/network/transport" "go.unistack.org/micro/v3/network/transport"
) )
// DefaultTunnel contains default tunnel implementation // DefaultTunnel contains default tunnel implementation

View File

@@ -5,18 +5,18 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/unistack-org/micro/v3/auth" "go.unistack.org/micro/v3/auth"
"github.com/unistack-org/micro/v3/broker" "go.unistack.org/micro/v3/broker"
"github.com/unistack-org/micro/v3/client" "go.unistack.org/micro/v3/client"
"github.com/unistack-org/micro/v3/config" "go.unistack.org/micro/v3/config"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/logger"
"github.com/unistack-org/micro/v3/metadata" "go.unistack.org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/meter" "go.unistack.org/micro/v3/meter"
"github.com/unistack-org/micro/v3/register" "go.unistack.org/micro/v3/register"
"github.com/unistack-org/micro/v3/router" "go.unistack.org/micro/v3/router"
"github.com/unistack-org/micro/v3/server" "go.unistack.org/micro/v3/server"
"github.com/unistack-org/micro/v3/store" "go.unistack.org/micro/v3/store"
"github.com/unistack-org/micro/v3/tracer" "go.unistack.org/micro/v3/tracer"
) )
// Options for micro service // Options for micro service

View File

@@ -1,5 +1,5 @@
// Package http enables the http profiler // Package http enables the http profiler
package http package http // import "go.unistack.org/micro/v3/profiler/http"
import ( import (
"context" "context"
@@ -7,7 +7,7 @@ import (
"net/http/pprof" "net/http/pprof"
"sync" "sync"
profile "github.com/unistack-org/micro/v3/profiler" profile "go.unistack.org/micro/v3/profiler"
) )
type httpProfile struct { type httpProfile struct {

View File

@@ -1,5 +1,5 @@
// Package pprof provides a pprof profiler which writes output to /tmp/[name].{cpu,mem}.pprof // Package pprof provides a pprof profiler which writes output to /tmp/[name].{cpu,mem}.pprof
package pprof package pprof // import "go.unistack.org/micro/v3/profiler/pprof"
import ( import (
"os" "os"
@@ -9,7 +9,7 @@ import (
"sync" "sync"
"time" "time"
profile "github.com/unistack-org/micro/v3/profiler" profile "go.unistack.org/micro/v3/profiler"
) )
type profiler struct { type profiler struct {
@@ -31,7 +31,7 @@ func (p *profiler) writeHeap(f *os.File) {
select { select {
case <-t.C: case <-t.C:
runtime.GC() runtime.GC()
pprof.WriteHeapProfile(f) _ = pprof.WriteHeapProfile(f)
case <-p.exit: case <-p.exit:
return return
} }

View File

@@ -1,5 +1,5 @@
// Package profiler is for profilers // Package profiler is for profilers
package profiler package profiler // import "go.unistack.org/micro/v3/profiler"
// Profiler interface // Profiler interface
type Profiler interface { type Profiler interface {

View File

@@ -2,11 +2,11 @@
package proxy package proxy
import ( import (
"github.com/unistack-org/micro/v3/client" "go.unistack.org/micro/v3/client"
"github.com/unistack-org/micro/v3/logger" "go.unistack.org/micro/v3/logger"
"github.com/unistack-org/micro/v3/meter" "go.unistack.org/micro/v3/meter"
"github.com/unistack-org/micro/v3/router" "go.unistack.org/micro/v3/router"
"github.com/unistack-org/micro/v3/tracer" "go.unistack.org/micro/v3/tracer"
) )
// Options for proxy // Options for proxy

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