Compare commits

...

239 Commits

Author SHA1 Message Date
renovate 501bf39827 chore(deps): update golang:1.26.1 docker digest to 5ba1126 (#795)
Release / release (push) Successful in 1m2s
schemas / vulnerabilities (push) Successful in 1m45s
schemas / check (push) Successful in 2m41s
schemas / check-release (push) Successful in 2m42s
pre-commit / pre-commit (push) Successful in 8m28s
schemas / build (push) Successful in 6m22s
schemas / deploy-prod (push) Successful in 1m16s
2026-04-07 06:59:28 +00:00
renovate e3fbc3e8cc fix(deps): update opentelemetry-go monorepo (#793)
Release / release (push) Successful in 55s
schemas / vulnerabilities (push) Successful in 2m2s
schemas / check (push) Successful in 2m38s
schemas / check-release (push) Successful in 2m57s
pre-commit / pre-commit (push) Successful in 10m24s
schemas / build (push) Successful in 10m49s
schemas / deploy-prod (push) Successful in 1m6s
2026-04-06 10:20:41 +00:00
renovate 37ec1f0cc4 fix(deps): update module github.com/alecthomas/kong to v1.15.0 (#791)
schemas / vulnerabilities (push) Successful in 1m53s
schemas / check (push) Successful in 2m36s
Release / release (push) Successful in 56s
schemas / check-release (push) Successful in 2m56s
pre-commit / pre-commit (push) Successful in 6m34s
schemas / build (push) Successful in 7m58s
schemas / deploy-prod (push) Successful in 1m13s
2026-04-01 23:10:11 +00:00
renovate 36c10eb51b fix(deps): update module gitlab.com/unboundsoftware/eventsourced/pg to v1.18.6 (#790)
schemas / vulnerabilities (push) Successful in 2m10s
schemas / check (push) Successful in 2m50s
Release / release (push) Failing after 52s
schemas / check-release (push) Successful in 3m20s
schemas / build (push) Successful in 5m53s
pre-commit / pre-commit (push) Successful in 7m2s
schemas / deploy-prod (push) Successful in 1m5s
2026-03-31 12:38:15 +00:00
releaser 6e46a2cbe7 chore(release): prepare for v0.9.6 (#789)
Release / release (push) Successful in 50s
schemas / vulnerabilities (push) Successful in 2m4s
schemas / check (push) Successful in 2m32s
schemas / check-release (push) Successful in 2m54s
Goreleaser / release (push) Successful in 2m52s
pre-commit / pre-commit (push) Successful in 6m33s
schemas / build (push) Successful in 6m24s
schemas / deploy-prod (push) Has been skipped
## [0.9.6] - 2026-03-29

### 🐛 Bug Fixes

- *(graph)* Stabilize debouncer tests with synctest fake clock
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.257 (#743)
- *(deps)* Update golang.org/x/net to v0.51.0
- *(deps)* Update module go.opentelemetry.io/contrib/bridges/otelslog to v0.16.0 (#745)
- *(deps)* Update opentelemetry-go monorepo (#744)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.260 (#750)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.261 (#751)
- *(deps)* Update opentelemetry-go monorepo (#755)
- *(deps)* Update module go.opentelemetry.io/contrib/bridges/otelslog to v0.17.0 (#756)
- *(deps)* Update module golang.org/x/sync to v0.20.0 (#760)
- *(deps)* Update module github.com/99designs/gqlgen to v0.17.88 (#762)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.262 (#765)
- *(deps)* Update module golang.org/x/crypto to v0.49.0 (#767)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.263 (#771)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.264 (#773)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.265 (#776)
- *(deps)* Update module gitlab.com/unboundsoftware/eventsourced/pg to v1.18.5 (#778)
- *(deps)* Update module github.com/99designs/gqlgen to v0.17.89 (#782)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.266 (#784)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.267 (#786)
- Cap Node.js heap to prevent OOM during wgc composition (#788)

### ⚙️ Miscellaneous Tasks

- *(deps)* Update golang docker tag to v1.26.1 (#752)
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.11.1
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.11.2 (#758)
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.11.3 (#763)
- *(deps)* Update pre-commit hook gitleaks/gitleaks to v8.30.1 (#769)
- *(deps)* Update golang:1.26.1 docker digest to 984bf90 (#774)
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.11.4 (#780)
- *(deps)* Update node.js to v24.14.1 (#783)

<!-- generated by git-cliff -->

---

**Note:** Please use **Squash Merge** when merging this PR.

Reviewed-on: #789
Co-authored-by: Unbound Releaser <releaser@unbound.se>
Co-committed-by: Unbound Releaser <releaser@unbound.se>
2026-03-29 09:54:53 +00:00
argoyle 9a32cdb1b3 fix: cap Node.js heap to prevent OOM during wgc composition (#788)
schemas / vulnerabilities (push) Successful in 1m56s
schemas / check (push) Successful in 2m46s
schemas / check-release (push) Successful in 2m53s
schemas / build (push) Successful in 6m4s
pre-commit / pre-commit (push) Successful in 6m51s
schemas / deploy-prod (push) Successful in 1m14s
Release / release (push) Successful in 1m4s
## Summary
- Sets `NODE_OPTIONS=--max-old-space-size=64` in Dockerfile to cap Node.js heap when running `wgc router compose`
- Prevents container OOMKills in memory-constrained environments (e.g. acctest with 128Mi limit)
- The wgc Node.js process was using unbounded heap, which combined with the Go service easily exceeded container memory limits during rapid schema publishing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #788
2026-03-29 09:31:53 +00:00
renovate 1150e82c22 chore(deps): update node.js to v24.14.1 (#783)
Release / release (push) Successful in 1m8s
schemas / vulnerabilities (push) Successful in 2m7s
schemas / check (push) Successful in 2m48s
schemas / check-release (push) Successful in 3m14s
pre-commit / pre-commit (push) Successful in 6m51s
schemas / build (push) Successful in 7m5s
schemas / deploy-prod (push) Successful in 1m25s
2026-03-26 14:08:20 +00:00
renovate eaae92ec0b fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.267 (#786)
Release / release (push) Failing after 44s
schemas / vulnerabilities (push) Successful in 2m4s
schemas / check (push) Successful in 2m48s
schemas / check-release (push) Successful in 3m5s
pre-commit / pre-commit (push) Successful in 7m22s
schemas / build (push) Successful in 5m34s
schemas / deploy-prod (push) Successful in 1m20s
2026-03-26 12:24:33 +00:00
renovate e4fd2b2b63 fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.266 (#784)
Release / release (push) Successful in 1m9s
schemas / check (push) Successful in 2m40s
schemas / vulnerabilities (push) Successful in 2m46s
schemas / check-release (push) Successful in 3m17s
pre-commit / pre-commit (push) Successful in 6m56s
schemas / build (push) Successful in 6m32s
schemas / deploy-prod (push) Successful in 1m17s
2026-03-26 11:23:35 +00:00
renovate 81af7a2416 fix(deps): update module github.com/99designs/gqlgen to v0.17.89 (#782)
Release / release (push) Failing after 57s
schemas / vulnerabilities (push) Successful in 2m21s
schemas / check (push) Successful in 2m38s
schemas / check-release (push) Successful in 2m48s
pre-commit / pre-commit (push) Successful in 6m50s
schemas / build (push) Successful in 10m16s
schemas / deploy-prod (push) Successful in 1m7s
2026-03-24 18:44:05 +00:00
renovate 62e9cef341 chore(deps): update pre-commit hook golangci/golangci-lint to v2.11.4 (#780)
schemas / vulnerabilities (push) Successful in 2m10s
schemas / check (push) Successful in 2m34s
schemas / check-release (push) Successful in 2m54s
Release / release (push) Successful in 58s
pre-commit / pre-commit (push) Successful in 7m10s
schemas / build (push) Successful in 12m21s
schemas / deploy-prod (push) Successful in 1m17s
2026-03-22 19:43:56 +00:00
renovate 972968f865 fix(deps): update module gitlab.com/unboundsoftware/eventsourced/pg to v1.18.5 (#778)
schemas / vulnerabilities (push) Successful in 1m54s
schemas / check (push) Successful in 2m51s
Release / release (push) Successful in 1m11s
schemas / check-release (push) Successful in 3m0s
pre-commit / pre-commit (push) Successful in 6m49s
schemas / build (push) Successful in 13m23s
schemas / deploy-prod (push) Successful in 1m35s
2026-03-19 10:00:34 +00:00
renovate 1ede47df82 fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.265 (#776)
Release / release (push) Successful in 1m10s
schemas / vulnerabilities (push) Successful in 2m5s
schemas / check (push) Successful in 2m40s
schemas / check-release (push) Successful in 3m16s
pre-commit / pre-commit (push) Successful in 6m50s
schemas / build (push) Successful in 8m47s
schemas / deploy-prod (push) Successful in 1m14s
2026-03-17 14:22:34 +00:00
renovate 882b87e882 fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.264 (#773)
schemas / vulnerabilities (push) Successful in 1m42s
schemas / check (push) Successful in 2m41s
Release / release (push) Successful in 1m12s
schemas / check-release (push) Successful in 3m11s
pre-commit / pre-commit (push) Successful in 7m12s
schemas / build (push) Successful in 8m28s
schemas / deploy-prod (push) Successful in 1m12s
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/wundergraph/graphql-go-tools/v2](https://github.com/wundergraph/graphql-go-tools) | `v2.0.0-rc.263` → `v2.0.0-rc.264` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fwundergraph%2fgraphql-go-tools%2fv2/v2.0.0-rc.264?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fwundergraph%2fgraphql-go-tools%2fv2/v2.0.0-rc.263/v2.0.0-rc.264?slim=true) |

---

### Release Notes

<details>
<summary>wundergraph/graphql-go-tools (github.com/wundergraph/graphql-go-tools/v2)</summary>

### [`v2.0.0-rc.264`](https://github.com/wundergraph/graphql-go-tools/releases/tag/v2.0.0-rc.264)

[Compare Source](https://github.com/wundergraph/graphql-go-tools/compare/v2.0.0-rc.263...v2.0.0-rc.264)

##### Features

- support sizedFields in cost computations ([#&#8203;1410](https://github.com/wundergraph/graphql-go-tools/issues/1410)) ([48f7582](https://github.com/wundergraph/graphql-go-tools/commit/48f75821b0d7e0ffb5f7e66cffba3d1ad0cbf9dc))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My41OS40IiwidXBkYXRlZEluVmVyIjoiNDMuNTkuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Reviewed-on: https://gitea.unbound.se/unboundsoftware/schemas/pulls/773
Co-authored-by: Renovate Bot <renovate@unbound.se>
Co-committed-by: Renovate Bot <renovate@unbound.se>
2026-03-17 05:48:52 +00:00
renovate 85dc71fe24 chore(deps): update golang:1.26.1 docker digest to 984bf90 (#774)
schemas / check (push) Successful in 2m23s
schemas / vulnerabilities (push) Successful in 2m3s
Release / release (push) Failing after 53s
schemas / check-release (push) Successful in 3m55s
pre-commit / pre-commit (push) Successful in 7m43s
schemas / build (push) Successful in 8m22s
schemas / deploy-prod (push) Successful in 1m13s
2026-03-17 04:11:31 +00:00
renovate a7804ac970 fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.263 (#771)
Release / release (push) Successful in 1m52s
schemas / vulnerabilities (push) Successful in 2m33s
schemas / check (push) Successful in 3m4s
schemas / check-release (push) Successful in 3m42s
schemas / build (push) Successful in 6m44s
pre-commit / pre-commit (push) Successful in 11m4s
schemas / deploy-prod (push) Successful in 1m45s
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/wundergraph/graphql-go-tools/v2](https://github.com/wundergraph/graphql-go-tools) | `v2.0.0-rc.262` → `v2.0.0-rc.263` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fwundergraph%2fgraphql-go-tools%2fv2/v2.0.0-rc.263?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fwundergraph%2fgraphql-go-tools%2fv2/v2.0.0-rc.262/v2.0.0-rc.263?slim=true) |

---

### Release Notes

<details>
<summary>wundergraph/graphql-go-tools (github.com/wundergraph/graphql-go-tools/v2)</summary>

### [`v2.0.0-rc.263`](https://github.com/wundergraph/graphql-go-tools/releases/tag/v2.0.0-rc.263)

[Compare Source](https://github.com/wundergraph/graphql-go-tools/compare/v2.0.0-rc.262...v2.0.0-rc.263)

##### Features

- support field resolvers with empty arguments ([#&#8203;1440](https://github.com/wundergraph/graphql-go-tools/issues/1440)) ([58305bc](https://github.com/wundergraph/graphql-go-tools/commit/58305bc61a774e0ced6def84f63f75f5c4556dc8))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My41OS40IiwidXBkYXRlZEluVmVyIjoiNDMuNTkuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Reviewed-on: https://gitea.unbound.se/unboundsoftware/schemas/pulls/771
Co-authored-by: Renovate Bot <renovate@unbound.se>
Co-committed-by: Renovate Bot <renovate@unbound.se>
2026-03-13 12:37:04 +00:00
renovate a94f9cb0c0 chore(deps): update pre-commit hook gitleaks/gitleaks to v8.30.1 (#769)
Release / release (push) Successful in 1m15s
schemas / vulnerabilities (push) Successful in 2m18s
schemas / check (push) Successful in 2m52s
schemas / check-release (push) Successful in 4m12s
pre-commit / pre-commit (push) Successful in 8m1s
schemas / build (push) Successful in 5m49s
schemas / deploy-prod (push) Successful in 1m9s
2026-03-12 17:19:56 +00:00
renovate 68870bb37e fix(deps): update module golang.org/x/crypto to v0.49.0 (#767)
Release / release (push) Successful in 59s
schemas / vulnerabilities (push) Successful in 1m30s
schemas / check (push) Successful in 1m52s
schemas / check-release (push) Successful in 2m30s
pre-commit / pre-commit (push) Successful in 4m53s
schemas / build (push) Successful in 4m55s
schemas / deploy-prod (push) Successful in 59s
2026-03-11 23:17:04 +00:00
renovate d8e34ccae5 fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.262 (#765)
Release / release (push) Successful in 1m17s
schemas / vulnerabilities (push) Successful in 2m43s
schemas / check (push) Successful in 3m2s
schemas / check-release (push) Successful in 3m51s
pre-commit / pre-commit (push) Successful in 6m55s
schemas / build (push) Successful in 7m24s
schemas / deploy-prod (push) Successful in 1m11s
2026-03-10 14:20:15 +00:00
renovate fdfdce46c0 chore(deps): update pre-commit hook golangci/golangci-lint to v2.11.3 (#763)
Release / release (push) Successful in 1m12s
schemas / check (push) Successful in 2m34s
schemas / vulnerabilities (push) Successful in 2m35s
schemas / check-release (push) Successful in 3m25s
schemas / build (push) Successful in 10m27s
pre-commit / pre-commit (push) Successful in 14m31s
schemas / deploy-prod (push) Successful in 1m11s
2026-03-10 11:39:58 +00:00
renovate 357be92d1e fix(deps): update module github.com/99designs/gqlgen to v0.17.88 (#762)
schemas / vulnerabilities (push) Successful in 1m57s
schemas / check (push) Successful in 2m42s
Release / release (push) Failing after 1m6s
schemas / check-release (push) Successful in 3m42s
pre-commit / pre-commit (push) Successful in 6m7s
schemas / build (push) Successful in 5m25s
schemas / deploy-prod (push) Successful in 1m25s
2026-03-09 11:50:57 +00:00
renovate 4edddee47b fix(deps): update module golang.org/x/sync to v0.20.0 (#760)
Release / release (push) Successful in 1m2s
schemas / check (push) Successful in 1m55s
schemas / vulnerabilities (push) Successful in 1m58s
schemas / check-release (push) Successful in 2m33s
pre-commit / pre-commit (push) Successful in 6m0s
schemas / build (push) Successful in 5m10s
schemas / deploy-prod (push) Successful in 1m16s
2026-03-08 13:17:26 +00:00
renovate f6df1a51da chore(deps): update pre-commit hook golangci/golangci-lint to v2.11.2 (#758)
schemas / vulnerabilities (push) Successful in 1m47s
Release / release (push) Successful in 1m21s
schemas / check (push) Successful in 2m27s
schemas / check-release (push) Successful in 2m48s
pre-commit / pre-commit (push) Successful in 5m42s
schemas / build (push) Successful in 5m42s
schemas / deploy-prod (push) Successful in 59s
2026-03-08 08:39:33 +00:00
renovate 7c109f3c69 fix(deps): update module go.opentelemetry.io/contrib/bridges/otelslog to v0.17.0 (#756)
Release / release (push) Successful in 1m8s
schemas / vulnerabilities (push) Successful in 2m5s
schemas / check (push) Successful in 2m40s
schemas / check-release (push) Successful in 3m0s
pre-commit / pre-commit (push) Successful in 7m44s
schemas / build (push) Successful in 8m45s
schemas / deploy-prod (push) Successful in 1m19s
2026-03-06 21:42:28 +00:00
renovate f9d0b470c9 fix(deps): update opentelemetry-go monorepo (#755)
schemas / vulnerabilities (push) Successful in 1m53s
Release / release (push) Failing after 2m30s
schemas / check (push) Successful in 2m59s
schemas / check-release (push) Successful in 3m11s
pre-commit / pre-commit (push) Successful in 6m2s
schemas / build (push) Successful in 21m13s
schemas / deploy-prod (push) Successful in 1m15s
2026-03-06 20:35:36 +00:00
argoyle 924588bef9 Merge pull request 'chore(deps): update pre-commit hook golangci/golangci-lint to v2.11.1' (#754) from renovate/golangci-golangci-lint-2.x into main
Release / release (push) Failing after 1m2s
schemas / vulnerabilities (push) Successful in 2m20s
schemas / check (push) Successful in 2m44s
schemas / check-release (push) Successful in 3m15s
pre-commit / pre-commit (push) Successful in 8m30s
schemas / build (push) Successful in 22m36s
schemas / deploy-prod (push) Successful in 1m30s
Reviewed-on: #754
2026-03-06 19:58:31 +00:00
renovate 086c2175d9 chore(deps): update pre-commit hook golangci/golangci-lint to v2.11.1
schemas / vulnerabilities (pull_request) Successful in 1m49s
schemas / check (pull_request) Successful in 2m22s
schemas / check-release (pull_request) Successful in 3m49s
pre-commit / pre-commit (pull_request) Successful in 6m56s
schemas / build (pull_request) Successful in 6m12s
schemas / deploy-prod (pull_request) Has been skipped
2026-03-06 19:35:23 +00:00
renovate d657883648 chore(deps): update golang docker tag to v1.26.1 (#752)
Release / release (push) Successful in 1m6s
schemas / vulnerabilities (push) Successful in 2m15s
schemas / check (push) Successful in 2m51s
schemas / check-release (push) Successful in 3m20s
pre-commit / pre-commit (push) Successful in 9m41s
schemas / build (push) Successful in 6m38s
schemas / deploy-prod (push) Successful in 1m18s
2026-03-06 03:26:08 +00:00
renovate bd7497639d fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.261 (#751)
Release / release (push) Failing after 48s
schemas / vulnerabilities (push) Successful in 1m48s
schemas / check (push) Successful in 2m25s
schemas / check-release (push) Successful in 3m46s
pre-commit / pre-commit (push) Successful in 6m41s
schemas / build (push) Successful in 5m42s
schemas / deploy-prod (push) Successful in 1m50s
2026-03-06 02:32:53 +00:00
renovate 9db7b89d9f fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.260 (#750)
Release / release (push) Failing after 1m18s
schemas / vulnerabilities (push) Successful in 2m26s
schemas / check (push) Successful in 2m46s
schemas / check-release (push) Successful in 3m16s
pre-commit / pre-commit (push) Successful in 7m26s
schemas / build (push) Successful in 5m53s
schemas / deploy-prod (push) Successful in 1m17s
2026-03-05 15:06:04 +00:00
renovate a01211c089 fix(deps): update opentelemetry-go monorepo (#744)
Release / release (push) Successful in 1m28s
schemas / vulnerabilities (push) Successful in 2m16s
schemas / check-release (push) Successful in 4m58s
schemas / check (push) Successful in 5m27s
pre-commit / pre-commit (push) Successful in 8m31s
schemas / build (push) Successful in 8m24s
schemas / deploy-prod (push) Successful in 1m10s
2026-03-03 10:19:26 +00:00
renovate 20ca68ad4c fix(deps): update module go.opentelemetry.io/contrib/bridges/otelslog to v0.16.0 (#745)
Release / release (push) Successful in 1m32s
schemas / vulnerabilities (push) Successful in 3m27s
schemas / check-release (push) Successful in 4m25s
schemas / check (push) Successful in 5m21s
schemas / build (push) Successful in 5m44s
schemas / deploy-prod (push) Successful in 2m17s
pre-commit / pre-commit (push) Successful in 12m58s
2026-03-03 09:22:41 +00:00
argoyle 87d1e631f9 Merge pull request 'fix(deps): update golang.org/x/net to v0.51.0' (#746) from fix-vulncheck-xnet into main
Release / release (push) Successful in 1m17s
schemas / vulnerabilities (push) Successful in 3m20s
schemas / check-release (push) Successful in 4m5s
schemas / check (push) Successful in 5m59s
pre-commit / pre-commit (push) Successful in 9m7s
schemas / build (push) Successful in 6m50s
schemas / deploy-prod (push) Successful in 1m13s
Reviewed-on: #746
2026-03-03 08:22:09 +00:00
argoyle c98df1b4e7 fix(deps): update golang.org/x/net to v0.51.0
schemas / vulnerabilities (pull_request) Successful in 2m4s
schemas / check-release (pull_request) Successful in 4m15s
schemas / check (pull_request) Successful in 5m19s
pre-commit / pre-commit (pull_request) Successful in 9m45s
schemas / build (pull_request) Successful in 9m1s
schemas / deploy-prod (pull_request) Has been skipped
2026-03-03 09:03:39 +01:00
renovate 7bd605d90f fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.257 (#743)
Release / release (push) Failing after 1m16s
schemas / vulnerabilities (push) Successful in 2m20s
schemas / check-release (push) Successful in 3m46s
schemas / check (push) Successful in 4m47s
pre-commit / pre-commit (push) Successful in 9m12s
schemas / build (push) Successful in 6m18s
schemas / deploy-prod (push) Successful in 1m2s
2026-02-25 15:22:49 +00:00
argoyle 60bec67023 Merge pull request 'fix(graph): stabilize debouncer tests with synctest fake clock' (#741) from fix-flaky-debouncer-test into main
Release / release (push) Successful in 1m15s
schemas / vulnerabilities (push) Successful in 2m43s
schemas / check-release (push) Successful in 4m22s
schemas / check (push) Successful in 6m22s
pre-commit / pre-commit (push) Successful in 12m20s
schemas / build (push) Successful in 9m21s
schemas / deploy-prod (push) Successful in 1m13s
Reviewed-on: #741
2026-02-25 12:49:10 +00:00
argoyle cc33c651cd fix(graph): stabilize debouncer tests with synctest fake clock
schemas / vulnerabilities (pull_request) Successful in 2m17s
schemas / check-release (pull_request) Successful in 4m9s
schemas / check (pull_request) Successful in 4m52s
pre-commit / pre-commit (pull_request) Successful in 9m43s
schemas / build (pull_request) Successful in 6m58s
schemas / deploy-prod (pull_request) Has been skipped
Replace real time.Sleep waits with testing/synctest fake clock to
eliminate CI flakiness caused by timer races on loaded machines.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 13:25:18 +01:00
argoyle 38b4ab8ee2 Merge pull request 'chore(release): prepare for v0.9.5' (#740) from next-release into main
Release / release (push) Successful in 1m0s
schemas / vulnerabilities (push) Successful in 4m2s
Goreleaser / release (push) Successful in 5m8s
schemas / check-release (push) Successful in 6m38s
schemas / check (push) Successful in 8m59s
schemas / build (push) Successful in 6m0s
schemas / deploy-prod (push) Has been skipped
pre-commit / pre-commit (push) Successful in 8m14s
Reviewed-on: #740
2026-02-25 11:32:35 +00:00
releaser 1fa91ad573 chore(release): prepare for v0.9.5
schemas / vulnerabilities (pull_request) Successful in 2m48s
schemas / check-release (pull_request) Successful in 5m48s
schemas / check (pull_request) Successful in 5m48s
schemas / build (pull_request) Successful in 12m9s
schemas / deploy-prod (pull_request) Has been skipped
pre-commit / pre-commit (pull_request) Successful in 12m36s
2026-02-25 06:27:43 +00:00
releaser 7182ae2a11 chore(release): prepare for v0.9.5 2026-02-25 06:27:39 +00:00
renovate 19b7f0edb6 chore(deps): update node.js to 7fddd9d (#739)
Release / release (push) Successful in 1m36s
schemas / vulnerabilities (push) Successful in 3m25s
schemas / check-release (push) Successful in 5m10s
schemas / check (push) Successful in 6m21s
pre-commit / pre-commit (push) Successful in 11m40s
schemas / build (push) Successful in 6m36s
schemas / deploy-prod (push) Successful in 1m13s
2026-02-25 06:26:07 +00:00
renovate 0b68cadd24 chore(deps): update node.js to v24.14.0 (#735)
Release / release (push) Successful in 1m5s
schemas / vulnerabilities (push) Successful in 2m51s
schemas / check-release (push) Successful in 3m42s
schemas / check (push) Successful in 6m23s
pre-commit / pre-commit (push) Successful in 10m4s
schemas / build (push) Successful in 7m19s
schemas / deploy-prod (push) Successful in 1m11s
2026-02-25 00:28:09 +00:00
renovate 50087adeac chore(deps): update golang:1.26.0 docker digest to 9835fb4 (#736)
schemas / vulnerabilities (push) Successful in 2m38s
schemas / check (push) Successful in 4m25s
schemas / check-release (push) Successful in 4m34s
Release / release (push) Successful in 1m23s
pre-commit / pre-commit (push) Successful in 17m31s
schemas / build (push) Successful in 26m14s
schemas / deploy-prod (push) Successful in 1m16s
2026-02-24 22:44:21 +00:00
argoyle 03c903cf0e Merge pull request 'fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.256' (#733) from renovate/github.com-wundergraph-graphql-go-tools-v2-2.x into main
Release / release (push) Successful in 2m0s
schemas / vulnerabilities (push) Successful in 4m40s
schemas / check-release (push) Successful in 10m7s
schemas / check (push) Successful in 11m58s
schemas / build (push) Successful in 9m21s
schemas / deploy-prod (push) Successful in 2m12s
pre-commit / pre-commit (push) Successful in 24m26s
Reviewed-on: #733
2026-02-23 16:32:56 +00:00
renovate 59d0d40d4d fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.256
schemas / vulnerabilities (pull_request) Successful in 4m44s
schemas / check-release (pull_request) Successful in 10m55s
schemas / check (pull_request) Successful in 12m29s
pre-commit / pre-commit (pull_request) Successful in 20m0s
schemas / build (pull_request) Successful in 10m4s
schemas / deploy-prod (pull_request) Has been skipped
2026-02-23 16:07:55 +00:00
argoyle 3bb0511277 Merge pull request 'chore(release): prepare for v0.9.4' (#732) from next-release into main
Release / release (push) Successful in 1m36s
schemas / vulnerabilities (push) Successful in 2m31s
schemas / check-release (push) Successful in 5m33s
schemas / check (push) Successful in 6m17s
pre-commit / pre-commit (push) Successful in 10m5s
Goreleaser / release (push) Successful in 3m39s
schemas / build (push) Failing after 1m1s
schemas / deploy-prod (push) Has been skipped
Reviewed-on: #732
2026-02-23 13:30:22 +00:00
releaser f0fadb4ab7 chore(release): prepare for v0.9.4
schemas / vulnerabilities (pull_request) Successful in 11m30s
schemas / check (pull_request) Successful in 17m40s
pre-commit / pre-commit (pull_request) Successful in 22m31s
schemas / check-release (pull_request) Successful in 25m51s
schemas / build (pull_request) Successful in 23m52s
schemas / deploy-prod (pull_request) Has been skipped
2026-02-23 12:39:51 +00:00
releaser 59c128fe0c chore(release): prepare for v0.9.4 2026-02-23 12:39:47 +00:00
argoyle 9518403394 Merge pull request 'fix(ci): pin goreleaser to v2.13.3 for Gitea SDK compatibility' (#731) from fix-goreleaser-gitea-sdk-compat into main
schemas / vulnerabilities (push) Successful in 3m20s
schemas / check-release (push) Successful in 4m57s
Release / release (push) Successful in 2m12s
schemas / check (push) Successful in 18m55s
pre-commit / pre-commit (push) Successful in 16m38s
schemas / build (push) Failing after 27m35s
schemas / deploy-prod (push) Has been skipped
Reviewed-on: #731
2026-02-23 12:28:21 +00:00
argoyle 3b4e513653 fix(ci): pin goreleaser to v2.13.3 for Gitea SDK compatibility
schemas / vulnerabilities (pull_request) Successful in 2m19s
schemas / check-release (pull_request) Successful in 6m5s
schemas / check (pull_request) Successful in 6m24s
pre-commit / pre-commit (pull_request) Successful in 12m13s
schemas / build (pull_request) Successful in 7m21s
schemas / deploy-prod (pull_request) Has been skipped
GoReleaser v2.14.0 upgraded the Gitea SDK from v0.22.1 to v0.23.2,
which fails to parse the Gitea server version during homebrew cask
publishing (error: unknown version: daf0483). Pin to v2.13.3 until
the Gitea server is updated to a compatible version.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 13:08:38 +01:00
argoyle fe1de50ded Merge pull request 'chore(release): prepare for v0.9.3' (#730) from next-release into main
Release / release (push) Successful in 56s
schemas / vulnerabilities (push) Successful in 8m44s
schemas / check-release (push) Successful in 16m53s
schemas / check (push) Successful in 21m15s
pre-commit / pre-commit (push) Successful in 29m33s
schemas / build (push) Successful in 11m14s
schemas / deploy-prod (push) Successful in 1m34s
Goreleaser / release (push) Failing after 5m37s
Reviewed-on: #730
2026-02-23 08:14:51 +00:00
releaser 2d243469ed chore(release): prepare for v0.9.3
schemas / vulnerabilities (pull_request) Successful in 9m25s
schemas / check-release (pull_request) Successful in 16m59s
schemas / check (pull_request) Successful in 27m0s
pre-commit / pre-commit (pull_request) Successful in 32m15s
schemas / build (pull_request) Successful in 7m55s
schemas / deploy-prod (pull_request) Has been skipped
2026-02-23 07:39:42 +00:00
releaser a23aa5e5e9 chore(release): prepare for v0.9.3 2026-02-23 07:39:39 +00:00
argoyle 2286e092d9 Merge pull request 'fix: prevent OOM on rapid schema publishing' (#729) from fix-oom-rapid-schema-publishing into main
schemas / vulnerabilities (push) Successful in 5m37s
Release / release (push) Successful in 1m42s
schemas / check-release (push) Successful in 14m32s
schemas / check (push) Successful in 22m34s
schemas / build (push) Successful in 11m37s
schemas / deploy-prod (push) Successful in 2m35s
pre-commit / pre-commit (push) Successful in 37m18s
Reviewed-on: #729
2026-02-23 07:31:08 +00:00
argoyle 28aa32ad8c fix: prevent OOM on rapid schema publishing
schemas / vulnerabilities (pull_request) Successful in 6m43s
schemas / check-release (pull_request) Successful in 11m27s
schemas / check (pull_request) Successful in 14m51s
pre-commit / pre-commit (pull_request) Successful in 19m39s
schemas / build (pull_request) Successful in 8m26s
schemas / deploy-prod (pull_request) Has been skipped
Add concurrency-limited CosmoGenerator (semaphore limit=1, 60s timeout)
to prevent unbounded concurrent wgc process spawning. Add debouncer
(500ms) to coalesce rapid schema updates per org+ref. Fix double
subgraph fetch in Supergraph resolver and goroutine leak in
SchemaUpdates subscription.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 08:05:47 +01:00
renovate a9885f8b65 fix(deps): update module gitlab.com/unboundsoftware/eventsourced/pg to v1.18.4 (#727)
Release / release (push) Successful in 1m48s
schemas / vulnerabilities (push) Successful in 3m31s
schemas / check-release (push) Successful in 19m53s
schemas / check (push) Successful in 28m16s
pre-commit / pre-commit (push) Successful in 41m21s
schemas / build (push) Successful in 15m44s
schemas / deploy-prod (push) Successful in 1m33s
2026-02-22 23:07:02 +00:00
argoyle 2fbe44fea1 Merge pull request 'fix(deps): update module github.com/pressly/goose/v3 to v3.27.0' (#725) from renovate/github.com-pressly-goose-v3-3.x into main
Release / release (push) Successful in 1m41s
schemas / vulnerabilities (push) Successful in 7m40s
schemas / check-release (push) Successful in 12m45s
schemas / check (push) Successful in 25m22s
pre-commit / pre-commit (push) Successful in 43m42s
schemas / build (push) Successful in 21m37s
schemas / deploy-prod (push) Successful in 1m47s
Reviewed-on: #725
2026-02-22 18:55:45 +00:00
renovate 80d08c1ba7 fix(deps): update module github.com/pressly/goose/v3 to v3.27.0
schemas / vulnerabilities (pull_request) Successful in 13m33s
schemas / check-release (pull_request) Successful in 31m22s
schemas / check (pull_request) Successful in 38m33s
pre-commit / pre-commit (pull_request) Successful in 57m7s
schemas / build (pull_request) Successful in 28m17s
schemas / deploy-prod (pull_request) Has been skipped
2026-02-22 17:09:40 +00:00
argoyle d6866b9f72 Merge pull request 'chore(deps): update actions/setup-node action to v6' (#724) from renovate/actions-setup-node-6.x into main
Release / release (push) Failing after 1m50s
schemas / vulnerabilities (push) Successful in 4m33s
schemas / check-release (push) Successful in 7m19s
schemas / check (push) Successful in 8m18s
pre-commit / pre-commit (push) Successful in 16m9s
schemas / build (push) Successful in 10m21s
schemas / deploy-prod (push) Successful in 1m1s
Reviewed-on: #724
2026-02-22 13:22:13 +00:00
renovate 3c1fc68016 chore(deps): update actions/setup-node action to v6
schemas / vulnerabilities (pull_request) Successful in 1m46s
schemas / check-release (pull_request) Successful in 4m33s
schemas / check (pull_request) Successful in 11m3s
pre-commit / pre-commit (pull_request) Successful in 14m11s
schemas / build (pull_request) Successful in 4m31s
schemas / deploy-prod (pull_request) Has been skipped
2026-02-22 11:35:29 +00:00
argoyle 817449afbc Merge pull request 'fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.255' (#721) from renovate/github.com-wundergraph-graphql-go-tools-v2-2.x into main
Release / release (push) Successful in 1m18s
schemas / vulnerabilities (push) Successful in 6m17s
schemas / check-release (push) Successful in 7m2s
schemas / check (push) Successful in 8m8s
schemas / build (push) Successful in 15m11s
schemas / deploy-prod (push) Successful in 1m57s
pre-commit / pre-commit (push) Successful in 25m52s
Reviewed-on: #721
2026-02-22 11:14:35 +00:00
argoyle 856423a221 Merge pull request 'chore(deps): update goreleaser/goreleaser-action action to v7' (#720) from renovate/goreleaser-goreleaser-action-7.x into main
Release / release (push) Successful in 2m17s
schemas / vulnerabilities (push) Successful in 4m32s
schemas / check (push) Successful in 7m22s
schemas / check-release (push) Successful in 8m32s
schemas / build (push) Successful in 7m4s
schemas / deploy-prod (push) Successful in 1m11s
pre-commit / pre-commit (push) Successful in 18m44s
Reviewed-on: #720
2026-02-22 10:48:57 +00:00
renovate 3c43f1bd80 chore(deps): update goreleaser/goreleaser-action action to v7
schemas / vulnerabilities (pull_request) Successful in 2m19s
pre-commit / pre-commit (pull_request) Successful in 11m12s
schemas / check-release (pull_request) Successful in 14m14s
schemas / check (pull_request) Successful in 19m52s
schemas / build (pull_request) Successful in 7m24s
schemas / deploy-prod (pull_request) Has been skipped
2026-02-22 10:03:48 +01:00
renovate 4fd36c1330 fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.255
schemas / vulnerabilities (pull_request) Successful in 4m39s
schemas / check-release (pull_request) Successful in 6m29s
schemas / check (pull_request) Successful in 9m54s
pre-commit / pre-commit (pull_request) Successful in 12m35s
schemas / build (pull_request) Successful in 7m25s
schemas / deploy-prod (pull_request) Has been skipped
2026-02-21 22:06:11 +00:00
renovate 9411a2dd7d fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.254 (#718)
schemas / vulnerabilities (push) Successful in 3m34s
Release / release (push) Successful in 5m28s
schemas / check-release (push) Successful in 5m53s
schemas / check (push) Successful in 10m11s
pre-commit / pre-commit (push) Successful in 19m21s
schemas / build (push) Successful in 9m58s
schemas / deploy-prod (push) Successful in 2m14s
2026-02-20 20:07:54 +00:00
argoyle f1dc71163b Merge pull request 'fix(deps): update module github.com/99designs/gqlgen to v0.17.87' (#713) from renovate/github.com-99designs-gqlgen-0.x into main
schemas / vulnerabilities (push) Successful in 4m50s
Release / release (push) Successful in 5m18s
schemas / check-release (push) Successful in 6m1s
schemas / check (push) Successful in 10m14s
pre-commit / pre-commit (push) Successful in 12m39s
schemas / build (push) Successful in 6m49s
schemas / deploy-prod (push) Successful in 1m44s
Reviewed-on: #713
2026-02-20 14:04:17 +00:00
renovate 08e84ac1dc fix(deps): update module github.com/99designs/gqlgen to v0.17.87
schemas / check-release (pull_request) Successful in 7m11s
schemas / check (pull_request) Successful in 10m16s
pre-commit / pre-commit (pull_request) Successful in 13m23s
schemas / vulnerabilities (pull_request) Successful in 21m6s
schemas / build (pull_request) Successful in 8m44s
schemas / deploy-prod (pull_request) Has been skipped
2026-02-20 14:26:46 +01:00
renovate 3ea1ef520c fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.253 (#716)
Release / release (push) Failing after 1m32s
schemas / vulnerabilities (push) Successful in 2m38s
schemas / check-release (push) Successful in 3m21s
schemas / check (push) Successful in 6m6s
pre-commit / pre-commit (push) Successful in 9m19s
schemas / build (push) Successful in 6m3s
schemas / deploy-prod (push) Failing after 2m59s
2026-02-20 12:24:11 +00:00
renovate e30ab93a47 fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.252 (#714)
schemas / vulnerabilities (push) Successful in 2m4s
Release / release (push) Successful in 2m23s
schemas / check-release (push) Successful in 3m36s
schemas / check (push) Successful in 5m25s
pre-commit / pre-commit (push) Successful in 9m0s
schemas / build (push) Successful in 11m55s
schemas / deploy-prod (push) Successful in 2m19s
2026-02-19 16:30:35 +00:00
argoyle 0eb59235c1 Merge pull request 'fix(deps): update module github.com/vektah/gqlparser/v2 to v2.5.32' (#711) from renovate/github.com-vektah-gqlparser-v2-2.x into main
schemas / vulnerabilities (push) Successful in 4m16s
schemas / check-release (push) Successful in 5m2s
Release / release (push) Successful in 3m50s
schemas / check (push) Successful in 9m44s
pre-commit / pre-commit (push) Successful in 16m21s
schemas / build (push) Successful in 12m51s
schemas / deploy-prod (push) Failing after 4m59s
Reviewed-on: #711
2026-02-19 15:27:00 +00:00
renovate 31022037ff fix(deps): update module github.com/vektah/gqlparser/v2 to v2.5.32
schemas / vulnerabilities (pull_request) Successful in 4m17s
schemas / check-release (pull_request) Successful in 6m2s
schemas / check (pull_request) Successful in 9m14s
pre-commit / pre-commit (pull_request) Successful in 12m3s
schemas / build (pull_request) Successful in 9m27s
schemas / deploy-prod (pull_request) Has been skipped
2026-02-19 13:44:52 +00:00
argoyle 085bf35d92 Merge pull request 'fix(deps): update module gitlab.com/unboundsoftware/eventsourced/pg to v1.18.3' (#708) from renovate/eventsourced into main
Release / release (push) Successful in 1m53s
schemas / vulnerabilities (push) Successful in 4m17s
schemas / check-release (push) Successful in 8m14s
schemas / check (push) Successful in 12m36s
pre-commit / pre-commit (push) Successful in 17m14s
schemas / build (push) Successful in 11m43s
schemas / deploy-prod (push) Successful in 1m32s
Reviewed-on: #708
2026-02-18 12:47:52 +00:00
argoyle 9d655e1a23 Merge pull request 'fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.251' (#707) from renovate/github.com-wundergraph-graphql-go-tools-v2-2.x into main
Release / release (push) Successful in 1m11s
schemas / vulnerabilities (push) Successful in 3m35s
schemas / check-release (push) Successful in 5m23s
schemas / check (push) Successful in 10m11s
schemas / build (push) Successful in 7m50s
schemas / deploy-prod (push) Successful in 2m15s
pre-commit / pre-commit (push) Successful in 20m42s
Reviewed-on: #707
2026-02-18 12:46:17 +00:00
renovate eee1d45d8c fix(deps): update module gitlab.com/unboundsoftware/eventsourced/pg to v1.18.3
schemas / vulnerabilities (pull_request) Successful in 10m29s
schemas / check-release (pull_request) Successful in 25m55s
schemas / check (pull_request) Successful in 43m26s
pre-commit / pre-commit (pull_request) Successful in 55m0s
schemas / build (pull_request) Successful in 29m31s
schemas / deploy-prod (pull_request) Has been skipped
2026-02-18 11:08:55 +00:00
renovate 782185981f fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.251
schemas / vulnerabilities (pull_request) Successful in 10m52s
schemas / check-release (pull_request) Successful in 12m47s
schemas / check (pull_request) Successful in 30m49s
pre-commit / pre-commit (pull_request) Successful in 57m57s
schemas / build (pull_request) Successful in 22m15s
schemas / deploy-prod (pull_request) Has been skipped
2026-02-18 11:08:42 +00:00
renovate e3f156fe9e chore(deps): update pre-commit hook golangci/golangci-lint to v2.10.1 (#706)
Release / release (push) Failing after 1m29s
schemas / check (push) Successful in 3m12s
schemas / vulnerabilities (push) Successful in 5m8s
schemas / check-release (push) Successful in 6m8s
pre-commit / pre-commit (push) Successful in 14m2s
schemas / build (push) Successful in 9m32s
schemas / deploy-prod (push) Successful in 2m4s
2026-02-17 17:55:12 +00:00
renovate 230876b523 fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.250 (#704)
Release / release (push) Failing after 2m21s
schemas / vulnerabilities (push) Successful in 4m47s
schemas / check-release (push) Successful in 10m4s
schemas / check (push) Successful in 12m57s
pre-commit / pre-commit (push) Successful in 15m14s
schemas / build (push) Successful in 9m15s
schemas / deploy-prod (push) Successful in 51s
2026-02-16 17:05:42 +00:00
renovate e64adc96ae fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.249 (#702)
Release / release (push) Successful in 2m53s
schemas / vulnerabilities (push) Successful in 4m10s
schemas / check (push) Successful in 11m2s
schemas / check-release (push) Successful in 11m33s
pre-commit / pre-commit (push) Successful in 16m21s
schemas / build (push) Successful in 6m34s
schemas / deploy-prod (push) Successful in 57s
2026-02-13 21:35:11 +00:00
argoyle f534752e2c Merge pull request 'chore(release): prepare for v0.9.2' (#701) from next-release into main
Release / release (push) Successful in 1m20s
schemas / vulnerabilities (push) Successful in 5m8s
schemas / check (push) Successful in 8m3s
schemas / check-release (push) Successful in 7m12s
Goreleaser / release (push) Successful in 7m12s
pre-commit / pre-commit (push) Successful in 12m36s
schemas / build (push) Successful in 5m40s
schemas / deploy-prod (push) Successful in 54s
Reviewed-on: #701
2026-02-13 09:28:17 +00:00
releaser 42f4c8014d chore(release): prepare for v0.9.2
schemas / vulnerabilities (pull_request) Successful in 3m25s
schemas / check-release (pull_request) Successful in 4m6s
schemas / check (pull_request) Successful in 7m0s
schemas / build (pull_request) Successful in 4m17s
schemas / deploy-prod (pull_request) Has been skipped
pre-commit / pre-commit (pull_request) Successful in 10m25s
2026-02-13 09:14:54 +00:00
releaser e098562e0c chore(release): prepare for v0.9.2 2026-02-13 09:14:50 +00:00
renovate 948ab167c1 chore(deps): update node.js to v24.13.1 (#699)
schemas / vulnerabilities (push) Successful in 3m20s
schemas / check (push) Successful in 4m36s
schemas / check-release (push) Successful in 5m3s
schemas / build (push) Successful in 5m40s
pre-commit / pre-commit (push) Successful in 11m19s
schemas / deploy-prod (push) Successful in 1m19s
Release / release (push) Successful in 1m3s
2026-02-12 18:19:39 +00:00
renovate 404c4106bc fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.248 (#698)
schemas / vulnerabilities (push) Successful in 5m13s
schemas / check-release (push) Successful in 5m36s
Release / release (push) Successful in 4m12s
schemas / check (push) Successful in 12m42s
pre-commit / pre-commit (push) Successful in 16m33s
schemas / build (push) Successful in 15m24s
schemas / deploy-prod (push) Failing after 2m47s
2026-02-11 20:17:37 +00:00
renovate 593874ba52 chore(deps): update golang docker tag to v1.26.0 (#696)
Release / release (push) Successful in 4m12s
schemas / vulnerabilities (push) Successful in 6m10s
schemas / check (push) Successful in 6m44s
pre-commit / pre-commit (push) Successful in 9m35s
schemas / check-release (push) Successful in 16m33s
schemas / build (push) Successful in 1h10m47s
schemas / deploy-prod (push) Successful in 2m41s
2026-02-11 15:30:17 +00:00
renovate bcbdc4458e chore(deps): update pre-commit hook golangci/golangci-lint to v2.9.0 (#697)
Release / release (push) Successful in 1m37s
schemas / vulnerabilities (push) Successful in 3m42s
schemas / check-release (push) Successful in 6m28s
schemas / check (push) Successful in 14m42s
pre-commit / pre-commit (push) Successful in 22m33s
schemas / build (push) Successful in 25m34s
schemas / deploy-prod (push) Successful in 1m9s
2026-02-11 13:51:39 +00:00
renovate 4b6637b027 chore(deps): update golang:1.25.7 docker digest to d2819ff (#695)
schemas / check (push) Failing after 2s
schemas / check-release (push) Failing after 2s
pre-commit / pre-commit (push) Failing after 2s
schemas / vulnerabilities (push) Failing after 3s
schemas / build (push) Has been skipped
Release / release (push) Failing after 2s
schemas / deploy-prod (push) Has been skipped
2026-02-11 00:22:36 +00:00
renovate bc7729b702 fix(deps): update module golang.org/x/crypto to v0.48.0 (#694)
Release / release (push) Failing after 2m0s
schemas / vulnerabilities (push) Successful in 5m6s
schemas / check-release (push) Successful in 6m0s
schemas / check (push) Successful in 13m25s
schemas / build (push) Successful in 4m41s
pre-commit / pre-commit (push) Successful in 17m25s
schemas / deploy-prod (push) Successful in 1m21s
2026-02-09 17:30:20 +00:00
renovate 942508e467 fix(deps): update eventsourced (#693)
Release / release (push) Successful in 1m8s
schemas / vulnerabilities (push) Successful in 1m50s
schemas / check-release (push) Successful in 6m4s
schemas / check (push) Successful in 6m7s
pre-commit / pre-commit (push) Successful in 15m11s
schemas / build (push) Successful in 11m52s
schemas / deploy-prod (push) Successful in 1m28s
2026-02-07 20:32:28 +00:00
renovate 59e8a3c43d fix(deps): update module github.com/alecthomas/kong to v1.14.0 (#692)
schemas / vulnerabilities (push) Successful in 2m1s
Release / release (push) Successful in 2m5s
schemas / check-release (push) Successful in 5m56s
pre-commit / pre-commit (push) Successful in 8m13s
schemas / check (push) Successful in 8m45s
schemas / build (push) Successful in 8m24s
schemas / deploy-prod (push) Successful in 55s
2026-02-07 00:41:14 +00:00
renovate d7358d8053 fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.247 (#691)
Release / release (push) Failing after 1m33s
schemas / vulnerabilities (push) Successful in 3m28s
schemas / check-release (push) Successful in 3m31s
schemas / check (push) Successful in 8m56s
pre-commit / pre-commit (push) Successful in 12m25s
schemas / build (push) Successful in 6m11s
schemas / deploy-prod (push) Successful in 1m1s
2026-02-06 13:17:32 +00:00
argoyle 2350099e7f Merge pull request 'chore(deps): update golang docker tag to v1.25.7' (#690) from renovate/golang-1.x into main
Release / release (push) Successful in 1m16s
schemas / vulnerabilities (push) Successful in 3m35s
schemas / check-release (push) Successful in 4m11s
pre-commit / pre-commit (push) Successful in 8m11s
schemas / check (push) Successful in 9m18s
schemas / build (push) Successful in 5m0s
schemas / deploy-prod (push) Successful in 1m24s
Reviewed-on: #690
2026-02-05 12:00:52 +00:00
renovate ab5a8a9f6a chore(deps): update golang docker tag to v1.25.7
schemas / vulnerabilities (pull_request) Successful in 2m14s
schemas / check (pull_request) Successful in 4m45s
schemas / check-release (pull_request) Successful in 10m57s
schemas / build (pull_request) Successful in 4m32s
schemas / deploy-prod (pull_request) Has been skipped
2026-02-04 18:15:45 +00:00
renovate 369a0b050a chore(deps): update golang:1.25.6 docker digest to ceda080 (#689)
schemas / check (push) Successful in 3m22s
pre-commit / pre-commit (push) Successful in 7m59s
schemas / check-release (push) Failing after 12m25s
schemas / vulnerabilities (push) Failing after 12m25s
schemas / build (push) Has been skipped
Release / release (push) Failing after 22m10s
schemas / deploy-prod (push) Has been skipped
2026-02-03 09:01:58 +00:00
renovate 3bf43210a1 fix(deps): update module go.opentelemetry.io/contrib/bridges/otelslog to v0.15.0 (#688)
Release / release (push) Successful in 55s
schemas / vulnerabilities (push) Successful in 1m31s
schemas / check-release (push) Successful in 4m58s
schemas / check (push) Successful in 5m51s
pre-commit / pre-commit (push) Successful in 7m51s
schemas / build (push) Successful in 8m38s
schemas / deploy-prod (push) Successful in 55s
2026-02-02 20:48:58 +00:00
renovate 148f8befe7 fix(deps): update opentelemetry-go monorepo (#687)
Release / release (push) Failing after 47s
schemas / vulnerabilities (push) Successful in 2m21s
schemas / check (push) Successful in 3m27s
schemas / check-release (push) Successful in 4m42s
schemas / build (push) Successful in 5m9s
schemas / deploy-prod (push) Successful in 1m25s
pre-commit / pre-commit (push) Successful in 15m42s
2026-02-02 19:34:16 +00:00
renovate 90b9e91d20 fix(deps): update module gitlab.com/unboundsoftware/eventsourced/pg to v1.18.1 (#686)
schemas / vulnerabilities (push) Successful in 1m41s
Release / release (push) Successful in 1m24s
schemas / check-release (push) Successful in 3m33s
schemas / check (push) Successful in 7m19s
pre-commit / pre-commit (push) Successful in 13m29s
schemas / build (push) Successful in 9m25s
schemas / deploy-prod (push) Successful in 57s
2026-01-30 07:24:43 +00:00
renovate 985bca2f58 fix(deps): update module gitlab.com/unboundsoftware/eventsourced/pg to v1.18.0 (#685)
schemas / vulnerabilities (push) Successful in 1m31s
Release / release (push) Successful in 1m23s
schemas / check-release (push) Successful in 6m10s
schemas / check (push) Successful in 12m6s
schemas / build (push) Successful in 3m24s
pre-commit / pre-commit (push) Successful in 16m9s
schemas / deploy-prod (push) Successful in 53s
2026-01-29 10:42:16 +00:00
renovate 3fce241596 chore(deps): update node.js to cd6fb7e (#684)
Release / release (push) Successful in 1m49s
schemas / check-release (push) Successful in 4m33s
schemas / vulnerabilities (push) Successful in 4m34s
schemas / check (push) Successful in 5m5s
schemas / build (push) Successful in 5m1s
schemas / deploy-prod (push) Successful in 1m30s
pre-commit / pre-commit (push) Successful in 13m38s
2026-01-28 08:30:07 +00:00
renovate a0baa44320 fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.246 (#683)
Release / release (push) Successful in 1m12s
schemas / vulnerabilities (push) Successful in 3m44s
schemas / check-release (push) Successful in 5m47s
schemas / check (push) Successful in 8m53s
pre-commit / pre-commit (push) Successful in 9m12s
schemas / build (push) Successful in 6m25s
schemas / deploy-prod (push) Successful in 58s
2026-01-27 14:40:50 +00:00
renovate 60939c3159 fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.245 (#682)
Release / release (push) Successful in 2m50s
schemas / vulnerabilities (push) Successful in 4m56s
schemas / check-release (push) Successful in 7m4s
schemas / check (push) Successful in 15m32s
schemas / build (push) Successful in 5m3s
pre-commit / pre-commit (push) Successful in 20m34s
schemas / deploy-prod (push) Successful in 1m47s
2026-01-26 14:25:49 +00:00
renovate 6530408f15 fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.244 (#681)
Release / release (push) Failing after 3m11s
schemas / vulnerabilities (push) Successful in 4m53s
schemas / check-release (push) Successful in 5m38s
schemas / check (push) Successful in 14m0s
pre-commit / pre-commit (push) Successful in 16m56s
schemas / build (push) Successful in 7m22s
schemas / deploy-prod (push) Successful in 1m4s
2026-01-26 10:08:30 +00:00
renovate fd6694614e fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.243 (#680)
Release / release (push) Successful in 2m44s
schemas / vulnerabilities (push) Successful in 5m49s
schemas / check-release (push) Successful in 12m43s
schemas / check (push) Successful in 14m54s
schemas / build (push) Successful in 15m55s
schemas / deploy-prod (push) Failing after 4m4s
pre-commit / pre-commit (push) Successful in 57m25s
2026-01-20 15:13:13 +00:00
argoyle 6143a96c75 Merge pull request 'fix(deps): update module github.com/auth0/go-jwt-middleware/v2 to v3' (#678) from renovate/github.com-auth0-go-jwt-middleware-v2-3.x into main
Release / release (push) Successful in 1m39s
schemas / vulnerabilities (push) Successful in 2m53s
schemas / check (push) Successful in 3m11s
schemas / check-release (push) Successful in 3m51s
pre-commit / pre-commit (push) Successful in 8m5s
schemas / build (push) Successful in 5m33s
schemas / deploy-prod (push) Successful in 1m0s
Reviewed-on: #678
2026-01-19 20:07:08 +00:00
argoyle 817927cb7d fix: migrate to go-jwt-middleware v3 API
schemas / check-release (pull_request) Successful in 1m57s
schemas / vulnerabilities (pull_request) Successful in 2m48s
schemas / check (pull_request) Successful in 8m17s
pre-commit / pre-commit (pull_request) Successful in 11m38s
schemas / build (pull_request) Successful in 5m31s
schemas / deploy-prod (pull_request) Has been skipped
- Use validator and jwks packages for JWT validation
- Replace manual JWKS caching with jwks.NewCachingProvider
- Add CustomClaims struct for https://unbound.se/roles claim
- Rename TokenFromContext to ClaimsFromContext
- Update middleware/auth.go to use new claims structure
- Update tests to use core.SetClaims and validator.ValidatedClaims

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 20:31:45 +01:00
renovate e2c1803683 fix(deps): update module github.com/auth0/go-jwt-middleware/v2 to v3 2026-01-19 20:19:58 +01:00
argoyle d79a10e910 Merge pull request 'chore(release): prepare for v0.9.1' (#677) from next-release into main
schemas / vulnerabilities (push) Successful in 1m58s
schemas / check-release (push) Successful in 2m40s
pre-commit / pre-commit (push) Successful in 7m12s
Goreleaser / release (push) Successful in 2m30s
schemas / check (push) Successful in 5m21s
schemas / build (push) Successful in 4m0s
schemas / deploy-prod (push) Has been skipped
Release / release (push) Successful in 1m18s
Reviewed-on: #677
2026-01-18 09:46:25 +00:00
releaser 48f0ec0bbe chore(release): prepare for v0.9.1 2026-01-18 09:43:20 +00:00
releaser b89b124a0e chore(release): prepare for v0.9.1 2026-01-18 09:43:11 +00:00
argoyle d008303068 Merge pull request 'fix(ci): run build job on tags for Docker images' (#676) from fix-build-on-tags into main
schemas / vulnerabilities (push) Successful in 1m21s
Release / release (push) Successful in 1m30s
schemas / check-release (push) Successful in 2m57s
schemas / check (push) Successful in 4m1s
pre-commit / pre-commit (push) Successful in 7m0s
schemas / build (push) Successful in 5m52s
schemas / deploy-prod (push) Successful in 1m6s
Reviewed-on: #676
2026-01-18 09:41:09 +00:00
argoyle e328ef5a64 fix(ci): run build job on tags for Docker images
schemas / vulnerabilities (pull_request) Successful in 1m33s
schemas / check (pull_request) Successful in 2m55s
schemas / check-release (pull_request) Successful in 2m9s
pre-commit / pre-commit (pull_request) Successful in 5m40s
schemas / build (pull_request) Successful in 4m20s
schemas / deploy-prod (pull_request) Has been skipped
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 10:30:24 +01:00
argoyle b2506188f3 Merge pull request 'chore(release): prepare for v0.9.0' (#639) from next-release into main
schemas / check (push) Successful in 11m47s
schemas / deploy-prod (push) Successful in 1m38s
Goreleaser / release (push) Successful in 16m29s
schemas / build (push) Successful in 12m52s
pre-commit / pre-commit (push) Successful in 31m14s
Release / release (push) Successful in 1m23s
schemas / vulnerabilities (push) Successful in 6m23s
schemas / check-release (push) Successful in 12m39s
Reviewed-on: #639
2026-01-18 07:46:37 +00:00
releaser 16ce04ea86 chore(release): prepare for v0.9.0 2026-01-17 22:21:23 +00:00
releaser 1d0f82a851 chore(release): prepare for v0.9.0 2026-01-17 22:20:56 +00:00
argoyle 7be533dc6c Merge pull request 'feat: migrate from GitLab CI to Gitea Actions' (#675) from migrate-to-gitea into main
Release / release (push) Successful in 2m34s
schemas / vulnerabilities (push) Successful in 4m48s
schemas / check-release (push) Successful in 5m40s
schemas / check (push) Successful in 6m46s
pre-commit / pre-commit (push) Successful in 6m29s
schemas / build (push) Successful in 5m3s
schemas / deploy-prod (push) Successful in 59s
Reviewed-on: #675
2026-01-17 22:18:02 +00:00
argoyle 73eae98929 feat: migrate from GitLab CI to Gitea Actions
schemas / vulnerabilities (pull_request) Successful in 2m15s
schemas / check-release (pull_request) Successful in 2m17s
schemas / check (pull_request) Successful in 4m48s
pre-commit / pre-commit (pull_request) Successful in 5m58s
schemas / build (pull_request) Successful in 3m36s
schemas / deploy-prod (pull_request) Has been skipped
- Update git remote to git.unbound.se
- Add Gitea workflows: ci.yaml, pre-commit.yaml, release.yaml, goreleaser.yaml
- Delete .gitlab-ci.yml
- Update Go module path to gitea.unbound.se/unboundsoftware/schemas
- Update all imports to new module path
- Update Docker registry to oci.unbound.se
- Update .goreleaser.yml for Gitea releases with internal cluster URL
- Remove GitLab CI linter from pre-commit config
- Use shared release workflow with tag_only for versioning

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 22:53:46 +01:00
argoyle aa41f48b0e Merge branch 'renovate/golang-1.x' into 'main'
chore(deps): update golang docker tag to v1.25.6

See merge request unboundsoftware/schemas!670
2026-01-17 20:12:07 +01:00
Renovate bb0c020812 chore(deps): update golang docker tag to v1.25.6 2026-01-15 23:56:35 +00:00
Unbound Release f0c7415d88 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release fbe180020c chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 3eba214a72 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 134b571baa chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release a1a23d69cf chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 6f1dda7be5 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release d50827018a chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 8a6163a921 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 054cfa1e52 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 7d25af472a chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release ec0d5dff74 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release e3d19384a0 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 42adcb1df5 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release e072ad685b chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 4222eb1268 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release ab3eafa331 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 86612d9e25 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release abc5668d33 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 892fc29331 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release bdb6e37b22 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 8d512e0290 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 3ab0a8e701 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 3bd3daccaf chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 3d6016d7e2 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 31cc2e10b6 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 136623c04b chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 12ff2fa1ba chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 1aef6a7a31 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release c02034d6b0 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release dc4555a168 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 0571f4d986 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 3c194dc127 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release f7ba0e3dc7 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 33667d4de1 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release bbf82399e1 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release f927d1c750 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release ef273520b5 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release c8e6287761 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 7052f0bfb1 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release c6f35f324e chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 08f0ee3374 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release d3c7941de8 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release b1a9021fee chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release ab32fddd2b chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release ded73a3065 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 4c571197b8 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release 9f276d7420 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release f56dfeafa7 chore(release): prepare for v0.9.0 2026-01-15 07:51:24 +00:00
Unbound Release f6f3810ffb chore(release): prepare for v0.8.1 2026-01-15 07:51:24 +00:00
Unbound Release 9d32174ae4 chore(release): prepare for v0.8.1 2026-01-15 07:51:24 +00:00
Unbound Release 26073e744d chore(release): prepare for v0.8.1 2026-01-15 07:51:24 +00:00
Unbound Release 6e38f3d5b5 chore(release): prepare for v0.8.1 2026-01-15 07:51:24 +00:00
argoyle 9199be9da5 Merge branch 'renovate/node-24.x' into 'main'
chore(deps): update node.js to v24.13.0

See merge request unboundsoftware/schemas!669
2026-01-15 08:43:39 +01:00
Renovate e3dcb013b6 chore(deps): update node.js to v24.13.0 2026-01-15 05:55:55 +00:00
argoyle 5982f3ac41 Merge branch 'renovate/alessandrojcm-commitlint-pre-commit-hook-9.x' into 'main'
chore(deps): update pre-commit hook alessandrojcm/commitlint-pre-commit-hook to v9.24.0

See merge request unboundsoftware/schemas!668
2026-01-14 07:10:27 +01:00
Renovate eb23f2f62b chore(deps): update pre-commit hook alessandrojcm/commitlint-pre-commit-hook to v9.24.0 2026-01-13 23:56:25 +00:00
argoyle 1effc3937a Merge branch 'renovate/github.com-99designs-gqlgen-0.x' into 'main'
fix(deps): update module github.com/99designs/gqlgen to v0.17.86

See merge request unboundsoftware/schemas!665
2026-01-13 09:28:17 +01:00
Renovate b69de655e4 fix(deps): update module github.com/99designs/gqlgen to v0.17.86 2026-01-13 08:40:58 +01:00
argoyle 73fdf6c896 Merge branch 'renovate/golang-1.25.5' into 'main'
chore(deps): update golang:1.25.5 docker digest to 3a01526

See merge request unboundsoftware/schemas!667
2026-01-13 08:39:13 +01:00
argoyle bf0953b520 Merge branch 'renovate/golang.org-x-crypto-0.x' into 'main'
fix(deps): update module golang.org/x/crypto to v0.47.0

See merge request unboundsoftware/schemas!666
2026-01-13 08:38:49 +01:00
argoyle 95aaea7ecf Merge branch 'renovate/goreleaser-goreleaser-2.x' into 'main'
chore(deps): update goreleaser/goreleaser docker tag to v2.13.3

See merge request unboundsoftware/schemas!664
2026-01-13 08:38:24 +01:00
Renovate 81c3a7ff13 chore(deps): update golang:1.25.5 docker digest to 3a01526 2026-01-13 05:57:40 +00:00
Renovate e322f7f7ad fix(deps): update module golang.org/x/crypto to v0.47.0 2026-01-12 17:58:30 +00:00
Renovate 4997c53968 chore(deps): update goreleaser/goreleaser docker tag to v2.13.3 2026-01-10 05:56:48 +00:00
argoyle cfcd023a83 Merge branch 'renovate/golangci-golangci-lint-2.x' into 'main'
chore(deps): update pre-commit hook golangci/golangci-lint to v2.8.0

See merge request unboundsoftware/schemas!663
2026-01-09 11:39:29 +01:00
Renovate 34e7a0b718 chore(deps): update pre-commit hook golangci/golangci-lint to v2.8.0 2026-01-07 21:59:31 +00:00
argoyle 7d38c31370 Merge branch 'renovate/golang-1.25.5' into 'main'
chore(deps): update golang:1.25.5 docker digest to ad03ba9

See merge request unboundsoftware/schemas!662
2025-12-30 19:39:57 +01:00
Renovate 968553b532 chore(deps): update golang:1.25.5 docker digest to ad03ba9 2025-12-30 03:58:45 +00:00
argoyle 0067b42f92 Merge branch 'renovate/goreleaser-goreleaser-2.x' into 'main'
chore(deps): update goreleaser/goreleaser docker tag to v2.13.2

See merge request unboundsoftware/schemas!661
2025-12-25 13:34:02 +01:00
Renovate 48776b6afc chore(deps): update goreleaser/goreleaser docker tag to v2.13.2 2025-12-24 19:09:17 +00:00
argoyle 03cf6be52c Merge branch 'renovate/github.com-wundergraph-graphql-go-tools-v2-2.x' into 'main'
fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.242

See merge request unboundsoftware/schemas!660
2025-12-19 13:15:38 +01:00
Renovate 6b5e66b1be fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.242 2025-12-19 12:00:46 +00:00
argoyle 25c3bf2356 Merge branch 'renovate/node-24.12.0-alpine' into 'main'
chore(deps): update node.js to c921b97

See merge request unboundsoftware/schemas!659
2025-12-18 07:12:30 +01:00
Renovate 7e5bbf3baa chore(deps): update node.js to c921b97 2025-12-18 04:57:56 +00:00
argoyle 8adae23068 Merge branch 'renovate/github.com-99designs-gqlgen-0.x' into 'main'
fix(deps): update module github.com/99designs/gqlgen to v0.17.85

See merge request unboundsoftware/schemas!658
2025-12-17 08:36:17 +01:00
Renovate 0e8d0965b7 fix(deps): update module github.com/99designs/gqlgen to v0.17.85 2025-12-17 07:58:03 +01:00
argoyle cfc5de1bb9 Merge branch 'renovate/node-24.x' into 'main'
chore(deps): update node.js to v24.12.0

See merge request unboundsoftware/schemas!657
2025-12-12 08:42:09 +01:00
Renovate 63567eaa8b chore(deps): update node.js to v24.12.0 2025-12-11 22:58:46 +00:00
argoyle e38e7a2936 Merge branch 'renovate/github.com-wundergraph-graphql-go-tools-v2-2.x' into 'main'
fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.241

See merge request unboundsoftware/schemas!656
2025-12-10 13:22:05 +01:00
Renovate 718585ebe8 fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.241 2025-12-10 11:58:49 +00:00
argoyle 6b3515ed14 Merge branch 'renovate/golang-1.25.5' into 'main'
chore(deps): update golang:1.25.5 docker digest to 0c27bcf

See merge request unboundsoftware/schemas!655
2025-12-09 09:40:20 +01:00
argoyle fee47b271a Merge branch 'renovate/opentelemetry-go-contrib-monorepo' into 'main'
fix(deps): update module go.opentelemetry.io/contrib/bridges/otelslog to v0.14.0

See merge request unboundsoftware/schemas!654
2025-12-09 09:40:00 +01:00
argoyle 1da8439372 Merge branch 'renovate/golang.org-x-crypto-0.x' into 'main'
fix(deps): update module golang.org/x/crypto to v0.46.0

See merge request unboundsoftware/schemas!653
2025-12-09 09:39:37 +01:00
Renovate 2de1324458 chore(deps): update golang:1.25.5 docker digest to 0c27bcf 2025-12-09 02:12:10 +00:00
Renovate 4673ecdd85 fix(deps): update module golang.org/x/crypto to v0.46.0 2025-12-08 22:11:32 +00:00
Renovate d92f24b0a1 fix(deps): update module go.opentelemetry.io/contrib/bridges/otelslog to v0.14.0 2025-12-08 22:11:26 +00:00
argoyle f80ee9d391 Merge branch 'renovate/opentelemetry-go-monorepo' into 'main'
fix(deps): update opentelemetry-go monorepo

See merge request unboundsoftware/schemas!651
2025-12-08 22:31:13 +01:00
Renovate 3da293252d fix(deps): update opentelemetry-go monorepo 2025-12-08 21:31:33 +01:00
argoyle 6ae6a4d6cf Merge branch 'test-cache-legacy-hash-optimizations' into 'main'
test(cache): update tests to use legacy hash for speed

See merge request unboundsoftware/schemas!652
2025-12-08 21:31:02 +01:00
argoyle 129cd8aad1 test(cache): update tests to use legacy hash for speed
Update test setup to leverage legacy hash for API keys in order  
to avoid the performance impact of bcrypt. Replace the API key  
based test with a user subscription-based test to enhance  
concurrency and reliability. Replace `OrganizationByAPIKey`  
with `OrganizationsByUser` to optimize the retrieval process.
2025-12-08 21:06:02 +01:00
argoyle 8dd2e57c70 Merge branch 'renovate/github.com-wundergraph-graphql-go-tools-v2-2.x' into 'main'
fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.240

See merge request unboundsoftware/schemas!650
2025-12-08 20:14:52 +01:00
Renovate 1914211b85 fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.240 2025-12-08 15:59:27 +00:00
argoyle ea8eb0a68c Merge branch 'renovate/goreleaser-goreleaser-2.x' into 'main'
chore(deps): update goreleaser/goreleaser docker tag to v2.13.1

See merge request unboundsoftware/schemas!649
2025-12-08 09:51:01 +01:00
argoyle e9d0e855af Merge branch 'renovate/golangci-golangci-lint-2.x' into 'main'
chore(deps): update pre-commit hook golangci/golangci-lint to v2.7.2

See merge request unboundsoftware/schemas!648
2025-12-08 09:42:48 +01:00
Renovate 8790fd0d82 chore(deps): update goreleaser/goreleaser docker tag to v2.13.1 2025-12-07 21:57:46 +00:00
Renovate a77a7f3a32 chore(deps): update pre-commit hook golangci/golangci-lint to v2.7.2 2025-12-07 16:59:19 +00:00
argoyle fbe962a7b7 Merge branch 'refactor-cache-optimize-test-setup' into 'main'
refactor(cache): optimize test setup and reduce iterations

See merge request unboundsoftware/schemas!647
2025-12-05 09:08:44 +01:00
argoyle b5bdcc9dbc Merge branch 'fix/docker-update-nodejs-version' into 'main'
fix(docker): update Node.js version to 24.11.1-alpine

See merge request unboundsoftware/schemas!646
2025-12-05 08:51:58 +01:00
argoyle fd1685867e refactor(cache): optimize test setup and reduce iterations
Remove bcrypt hashing for API keys to speed up concurrent tests and 
replace it with a legacy hashing function. Reduce the number of 
concurrent readers and writers in the test to improve performance 
while retaining essential functionality checks. Use a more efficient 
method to fetch organizations within the concurrency test block.
2025-12-05 08:47:11 +01:00
argoyle 114cbf89c5 fix(docker): update Node.js version to 24.11.1-alpine
Updates the Node.js base image in the Dockerfile to the latest
24.11.1-alpine version for improved performance and security. This
change ensures the application runs on a stable and supported
version of Node.js.
2025-12-05 08:32:22 +01:00
argoyle 000ad8b4ad Merge branch 'renovate/node-24-alpine' into 'main'
chore(deps): update node.js to 682368d

See merge request unboundsoftware/schemas!645
2025-12-05 08:20:40 +01:00
Renovate 0820fb542f chore(deps): update node.js to 682368d 2025-12-05 01:57:47 +00:00
argoyle f0d4285bee Merge branch 'renovate/golangci-golangci-lint-2.x' into 'main'
chore(deps): update pre-commit hook golangci/golangci-lint to v2.7.1

See merge request unboundsoftware/schemas!644
2025-12-04 20:42:45 +01:00
Renovate e1c10f0537 chore(deps): update pre-commit hook golangci/golangci-lint to v2.7.1 2025-12-04 15:00:22 +00:00
argoyle f8c593de3e Merge branch 'renovate/golangci-golangci-lint-2.x' into 'main'
chore(deps): update pre-commit hook golangci/golangci-lint to v2.7.0

See merge request unboundsoftware/schemas!643
2025-12-04 08:28:46 +01:00
Renovate 870f29e59f chore(deps): update pre-commit hook golangci/golangci-lint to v2.7.0 2025-12-03 19:58:58 +00:00
argoyle a246c236db Merge branch 'renovate/golang-1.x' into 'main'
chore(deps): update golang docker tag to v1.25.5

See merge request unboundsoftware/schemas!642
2025-12-02 19:43:34 +01:00
Renovate cebeba4461 chore(deps): update golang docker tag to v1.25.5 2025-12-02 18:12:06 +00:00
argoyle b77165f6c8 Merge branch 'renovate/goreleaser-goreleaser-2.x' into 'main'
chore(deps): update goreleaser/goreleaser docker tag to v2.13.0

See merge request unboundsoftware/schemas!641
2025-12-01 07:40:22 +01:00
Renovate 9e85ee1473 chore(deps): update goreleaser/goreleaser docker tag to v2.13.0 2025-11-30 19:57:39 +00:00
argoyle c113eb920b Merge branch 'renovate/gitleaks-gitleaks-8.x' into 'main'
chore(deps): update pre-commit hook gitleaks/gitleaks to v8.30.0

See merge request unboundsoftware/schemas!640
2025-11-27 00:04:46 +01:00
Renovate 90cc64ece9 chore(deps): update pre-commit hook gitleaks/gitleaks to v8.30.0 2025-11-26 18:59:33 +00:00
argoyle ea4df08beb Merge branch 'renovate/github.com-99designs-gqlgen-0.x' into 'main'
fix(deps): update module github.com/99designs/gqlgen to v0.17.84

See merge request unboundsoftware/schemas!639
2025-11-24 20:42:05 +01:00
Renovate ca7e063888 fix(deps): update module github.com/99designs/gqlgen to v0.17.84 2025-11-24 19:34:22 +01:00
argoyle 7b9dc1456b Merge branch 'fix/k8s-update-ingress-class' into 'main'
fix(k8s): update ingress class configuration for schema

See merge request unboundsoftware/schemas!638
2025-11-23 16:00:39 +01:00
argoyle 49af5f0cb1 fix(k8s): update ingress class configuration for schema
Remove the deprecated annotation for ingress class and add 
ingressClassName to align with current Kubernetes standards. 
This ensures better compatibility and adherence to best practices.
2025-11-23 14:28:08 +01:00
argoyle e347d74a39 Merge branch 'feat/manage-organizations-users' into 'main'
feat: add commands for managing organizations and users

See merge request unboundsoftware/schemas!637
2025-11-22 18:57:00 +01:00
argoyle ffcf41b85a feat: add commands for managing organizations and users
Introduce `AddUserToOrganization`, `RemoveAPIKey`, and 
`RemoveOrganization` commands to enhance organization 
management. Implement validation for user addition and 
API key removal. Update GraphQL schema to support new 
mutations and add caching for the new events, ensuring 
that organizations and their relationships are accurately 
represented in the cache.
2025-11-22 18:37:07 +01:00
argoyle 335a9f3b54 Merge branch 'renovate/github.com-wundergraph-graphql-go-tools-v2-2.x' into 'main'
fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.239

See merge request unboundsoftware/schemas!636
2025-11-22 10:00:25 +01:00
Renovate c0e790b684 fix(deps): update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.239 2025-11-21 22:08:12 +00:00
argoyle 3b47365f10 Merge branch 'test/add-validation-event-tests' into 'main'
test: add validation and event tests for organization and API key

See merge request unboundsoftware/schemas!634
2025-11-21 13:12:02 +01:00
argoyle 862060875b test: add validation and event tests for organization and API key
Adds unit tests for the `AddOrganization` and `AddAPIKey` commands. 
These tests validate various scenarios, including success cases, 
handling of already existing organizations or keys, and ensuring 
required fields are checked. The
changes enhance test coverage and ensure robustness of the command 
logic.
2025-11-21 12:49:23 +01:00
44 changed files with 3537 additions and 1211 deletions
+1 -2
View File
@@ -1,6 +1,5 @@
.gitignore .gitignore
/.gitlab /.gitea
.gitlab-ci.yml
.graphqlconfig .graphqlconfig
/exported /exported
/k8s /k8s
+94
View File
@@ -0,0 +1,94 @@
name: schemas
on:
push:
branches: [main]
tags:
- 'v*'
pull_request:
branches: [main]
workflow_dispatch:
inputs:
deploy_prod:
description: 'Deploy to production'
required: false
default: 'false'
type: boolean
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: 'stable'
- name: Generate and format check
run: |
go install mvdan.cc/gofumpt@latest
go install golang.org/x/tools/cmd/goimports@latest
go generate ./...
git diff --stat --exit-code
- name: Run tests
run: go test -race -coverprofile=coverage.txt ./...
vulnerabilities:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: 'stable'
- name: Check vulnerabilities
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
check-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-node@v6
with:
node-version: '24'
- uses: actions/setup-go@v6
with:
go-version: 'stable'
- name: Check goreleaser config
uses: goreleaser/goreleaser-action@v7
with:
version: '~> v2'
args: check
- name: Test release build
uses: goreleaser/goreleaser-action@v7
with:
version: '~> v2'
args: release --snapshot --clean
build:
needs: [check, vulnerabilities, check-release]
runs-on: ubuntu-latest
env:
BUILDTOOLS_CONTENT: ${{ secrets.BUILDTOOLS_CONTENT }}
GITEA_REPOSITORY: ${{ gitea.repository }}
steps:
- uses: actions/checkout@v6
- uses: buildtool/setup-buildtools-action@v1
- name: Build and push
run: unset GITEA_TOKEN && build && push
deploy-prod:
needs: build
if: gitea.ref == 'refs/heads/main'
runs-on: ubuntu-latest
env:
BUILDTOOLS_CONTENT: ${{ secrets.BUILDTOOLS_CONTENT }}
GITEA_REPOSITORY: ${{ gitea.repository }}
environment: prod
steps:
- uses: actions/checkout@v6
- uses: buildtool/setup-buildtools-action@v1
- name: Deploy to production
run: deploy prod
+33
View File
@@ -0,0 +1,33 @@
name: Goreleaser
on:
push:
tags:
- 'v*'
env:
RELEASE_TOKEN_FILE: /runner-secrets/release-token
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-node@v6
with:
node-version: '24'
- uses: actions/setup-go@v6
with:
go-version: 'stable'
- name: Install goreleaser
uses: goreleaser/goreleaser-action@v7
with:
version: 'v2.13.3'
install-only: true
- name: Release
run: |
GITEA_TOKEN=$(cat "${RELEASE_TOKEN_FILE}")
export GITEA_TOKEN
goreleaser release --clean
+25
View File
@@ -0,0 +1,25 @@
name: pre-commit
permissions: read-all
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
pre-commit:
runs-on: ubuntu-latest
env:
SKIP: no-commit-to-branch
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: 'stable'
- uses: actions/setup-python@v6
with:
python-version: '3.14'
- name: Install goimports
run: go install golang.org/x/tools/cmd/goimports@latest
- uses: pre-commit/action@v3.0.1
+11
View File
@@ -0,0 +1,11 @@
name: Release
on:
push:
branches: [main]
jobs:
release:
uses: unboundsoftware/shared-workflows/.gitea/workflows/Release.yml@main
with:
tag_only: true
+1
View File
@@ -3,6 +3,7 @@
.testCoverage.txt .testCoverage.txt
.testCoverage.txt.tmp .testCoverage.txt.tmp
coverage.html coverage.html
coverage.out
/exported /exported
/release /release
/schemactl /schemactl
-89
View File
@@ -1,89 +0,0 @@
include:
- template: 'Workflows/MergeRequest-Pipelines.gitlab-ci.yml'
- project: unboundsoftware/ci-templates
file: Defaults.gitlab-ci.yml
- project: unboundsoftware/ci-templates
file: Release.gitlab-ci.yml
- project: unboundsoftware/ci-templates
file: Pre-Commit-Go.gitlab-ci.yml
stages:
- build
- test
- deploy-prod
- release
variables:
UNBOUND_RELEASE_TAG_ONLY: true
.buildtools:
image: buildtool/build-tools:${BUILDTOOLS_VERSION}
check:
stage: .pre
image: amd64/golang:1.25.4@sha256:efe81fa41fdf81fb873ab7cd931b9bb29bd10aced6c252cbd91739c34e654f01
script:
- go install mvdan.cc/gofumpt@latest
- go install golang.org/x/tools/cmd/goimports@latest
- go generate ./...
- git diff --stat --exit-code
build:
extends: .buildtools
stage: build
script:
- build
- curl -Os https://uploader.codecov.io/latest/linux/codecov
- chmod +x codecov
- ./codecov -t ${CODECOV_TOKEN} -R $CI_PROJECT_DIR -C $CI_COMMIT_SHA -r $CI_PROJECT_PATH
- push
vulnerabilities:
stage: build
image: amd64/golang:1.25.4@sha256:efe81fa41fdf81fb873ab7cd931b9bb29bd10aced6c252cbd91739c34e654f01
script:
- go install golang.org/x/vuln/cmd/govulncheck@latest
- govulncheck ./...
deploy-prod:
extends: .buildtools
stage: deploy-prod
before_script:
- echo Deploy to prod
script:
- deploy prod
rules:
- if: $CI_COMMIT_BRANCH == "main"
environment:
name: prod
resource_group: prod
check_release:
stage: test
image:
name: goreleaser/goreleaser:v2.12.7@sha256:a2a47c0dda85f8d40eaaa5b9765bf76c69addb6060666f8a51441410d9b008e9
entrypoint: [ '' ]
variables:
GOTOOLCHAIN: auto
script: |
goreleaser check
goreleaser release --snapshot --clean
release:
stage: release
needs:
- unbound_release_prepare_release
image:
name: goreleaser/goreleaser:v2.12.7@sha256:a2a47c0dda85f8d40eaaa5b9765bf76c69addb6060666f8a51441410d9b008e9
entrypoint: [ '' ]
variables:
# Disable shallow cloning so that goreleaser can diff between tags to
# generate a changelog.
GIT_DEPTH: 0
GITLAB_TOKEN: $GITLAB_CI_TOKEN
GOTOOLCHAIN: auto
# Only run this release job for tags, not every commit (for example).
rules:
- if: $CI_COMMIT_TAG
script: |
goreleaser release --clean --release-notes=CHANGES.md
+4 -5
View File
@@ -1,6 +1,10 @@
project_name: unbound-schemas project_name: unbound-schemas
version: 2 version: 2
gitea_urls:
api: http://gitea-http.gitea.svc.cluster.local:3000/api/v1
download: https://gitea.unbound.se
env: env:
- CGO_ENABLED=0 - CGO_ENABLED=0
@@ -27,11 +31,6 @@ homebrew_casks:
name: "Joakim Olsson" name: "Joakim Olsson"
email: joakim@unbound.se email: joakim@unbound.se
homepage: "https://schemas.unbound.se/" homepage: "https://schemas.unbound.se/"
hooks:
post:
install: |
# replace foo with the actual binary name
system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/schemactl"]
archives: archives:
- id: unbound-schemas - id: unbound-schemas
+4 -11
View File
@@ -10,15 +10,8 @@ repos:
args: args:
- --allow-multiple-documents - --allow-multiple-documents
- id: check-added-large-files - id: check-added-large-files
- repo: https://gitlab.com/devopshq/gitlab-ci-linter
rev: v1.0.6
hooks:
- id: gitlab-ci-linter
args:
- --project
- unboundsoftware/schemas
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
rev: v9.23.0 rev: v9.24.0
hooks: hooks:
- id: commitlint - id: commitlint
stages: [ commit-msg ] stages: [ commit-msg ]
@@ -30,18 +23,18 @@ repos:
- id: go-imports - id: go-imports
args: args:
- -local - -local
- gitlab.com/unboundsoftware/schemas - git.unbound.se/unboundsoftware/schemas
- repo: https://github.com/lietu/go-pre-commit - repo: https://github.com/lietu/go-pre-commit
rev: v1.0.0 rev: v1.0.0
hooks: hooks:
- id: go-test - id: go-test
- id: gofumpt - id: gofumpt
- repo: https://github.com/golangci/golangci-lint - repo: https://github.com/golangci/golangci-lint
rev: v2.6.2 rev: v2.11.4
hooks: hooks:
- id: golangci-lint-full - id: golangci-lint-full
- repo: https://github.com/gitleaks/gitleaks - repo: https://github.com/gitleaks/gitleaks
rev: v8.29.1 rev: v8.30.1
hooks: hooks:
- id: gitleaks - id: gitleaks
exclude: '^ctl/generated.go|graph/generated/.*$|^graph/model/models_gen.go|^tools/.*$$' exclude: '^ctl/generated.go|graph/generated/.*$|^graph/model/models_gen.go|^tools/.*$$'
+3 -1
View File
@@ -1 +1,3 @@
{"version":"v0.8.0"} {
"version": "v0.9.6"
}
+169
View File
@@ -2,6 +2,175 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [0.9.6] - 2026-03-29
### 🐛 Bug Fixes
- *(graph)* Stabilize debouncer tests with synctest fake clock
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.257 (#743)
- *(deps)* Update golang.org/x/net to v0.51.0
- *(deps)* Update module go.opentelemetry.io/contrib/bridges/otelslog to v0.16.0 (#745)
- *(deps)* Update opentelemetry-go monorepo (#744)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.260 (#750)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.261 (#751)
- *(deps)* Update opentelemetry-go monorepo (#755)
- *(deps)* Update module go.opentelemetry.io/contrib/bridges/otelslog to v0.17.0 (#756)
- *(deps)* Update module golang.org/x/sync to v0.20.0 (#760)
- *(deps)* Update module github.com/99designs/gqlgen to v0.17.88 (#762)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.262 (#765)
- *(deps)* Update module golang.org/x/crypto to v0.49.0 (#767)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.263 (#771)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.264 (#773)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.265 (#776)
- *(deps)* Update module gitlab.com/unboundsoftware/eventsourced/pg to v1.18.5 (#778)
- *(deps)* Update module github.com/99designs/gqlgen to v0.17.89 (#782)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.266 (#784)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.267 (#786)
- Cap Node.js heap to prevent OOM during wgc composition (#788)
### ⚙️ Miscellaneous Tasks
- *(deps)* Update golang docker tag to v1.26.1 (#752)
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.11.1
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.11.2 (#758)
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.11.3 (#763)
- *(deps)* Update pre-commit hook gitleaks/gitleaks to v8.30.1 (#769)
- *(deps)* Update golang:1.26.1 docker digest to 984bf90 (#774)
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.11.4 (#780)
- *(deps)* Update node.js to v24.14.1 (#783)
## [0.9.5] - 2026-02-25
### 🐛 Bug Fixes
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.256
### ⚙️ Miscellaneous Tasks
- *(deps)* Update golang:1.26.0 docker digest to 9835fb4 (#736)
- *(deps)* Update node.js to v24.14.0 (#735)
- *(deps)* Update node.js to 7fddd9d (#739)
## [0.9.4] - 2026-02-23
### 🐛 Bug Fixes
- *(ci)* Pin goreleaser to v2.13.3 for Gitea SDK compatibility
## [0.9.3] - 2026-02-23
### 🐛 Bug Fixes
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.249 (#702)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.250 (#704)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.251
- *(deps)* Update module gitlab.com/unboundsoftware/eventsourced/pg to v1.18.3
- *(deps)* Update module github.com/vektah/gqlparser/v2 to v2.5.32
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.252 (#714)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.253 (#716)
- *(deps)* Update module github.com/99designs/gqlgen to v0.17.87
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.254 (#718)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.255
- *(deps)* Update module github.com/pressly/goose/v3 to v3.27.0
- *(deps)* Update module gitlab.com/unboundsoftware/eventsourced/pg to v1.18.4 (#727)
- Prevent OOM on rapid schema publishing
### ⚙️ Miscellaneous Tasks
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.10.1 (#706)
- *(deps)* Update goreleaser/goreleaser-action action to v7
- *(deps)* Update actions/setup-node action to v6
## [0.9.2] - 2026-02-13
### 🐛 Bug Fixes
- *(deps)* Update module github.com/auth0/go-jwt-middleware/v2 to v3
- Migrate to go-jwt-middleware v3 API
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.243 (#680)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.244 (#681)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.245 (#682)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.246 (#683)
- *(deps)* Update module gitlab.com/unboundsoftware/eventsourced/pg to v1.18.0 (#685)
- *(deps)* Update module gitlab.com/unboundsoftware/eventsourced/pg to v1.18.1 (#686)
- *(deps)* Update opentelemetry-go monorepo (#687)
- *(deps)* Update module go.opentelemetry.io/contrib/bridges/otelslog to v0.15.0 (#688)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.247 (#691)
- *(deps)* Update module github.com/alecthomas/kong to v1.14.0 (#692)
- *(deps)* Update eventsourced (#693)
- *(deps)* Update module golang.org/x/crypto to v0.48.0 (#694)
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.248 (#698)
### ⚙️ Miscellaneous Tasks
- *(deps)* Update node.js to cd6fb7e (#684)
- *(deps)* Update golang:1.25.6 docker digest to ceda080 (#689)
- *(deps)* Update golang docker tag to v1.25.7
- *(deps)* Update golang:1.25.7 docker digest to d2819ff (#695)
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.9.0 (#697)
- *(deps)* Update golang docker tag to v1.26.0 (#696)
- *(deps)* Update node.js to v24.13.1 (#699)
## [0.9.1] - 2026-01-18
### 🐛 Bug Fixes
- *(ci)* Run build job on tags for Docker images
## [0.9.0] - 2026-01-18
### 🚀 Features
- Add commands for managing organizations and users
- Migrate from GitLab CI to Gitea Actions
### 🐛 Bug Fixes
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.239
- *(k8s)* Update ingress class configuration for schema
- *(deps)* Update module github.com/99designs/gqlgen to v0.17.84
- *(docker)* Update Node.js version to 24.11.1-alpine
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.240
- *(deps)* Update opentelemetry-go monorepo
- *(deps)* Update module golang.org/x/crypto to v0.46.0
- *(deps)* Update module go.opentelemetry.io/contrib/bridges/otelslog to v0.14.0
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.241
- *(deps)* Update module github.com/99designs/gqlgen to v0.17.85
- *(deps)* Update module github.com/wundergraph/graphql-go-tools/v2 to v2.0.0-rc.242
- *(deps)* Update module golang.org/x/crypto to v0.47.0
- *(deps)* Update module github.com/99designs/gqlgen to v0.17.86
### 🚜 Refactor
- *(cache)* Optimize test setup and reduce iterations
### 🧪 Testing
- Add validation and event tests for organization and API key
- *(cache)* Update tests to use legacy hash for speed
### ⚙️ Miscellaneous Tasks
- *(deps)* Update pre-commit hook gitleaks/gitleaks to v8.30.0
- *(deps)* Update goreleaser/goreleaser docker tag to v2.13.0
- *(deps)* Update golang docker tag to v1.25.5
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.7.0
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.7.1
- *(deps)* Update node.js to 682368d
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.7.2
- *(deps)* Update goreleaser/goreleaser docker tag to v2.13.1
- *(deps)* Update golang:1.25.5 docker digest to 0c27bcf
- *(deps)* Update node.js to v24.12.0
- *(deps)* Update node.js to c921b97
- *(deps)* Update goreleaser/goreleaser docker tag to v2.13.2
- *(deps)* Update golang:1.25.5 docker digest to ad03ba9
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.8.0
- *(deps)* Update goreleaser/goreleaser docker tag to v2.13.3
- *(deps)* Update golang:1.25.5 docker digest to 3a01526
- *(deps)* Update pre-commit hook alessandrojcm/commitlint-pre-commit-hook to v9.24.0
- *(deps)* Update node.js to v24.13.0
- *(deps)* Update golang docker tag to v1.25.6
## [0.8.0] - 2025-11-21 ## [0.8.0] - 2025-11-21
### 🚀 Features ### 🚀 Features
+5 -2
View File
@@ -1,4 +1,4 @@
FROM amd64/golang:1.25.4@sha256:efe81fa41fdf81fb873ab7cd931b9bb29bd10aced6c252cbd91739c34e654f01 as modules FROM amd64/golang:1.26.1@sha256:5ba1126a6dbb65aa517ff5eaecab6abc29480671e5c0ca176192de8da9dba4b8 as modules
WORKDIR /build WORKDIR /build
ADD go.* /build ADD go.* /build
RUN go mod download RUN go mod download
@@ -24,12 +24,15 @@ RUN GOOS=linux GOARCH=amd64 go build \
FROM scratch as export FROM scratch as export
COPY --from=build /build/coverage.txt / COPY --from=build /build/coverage.txt /
FROM node:24-alpine@sha256:2867d550cf9d8bb50059a0fff528741f11a84d985c732e60e19e8e75c7239c43 FROM node:24.14.1-alpine@sha256:01743339035a5c3c11a373cd7c83aeab6ed1457b55da6a69e014a95ac4e4700b
ENV TZ Europe/Stockholm ENV TZ Europe/Stockholm
# Install wgc CLI globally for Cosmo Router composition # Install wgc CLI globally for Cosmo Router composition
RUN npm install -g wgc@latest RUN npm install -g wgc@latest
# Cap Node.js heap for runtime wgc invocations to prevent OOM
ENV NODE_OPTIONS="--max-old-space-size=64"
# Copy timezone data and certificates # Copy timezone data and certificates
COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
+80 -2
View File
@@ -9,8 +9,8 @@ import (
"github.com/sparetimecoders/goamqp" "github.com/sparetimecoders/goamqp"
"gitlab.com/unboundsoftware/eventsourced/eventsourced" "gitlab.com/unboundsoftware/eventsourced/eventsourced"
"gitlab.com/unboundsoftware/schemas/domain" "gitea.unbound.se/unboundsoftware/schemas/domain"
"gitlab.com/unboundsoftware/schemas/hash" "gitea.unbound.se/unboundsoftware/schemas/hash"
) )
type Cache struct { type Cache struct {
@@ -53,6 +53,17 @@ func (c *Cache) OrganizationsByUser(sub string) []domain.Organization {
return orgs return orgs
} }
func (c *Cache) AllOrganizations() []domain.Organization {
c.mu.RLock()
defer c.mu.RUnlock()
orgs := make([]domain.Organization, 0, len(c.organizations))
for _, org := range c.organizations {
orgs = append(orgs, org)
}
return orgs
}
func (c *Cache) ApiKeyByKey(key string) *domain.APIKey { func (c *Cache) ApiKeyByKey(key string) *domain.APIKey {
c.mu.RLock() c.mu.RLock()
defer c.mu.RUnlock() defer c.mu.RUnlock()
@@ -100,6 +111,16 @@ func (c *Cache) Update(msg any, _ goamqp.Headers) (any, error) {
c.organizations[m.ID.String()] = o c.organizations[m.ID.String()] = o
c.addUser(m.Initiator, o) c.addUser(m.Initiator, o)
c.logger.With("org_id", m.ID.String(), "event", "OrganizationAdded").Debug("cache updated") c.logger.With("org_id", m.ID.String(), "event", "OrganizationAdded").Debug("cache updated")
case *domain.UserAddedToOrganization:
org, exists := c.organizations[m.ID.String()]
if exists {
m.UpdateOrganization(&org)
c.organizations[m.ID.String()] = org
c.addUser(m.UserId, org)
c.logger.With("org_id", m.ID.String(), "user_id", m.UserId, "event", "UserAddedToOrganization").Debug("cache updated")
} else {
c.logger.With("org_id", m.ID.String(), "event", "UserAddedToOrganization").Warn("organization not found in cache")
}
case *domain.APIKeyAdded: case *domain.APIKeyAdded:
key := domain.APIKey{ key := domain.APIKey{
Name: m.Name, Name: m.Name,
@@ -117,6 +138,63 @@ func (c *Cache) Update(msg any, _ goamqp.Headers) (any, error) {
org.APIKeys = append(org.APIKeys, key) org.APIKeys = append(org.APIKeys, key)
c.organizations[m.OrganizationId] = org c.organizations[m.OrganizationId] = org
c.logger.With("org_id", m.OrganizationId, "key_name", m.Name, "event", "APIKeyAdded").Debug("cache updated") c.logger.With("org_id", m.OrganizationId, "key_name", m.Name, "event", "APIKeyAdded").Debug("cache updated")
case *domain.APIKeyRemoved:
orgId := m.ID.String()
org, exists := c.organizations[orgId]
if exists {
// Remove from organization's API keys list
for i, key := range org.APIKeys {
if key.Name == m.KeyName {
org.APIKeys = append(org.APIKeys[:i], org.APIKeys[i+1:]...)
break
}
}
c.organizations[orgId] = org
// Remove from apiKeys map
delete(c.apiKeys, apiKeyId(orgId, m.KeyName))
c.logger.With("org_id", orgId, "key_name", m.KeyName, "event", "APIKeyRemoved").Debug("cache updated")
} else {
c.logger.With("org_id", orgId, "event", "APIKeyRemoved").Warn("organization not found in cache")
}
case *domain.OrganizationRemoved:
orgId := m.ID.String()
org, exists := c.organizations[orgId]
if exists {
// Remove all API keys for this organization
for _, key := range org.APIKeys {
delete(c.apiKeys, apiKeyId(orgId, key.Name))
}
// Remove organization from all users
for userId, userOrgs := range c.users {
for i, userOrgId := range userOrgs {
if userOrgId == orgId {
c.users[userId] = append(userOrgs[:i], userOrgs[i+1:]...)
break
}
}
// If user has no more organizations, remove from map
if len(c.users[userId]) == 0 {
delete(c.users, userId)
}
}
// Remove services for this organization
if refs, exists := c.services[orgId]; exists {
for ref := range refs {
// Remove all subgraphs for this org/ref combination
for service := range refs[ref] {
delete(c.subGraphs, subGraphKey(orgId, ref, service))
}
// Remove lastUpdate for this org/ref
delete(c.lastUpdate, refKey(orgId, ref))
}
delete(c.services, orgId)
}
// Remove organization
delete(c.organizations, orgId)
c.logger.With("org_id", orgId, "event", "OrganizationRemoved").Debug("cache updated")
} else {
c.logger.With("org_id", orgId, "event", "OrganizationRemoved").Warn("organization not found in cache")
}
case *domain.SubGraphUpdated: case *domain.SubGraphUpdated:
c.updateSubGraph(m.OrganizationId, m.Ref, m.ID.String(), m.Service, m.Time) c.updateSubGraph(m.OrganizationId, m.Ref, m.ID.String(), m.Service, m.Time)
c.logger.With("org_id", m.OrganizationId, "ref", m.Ref, "service", m.Service, "event", "SubGraphUpdated").Debug("cache updated") c.logger.With("org_id", m.OrganizationId, "ref", m.Ref, "service", m.Service, "event", "SubGraphUpdated").Debug("cache updated")
+228 -30
View File
@@ -12,8 +12,8 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gitlab.com/unboundsoftware/eventsourced/eventsourced" "gitlab.com/unboundsoftware/eventsourced/eventsourced"
"gitlab.com/unboundsoftware/schemas/domain" "gitea.unbound.se/unboundsoftware/schemas/domain"
"gitlab.com/unboundsoftware/schemas/hash" "gitea.unbound.se/unboundsoftware/schemas/hash"
) )
func TestCache_OrganizationByAPIKey(t *testing.T) { func TestCache_OrganizationByAPIKey(t *testing.T) {
@@ -320,24 +320,18 @@ func TestCache_ConcurrentReads(t *testing.T) {
logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
c := New(logger) c := New(logger)
// Setup test data // Setup test data - use legacy hash to avoid slow bcrypt
orgID := uuid.New().String() orgID := uuid.New().String()
apiKey := "test-concurrent-key" // gitleaks:allow userSub := "test-user"
hashedKey, err := hash.APIKey(apiKey)
require.NoError(t, err)
org := domain.Organization{ org := domain.Organization{
BaseAggregate: eventsourced.BaseAggregateFromString(orgID), BaseAggregate: eventsourced.BaseAggregateFromString(orgID),
Name: "Concurrent Test Org", Name: "Concurrent Test Org",
} }
c.organizations[orgID] = org c.organizations[orgID] = org
c.apiKeys[apiKeyId(orgID, "test-key")] = domain.APIKey{ c.users[userSub] = []string{orgID}
Name: "test-key",
OrganizationId: orgID,
Key: hashedKey,
}
// Run concurrent reads (reduced for race detector) // Run concurrent reads using fast OrganizationsByUser
var wg sync.WaitGroup var wg sync.WaitGroup
numGoroutines := 20 numGoroutines := 20
@@ -345,9 +339,9 @@ func TestCache_ConcurrentReads(t *testing.T) {
wg.Add(1) wg.Add(1)
go func() { go func() {
defer wg.Done() defer wg.Done()
org := c.OrganizationByAPIKey(apiKey) orgs := c.OrganizationsByUser(userSub)
assert.NotNil(t, org) assert.NotEmpty(t, orgs)
assert.Equal(t, "Concurrent Test Org", org.Name) assert.Equal(t, "Concurrent Test Org", orgs[0].Name)
}() }()
} }
@@ -387,11 +381,10 @@ func TestCache_ConcurrentReadsAndWrites(t *testing.T) {
logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
c := New(logger) c := New(logger)
// Setup initial data // Setup initial data - use legacy hash to avoid slow bcrypt in concurrent test
orgID := uuid.New().String() orgID := uuid.New().String()
apiKey := "test-rw-key" // gitleaks:allow legacyKey := "test-rw-key" // gitleaks:allow
hashedKey, err := hash.APIKey(apiKey) legacyHash := hash.String(legacyKey)
require.NoError(t, err)
org := domain.Organization{ org := domain.Organization{
BaseAggregate: eventsourced.BaseAggregateFromString(orgID), BaseAggregate: eventsourced.BaseAggregateFromString(orgID),
@@ -401,26 +394,21 @@ func TestCache_ConcurrentReadsAndWrites(t *testing.T) {
c.apiKeys[apiKeyId(orgID, "test-key")] = domain.APIKey{ c.apiKeys[apiKeyId(orgID, "test-key")] = domain.APIKey{
Name: "test-key", Name: "test-key",
OrganizationId: orgID, OrganizationId: orgID,
Key: hashedKey, Key: legacyHash,
} }
c.users["user-initial"] = []string{orgID} c.users["user-initial"] = []string{orgID}
var wg sync.WaitGroup var wg sync.WaitGroup
numReaders := 10 // Reduced for race detector numReaders := 5
numWriters := 5 // Reduced for race detector numWriters := 3
iterations := 3 // Reduced for race detector
// Concurrent readers // Concurrent readers - use OrganizationsByUser which is fast
for i := 0; i < numReaders; i++ { for i := 0; i < numReaders; i++ {
wg.Add(1) wg.Add(1)
go func() { go func() {
defer wg.Done() defer wg.Done()
for j := 0; j < iterations; j++ { orgs := c.OrganizationsByUser("user-initial")
org := c.OrganizationByAPIKey(apiKey) assert.NotEmpty(t, orgs)
assert.NotNil(t, org)
orgs := c.OrganizationsByUser("user-initial")
assert.NotEmpty(t, orgs)
}
}() }()
} }
@@ -445,3 +433,213 @@ func TestCache_ConcurrentReadsAndWrites(t *testing.T) {
// Verify cache is in consistent state // Verify cache is in consistent state
assert.GreaterOrEqual(t, len(c.organizations), numWriters) assert.GreaterOrEqual(t, len(c.organizations), numWriters)
} }
func TestCache_Update_APIKeyRemoved(t *testing.T) {
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
c := New(logger)
orgID := uuid.New().String()
keyName := "test-key"
hashedKey := "hashed-key-value"
// Add organization with API key
org := domain.Organization{
BaseAggregate: eventsourced.BaseAggregateFromString(orgID),
Name: "Test Org",
APIKeys: []domain.APIKey{
{
Name: keyName,
OrganizationId: orgID,
Key: hashedKey,
Refs: []string{"main"},
Read: true,
Publish: false,
},
},
}
c.organizations[orgID] = org
c.apiKeys[apiKeyId(orgID, keyName)] = org.APIKeys[0]
// Verify key exists before removal
_, exists := c.apiKeys[apiKeyId(orgID, keyName)]
assert.True(t, exists)
// Remove the API key
event := &domain.APIKeyRemoved{
KeyName: keyName,
Initiator: "user-123",
}
event.ID = *eventsourced.IdFromString(orgID)
_, err := c.Update(event, nil)
require.NoError(t, err)
// Verify API key was removed from cache
_, exists = c.apiKeys[apiKeyId(orgID, keyName)]
assert.False(t, exists, "API key should be removed from cache")
// Verify API key was removed from organization
updatedOrg := c.organizations[orgID]
assert.Len(t, updatedOrg.APIKeys, 0, "API key should be removed from organization")
}
func TestCache_Update_APIKeyRemoved_MultipleKeys(t *testing.T) {
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
c := New(logger)
orgID := uuid.New().String()
// Add organization with multiple API keys
org := domain.Organization{
BaseAggregate: eventsourced.BaseAggregateFromString(orgID),
Name: "Test Org",
APIKeys: []domain.APIKey{
{
Name: "key1",
OrganizationId: orgID,
Key: "hash1",
},
{
Name: "key2",
OrganizationId: orgID,
Key: "hash2",
},
{
Name: "key3",
OrganizationId: orgID,
Key: "hash3",
},
},
}
c.organizations[orgID] = org
c.apiKeys[apiKeyId(orgID, "key1")] = org.APIKeys[0]
c.apiKeys[apiKeyId(orgID, "key2")] = org.APIKeys[1]
c.apiKeys[apiKeyId(orgID, "key3")] = org.APIKeys[2]
// Remove the middle key
event := &domain.APIKeyRemoved{
KeyName: "key2",
Initiator: "user-123",
}
event.ID = *eventsourced.IdFromString(orgID)
_, err := c.Update(event, nil)
require.NoError(t, err)
// Verify only key2 was removed
_, exists := c.apiKeys[apiKeyId(orgID, "key1")]
assert.True(t, exists, "key1 should still exist")
_, exists = c.apiKeys[apiKeyId(orgID, "key2")]
assert.False(t, exists, "key2 should be removed")
_, exists = c.apiKeys[apiKeyId(orgID, "key3")]
assert.True(t, exists, "key3 should still exist")
// Verify organization has 2 keys remaining
updatedOrg := c.organizations[orgID]
assert.Len(t, updatedOrg.APIKeys, 2)
assert.Equal(t, "key1", updatedOrg.APIKeys[0].Name)
assert.Equal(t, "key3", updatedOrg.APIKeys[1].Name)
}
func TestCache_Update_OrganizationRemoved(t *testing.T) {
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
c := New(logger)
orgID := uuid.New().String()
userSub := "user-123"
// Add organization with API keys, users, and subgraphs
org := domain.Organization{
BaseAggregate: eventsourced.BaseAggregateFromString(orgID),
Name: "Test Org",
APIKeys: []domain.APIKey{
{
Name: "key1",
OrganizationId: orgID,
Key: "hash1",
},
},
}
c.organizations[orgID] = org
c.apiKeys[apiKeyId(orgID, "key1")] = org.APIKeys[0]
c.users[userSub] = []string{orgID}
c.services[orgID] = map[string]map[string]struct{}{
"main": {
"service1": {},
},
}
c.subGraphs[subGraphKey(orgID, "main", "service1")] = "subgraph-id"
c.lastUpdate[refKey(orgID, "main")] = "2024-01-01T12:00:00Z"
// Remove the organization
event := &domain.OrganizationRemoved{
Initiator: userSub,
}
event.ID = *eventsourced.IdFromString(orgID)
_, err := c.Update(event, nil)
require.NoError(t, err)
// Verify organization was removed
_, exists := c.organizations[orgID]
assert.False(t, exists, "Organization should be removed from cache")
// Verify API keys were removed
_, exists = c.apiKeys[apiKeyId(orgID, "key1")]
assert.False(t, exists, "API keys should be removed from cache")
// Verify user association was removed
userOrgs := c.users[userSub]
assert.NotContains(t, userOrgs, orgID, "User should not be associated with removed organization")
// Verify services were removed
_, exists = c.services[orgID]
assert.False(t, exists, "Services should be removed from cache")
// Verify subgraphs were removed
_, exists = c.subGraphs[subGraphKey(orgID, "main", "service1")]
assert.False(t, exists, "Subgraphs should be removed from cache")
// Verify lastUpdate was removed
_, exists = c.lastUpdate[refKey(orgID, "main")]
assert.False(t, exists, "LastUpdate should be removed from cache")
}
func TestCache_Update_OrganizationRemoved_MultipleUsers(t *testing.T) {
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
c := New(logger)
orgID := uuid.New().String()
user1 := "user-1"
user2 := "user-2"
otherOrgID := uuid.New().String()
// Add organization
org := domain.Organization{
BaseAggregate: eventsourced.BaseAggregateFromString(orgID),
Name: "Test Org",
}
c.organizations[orgID] = org
// Add users with multiple org associations
c.users[user1] = []string{orgID, otherOrgID}
c.users[user2] = []string{orgID}
// Remove the organization
event := &domain.OrganizationRemoved{
Initiator: user1,
}
event.ID = *eventsourced.IdFromString(orgID)
_, err := c.Update(event, nil)
require.NoError(t, err)
// Verify user1 still has otherOrgID but not removed orgID
assert.Len(t, c.users[user1], 1)
assert.Equal(t, otherOrgID, c.users[user1][0])
// Verify user2 has no organizations
assert.Len(t, c.users[user2], 0)
}
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"github.com/alecthomas/kong" "github.com/alecthomas/kong"
"github.com/apex/log" "github.com/apex/log"
"gitlab.com/unboundsoftware/schemas/ctl" "gitea.unbound.se/unboundsoftware/schemas/ctl"
) )
type Context struct { type Context struct {
+25 -14
View File
@@ -26,15 +26,15 @@ import (
"gitlab.com/unboundsoftware/eventsourced/eventsourced" "gitlab.com/unboundsoftware/eventsourced/eventsourced"
"gitlab.com/unboundsoftware/eventsourced/pg" "gitlab.com/unboundsoftware/eventsourced/pg"
"gitlab.com/unboundsoftware/schemas/cache" "gitea.unbound.se/unboundsoftware/schemas/cache"
"gitlab.com/unboundsoftware/schemas/domain" "gitea.unbound.se/unboundsoftware/schemas/domain"
"gitlab.com/unboundsoftware/schemas/graph" "gitea.unbound.se/unboundsoftware/schemas/graph"
"gitlab.com/unboundsoftware/schemas/graph/generated" "gitea.unbound.se/unboundsoftware/schemas/graph/generated"
"gitlab.com/unboundsoftware/schemas/health" "gitea.unbound.se/unboundsoftware/schemas/health"
"gitlab.com/unboundsoftware/schemas/logging" "gitea.unbound.se/unboundsoftware/schemas/logging"
"gitlab.com/unboundsoftware/schemas/middleware" "gitea.unbound.se/unboundsoftware/schemas/middleware"
"gitlab.com/unboundsoftware/schemas/monitoring" "gitea.unbound.se/unboundsoftware/schemas/monitoring"
"gitlab.com/unboundsoftware/schemas/store" "gitea.unbound.se/unboundsoftware/schemas/store"
) )
type CLI struct { type CLI struct {
@@ -92,7 +92,10 @@ func start(closeEvents chan error, logger *slog.Logger, connectToAmqpFunc func(u
pg.WithEventTypes( pg.WithEventTypes(
&domain.SubGraphUpdated{}, &domain.SubGraphUpdated{},
&domain.OrganizationAdded{}, &domain.OrganizationAdded{},
&domain.UserAddedToOrganization{},
&domain.APIKeyAdded{}, &domain.APIKeyAdded{},
&domain.APIKeyRemoved{},
&domain.OrganizationRemoved{},
), ),
) )
if err != nil { if err != nil {
@@ -127,10 +130,16 @@ func start(closeEvents chan error, logger *slog.Logger, connectToAmqpFunc func(u
goamqp.EventStreamPublisher(publisher), goamqp.EventStreamPublisher(publisher),
goamqp.TransientEventStreamConsumer("SubGraph.Updated", serviceCache.Update, domain.SubGraphUpdated{}), goamqp.TransientEventStreamConsumer("SubGraph.Updated", serviceCache.Update, domain.SubGraphUpdated{}),
goamqp.TransientEventStreamConsumer("Organization.Added", serviceCache.Update, domain.OrganizationAdded{}), goamqp.TransientEventStreamConsumer("Organization.Added", serviceCache.Update, domain.OrganizationAdded{}),
goamqp.TransientEventStreamConsumer("Organization.UserAdded", serviceCache.Update, domain.UserAddedToOrganization{}),
goamqp.TransientEventStreamConsumer("Organization.APIKeyAdded", serviceCache.Update, domain.APIKeyAdded{}), goamqp.TransientEventStreamConsumer("Organization.APIKeyAdded", serviceCache.Update, domain.APIKeyAdded{}),
goamqp.TransientEventStreamConsumer("Organization.APIKeyRemoved", serviceCache.Update, domain.APIKeyRemoved{}),
goamqp.TransientEventStreamConsumer("Organization.Removed", serviceCache.Update, domain.OrganizationRemoved{}),
goamqp.WithTypeMapping("SubGraph.Updated", domain.SubGraphUpdated{}), goamqp.WithTypeMapping("SubGraph.Updated", domain.SubGraphUpdated{}),
goamqp.WithTypeMapping("Organization.Added", domain.OrganizationAdded{}), goamqp.WithTypeMapping("Organization.Added", domain.OrganizationAdded{}),
goamqp.WithTypeMapping("Organization.UserAdded", domain.UserAddedToOrganization{}),
goamqp.WithTypeMapping("Organization.APIKeyAdded", domain.APIKeyAdded{}), goamqp.WithTypeMapping("Organization.APIKeyAdded", domain.APIKeyAdded{}),
goamqp.WithTypeMapping("Organization.APIKeyRemoved", domain.APIKeyRemoved{}),
goamqp.WithTypeMapping("Organization.Removed", domain.OrganizationRemoved{}),
} }
if err := conn.Start(rootCtx, setups...); err != nil { if err := conn.Start(rootCtx, setups...); err != nil {
return fmt.Errorf("failed to setup AMQP: %v", err) return fmt.Errorf("failed to setup AMQP: %v", err)
@@ -192,11 +201,13 @@ func start(closeEvents chan error, logger *slog.Logger, connectToAmqpFunc func(u
defer rootCancel() defer rootCancel()
resolver := &graph.Resolver{ resolver := &graph.Resolver{
EventStore: eventStore, EventStore: eventStore,
Publisher: eventPublisher, Publisher: eventPublisher,
Logger: logger, Logger: logger,
Cache: serviceCache, Cache: serviceCache,
PubSub: graph.NewPubSub(), PubSub: graph.NewPubSub(),
CosmoGenerator: graph.NewCosmoGenerator(&graph.DefaultCommandExecutor{}, 60*time.Second),
Debouncer: graph.NewDebouncer(500 * time.Millisecond),
} }
config := generated.Config{ config := generated.Config{
+3 -3
View File
@@ -10,9 +10,9 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gitlab.com/unboundsoftware/eventsourced/eventsourced" "gitlab.com/unboundsoftware/eventsourced/eventsourced"
"gitlab.com/unboundsoftware/schemas/domain" "gitea.unbound.se/unboundsoftware/schemas/domain"
"gitlab.com/unboundsoftware/schemas/hash" "gitea.unbound.se/unboundsoftware/schemas/hash"
"gitlab.com/unboundsoftware/schemas/middleware" "gitea.unbound.se/unboundsoftware/schemas/middleware"
) )
// MockCache is a mock implementation for testing // MockCache is a mock implementation for testing
+6
View File
@@ -23,6 +23,8 @@ func (o *Organization) Apply(event eventsourced.Event) error {
switch e := event.(type) { switch e := event.(type) {
case *OrganizationAdded: case *OrganizationAdded:
e.UpdateOrganization(o) e.UpdateOrganization(o)
case *UserAddedToOrganization:
e.UpdateOrganization(o)
case *APIKeyAdded: case *APIKeyAdded:
o.APIKeys = append(o.APIKeys, APIKey{ o.APIKeys = append(o.APIKeys, APIKey{
Name: e.Name, Name: e.Name,
@@ -36,6 +38,10 @@ func (o *Organization) Apply(event eventsourced.Event) error {
}) })
o.ChangedBy = e.Initiator o.ChangedBy = e.Initiator
o.ChangedAt = e.When() o.ChangedAt = e.When()
case *APIKeyRemoved:
e.UpdateOrganization(o)
case *OrganizationRemoved:
e.UpdateOrganization(o)
default: default:
return fmt.Errorf("unexpected event type: %+v", event) return fmt.Errorf("unexpected event type: %+v", event)
} }
+83 -1
View File
@@ -7,7 +7,7 @@ import (
"gitlab.com/unboundsoftware/eventsourced/eventsourced" "gitlab.com/unboundsoftware/eventsourced/eventsourced"
"gitlab.com/unboundsoftware/schemas/hash" "gitea.unbound.se/unboundsoftware/schemas/hash"
) )
type AddOrganization struct { type AddOrganization struct {
@@ -34,6 +34,37 @@ func (a AddOrganization) Event(context.Context) eventsourced.Event {
var _ eventsourced.Command = AddOrganization{} var _ eventsourced.Command = AddOrganization{}
type AddUserToOrganization struct {
UserId string
Initiator string
}
func (a AddUserToOrganization) Validate(_ context.Context, aggregate eventsourced.Aggregate) error {
if aggregate.Identity() == nil {
return fmt.Errorf("organization does not exist")
}
if len(a.UserId) == 0 {
return fmt.Errorf("userId is required")
}
// Check if user is already in the organization
org := aggregate.(*Organization)
for _, user := range org.Users {
if user == a.UserId {
return fmt.Errorf("user is already a member of this organization")
}
}
return nil
}
func (a AddUserToOrganization) Event(context.Context) eventsourced.Event {
return &UserAddedToOrganization{
UserId: a.UserId,
Initiator: a.Initiator,
}
}
var _ eventsourced.Command = AddUserToOrganization{}
type AddAPIKey struct { type AddAPIKey struct {
Name string Name string
Key string Key string
@@ -79,6 +110,57 @@ func (a AddAPIKey) Event(context.Context) eventsourced.Event {
var _ eventsourced.Command = AddAPIKey{} var _ eventsourced.Command = AddAPIKey{}
type RemoveAPIKey struct {
KeyName string
Initiator string
}
func (r RemoveAPIKey) Validate(_ context.Context, aggregate eventsourced.Aggregate) error {
if aggregate.Identity() == nil {
return fmt.Errorf("organization does not exist")
}
org := aggregate.(*Organization)
found := false
for _, k := range org.APIKeys {
if k.Name == r.KeyName {
found = true
break
}
}
if !found {
return fmt.Errorf("API key '%s' not found", r.KeyName)
}
return nil
}
func (r RemoveAPIKey) Event(context.Context) eventsourced.Event {
return &APIKeyRemoved{
KeyName: r.KeyName,
Initiator: r.Initiator,
}
}
var _ eventsourced.Command = RemoveAPIKey{}
type RemoveOrganization struct {
Initiator string
}
func (r RemoveOrganization) Validate(_ context.Context, aggregate eventsourced.Aggregate) error {
if aggregate.Identity() == nil {
return fmt.Errorf("organization does not exist")
}
return nil
}
func (r RemoveOrganization) Event(context.Context) eventsourced.Event {
return &OrganizationRemoved{
Initiator: r.Initiator,
}
}
var _ eventsourced.Command = RemoveOrganization{}
type UpdateSubGraph struct { type UpdateSubGraph struct {
OrganizationId string OrganizationId string
Ref string Ref string
+502 -1
View File
@@ -7,10 +7,68 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
"gitlab.com/unboundsoftware/schemas/hash" "gitea.unbound.se/unboundsoftware/schemas/hash"
) )
// AddOrganization tests
func TestAddOrganization_Validate_Success(t *testing.T) {
cmd := AddOrganization{
Name: "Test Org",
Initiator: "user@example.com",
}
org := &Organization{} // New organization with no identity
err := cmd.Validate(context.Background(), org)
assert.NoError(t, err)
}
func TestAddOrganization_Validate_AlreadyExists(t *testing.T) {
cmd := AddOrganization{
Name: "Test Org",
Initiator: "user@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("existing-org-id"),
}
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "already exists")
}
func TestAddOrganization_Validate_EmptyName(t *testing.T) {
cmd := AddOrganization{
Name: "",
Initiator: "user@example.com",
}
org := &Organization{}
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "name is required")
}
func TestAddOrganization_Event(t *testing.T) {
cmd := AddOrganization{
Name: "Test Org",
Initiator: "user@example.com",
}
event := cmd.Event(context.Background())
require.NotNil(t, event)
orgEvent, ok := event.(*OrganizationAdded)
require.True(t, ok)
assert.Equal(t, "Test Org", orgEvent.Name)
assert.Equal(t, "user@example.com", orgEvent.Initiator)
}
// AddAPIKey tests
func TestAddAPIKey_Event(t *testing.T) { func TestAddAPIKey_Event(t *testing.T) {
type fields struct { type fields struct {
Name string Name string
@@ -74,3 +132,446 @@ func TestAddAPIKey_Event(t *testing.T) {
}) })
} }
} }
func TestAddAPIKey_Validate_Success(t *testing.T) {
cmd := AddAPIKey{
Name: "production-key",
Key: "us_ak_1234567890123456",
Refs: []string{"main"},
Read: true,
Publish: false,
Initiator: "user@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
APIKeys: []APIKey{},
}
err := cmd.Validate(context.Background(), org)
assert.NoError(t, err)
}
func TestAddAPIKey_Validate_OrganizationNotExists(t *testing.T) {
cmd := AddAPIKey{
Name: "production-key",
Key: "us_ak_1234567890123456",
Refs: []string{"main"},
Read: true,
Publish: false,
Initiator: "user@example.com",
}
org := &Organization{} // No identity means it doesn't exist
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "does not exist")
}
func TestAddAPIKey_Validate_DuplicateKeyName(t *testing.T) {
cmd := AddAPIKey{
Name: "existing-key",
Key: "us_ak_1234567890123456",
Refs: []string{"main"},
Read: true,
Publish: false,
Initiator: "user@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
APIKeys: []APIKey{
{
Name: "existing-key",
Key: "hashed-key",
},
},
}
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "already exist")
assert.Contains(t, err.Error(), "existing-key")
}
// UpdateSubGraph tests
func TestUpdateSubGraph_Validate_Success(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: &url,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
}
err := cmd.Validate(context.Background(), subGraph)
assert.NoError(t, err)
}
func TestUpdateSubGraph_Validate_MissingRef(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "",
Service: "users",
Url: &url,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "ref is missing")
}
func TestUpdateSubGraph_Validate_RefWhitespaceOnly(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: " ",
Service: "users",
Url: &url,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "ref is missing")
}
func TestUpdateSubGraph_Validate_MissingService(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "",
Url: &url,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "service is missing")
}
func TestUpdateSubGraph_Validate_ServiceWhitespaceOnly(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: " ",
Url: &url,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "service is missing")
}
func TestUpdateSubGraph_Validate_MissingSDL(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: &url,
Sdl: "",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "SDL is missing")
}
func TestUpdateSubGraph_Validate_SDLWhitespaceOnly(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: &url,
Sdl: " \n\t ",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "SDL is missing")
}
func TestUpdateSubGraph_Validate_MissingURL_NoExistingURL(t *testing.T) {
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: nil,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
Url: nil, // No existing URL
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "url is missing")
}
func TestUpdateSubGraph_Validate_MissingURL_HasExistingURL(t *testing.T) {
existingURL := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: nil,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
Url: &existingURL, // Has existing URL, so nil is OK
}
err := cmd.Validate(context.Background(), subGraph)
assert.NoError(t, err)
}
func TestUpdateSubGraph_Validate_EmptyURL_NoExistingURL(t *testing.T) {
emptyURL := ""
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: &emptyURL,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
Url: nil,
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "url is missing")
}
func TestUpdateSubGraph_Validate_URLWhitespaceOnly_NoExistingURL(t *testing.T) {
whitespaceURL := " "
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: &whitespaceURL,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
Url: nil,
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "url is missing")
}
func TestUpdateSubGraph_Validate_WrongAggregateType(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: &url,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
// Pass wrong aggregate type
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
}
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "not a SubGraph")
}
func TestUpdateSubGraph_Event(t *testing.T) {
url := "http://example.com/graphql"
wsURL := "ws://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: &url,
WSUrl: &wsURL,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
event := cmd.Event(context.Background())
require.NotNil(t, event)
subGraphEvent, ok := event.(*SubGraphUpdated)
require.True(t, ok)
assert.Equal(t, "org-123", subGraphEvent.OrganizationId)
assert.Equal(t, "main", subGraphEvent.Ref)
assert.Equal(t, "users", subGraphEvent.Service)
assert.Equal(t, url, *subGraphEvent.Url)
assert.Equal(t, wsURL, *subGraphEvent.WSUrl)
assert.Equal(t, "type Query { hello: String }", subGraphEvent.Sdl)
assert.Equal(t, "user@example.com", subGraphEvent.Initiator)
}
// RemoveAPIKey tests
func TestRemoveAPIKey_Validate_Success(t *testing.T) {
cmd := RemoveAPIKey{
KeyName: "production-key",
Initiator: "user@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
APIKeys: []APIKey{
{
Name: "production-key",
Key: "hashed-key",
},
},
}
err := cmd.Validate(context.Background(), org)
assert.NoError(t, err)
}
func TestRemoveAPIKey_Validate_OrganizationNotExists(t *testing.T) {
cmd := RemoveAPIKey{
KeyName: "production-key",
Initiator: "user@example.com",
}
org := &Organization{} // No identity means it doesn't exist
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "does not exist")
}
func TestRemoveAPIKey_Validate_KeyNotFound(t *testing.T) {
cmd := RemoveAPIKey{
KeyName: "non-existent-key",
Initiator: "user@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
APIKeys: []APIKey{
{
Name: "production-key",
Key: "hashed-key",
},
},
}
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "not found")
assert.Contains(t, err.Error(), "non-existent-key")
}
func TestRemoveAPIKey_Event(t *testing.T) {
cmd := RemoveAPIKey{
KeyName: "production-key",
Initiator: "user@example.com",
}
event := cmd.Event(context.Background())
require.NotNil(t, event)
keyEvent, ok := event.(*APIKeyRemoved)
require.True(t, ok)
assert.Equal(t, "production-key", keyEvent.KeyName)
assert.Equal(t, "user@example.com", keyEvent.Initiator)
}
// RemoveOrganization tests
func TestRemoveOrganization_Validate_Success(t *testing.T) {
cmd := RemoveOrganization{
Initiator: "user@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
Name: "Test Org",
}
err := cmd.Validate(context.Background(), org)
assert.NoError(t, err)
}
func TestRemoveOrganization_Validate_OrganizationNotExists(t *testing.T) {
cmd := RemoveOrganization{
Initiator: "user@example.com",
}
org := &Organization{} // No identity means it doesn't exist
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "does not exist")
}
func TestRemoveOrganization_Event(t *testing.T) {
cmd := RemoveOrganization{
Initiator: "user@example.com",
}
event := cmd.Event(context.Background())
require.NotNil(t, event)
orgEvent, ok := event.(*OrganizationRemoved)
require.True(t, ok)
assert.Equal(t, "user@example.com", orgEvent.Initiator)
}
+48
View File
@@ -17,6 +17,24 @@ func (a *OrganizationAdded) UpdateOrganization(o *Organization) {
o.ChangedAt = a.When() o.ChangedAt = a.When()
} }
type UserAddedToOrganization struct {
eventsourced.BaseEvent
UserId string `json:"userId"`
Initiator string `json:"initiator"`
}
func (a *UserAddedToOrganization) UpdateOrganization(o *Organization) {
// Check if user is already in the organization
for _, user := range o.Users {
if user == a.UserId {
return // User already exists, no need to add
}
}
o.Users = append(o.Users, a.UserId)
o.ChangedBy = a.Initiator
o.ChangedAt = a.When()
}
type APIKeyAdded struct { type APIKeyAdded struct {
eventsourced.BaseEvent eventsourced.BaseEvent
OrganizationId string `json:"organizationId"` OrganizationId string `json:"organizationId"`
@@ -34,6 +52,36 @@ func (a *APIKeyAdded) EnrichFromAggregate(aggregate eventsourced.Aggregate) {
var _ eventsourced.EnrichableEvent = &APIKeyAdded{} var _ eventsourced.EnrichableEvent = &APIKeyAdded{}
type APIKeyRemoved struct {
eventsourced.BaseEvent
KeyName string `json:"keyName"`
Initiator string `json:"initiator"`
}
func (a *APIKeyRemoved) UpdateOrganization(o *Organization) {
// Remove the API key from the organization
for i, key := range o.APIKeys {
if key.Name == a.KeyName {
o.APIKeys = append(o.APIKeys[:i], o.APIKeys[i+1:]...)
break
}
}
o.ChangedBy = a.Initiator
o.ChangedAt = a.When()
}
type OrganizationRemoved struct {
eventsourced.BaseEvent
Initiator string `json:"initiator"`
}
func (a *OrganizationRemoved) UpdateOrganization(o *Organization) {
// Mark organization as removed by clearing critical fields
// The aggregate will still exist in the event store, but it's logically deleted
o.ChangedBy = a.Initiator
o.ChangedAt = a.When()
}
type SubGraphUpdated struct { type SubGraphUpdated struct {
eventsourced.BaseEvent eventsourced.BaseEvent
OrganizationId string `json:"organizationId"` OrganizationId string `json:"organizationId"`
+254
View File
@@ -0,0 +1,254 @@
package domain
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
)
func TestOrganizationAdded_UpdateOrganization(t *testing.T) {
event := &OrganizationAdded{
BaseEvent: eventsourced.BaseEvent{
EventTime: eventsourced.EventTime{
Time: time.Now(),
},
},
Name: "Test Organization",
Initiator: "user@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
}
event.UpdateOrganization(org)
assert.Equal(t, "Test Organization", org.Name)
assert.Equal(t, []string{"user@example.com"}, org.Users)
assert.Equal(t, "user@example.com", org.CreatedBy)
assert.Equal(t, "user@example.com", org.ChangedBy)
assert.Equal(t, event.When(), org.CreatedAt)
assert.Equal(t, event.When(), org.ChangedAt)
}
func TestUserAddedToOrganization_UpdateOrganization(t *testing.T) {
event := &UserAddedToOrganization{
BaseEvent: eventsourced.BaseEvent{
EventTime: eventsourced.EventTime{
Time: time.Now(),
},
},
UserId: "new-user@example.com",
Initiator: "admin@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
Users: []string{"existing-user@example.com"},
}
event.UpdateOrganization(org)
assert.Len(t, org.Users, 2)
assert.Contains(t, org.Users, "existing-user@example.com")
assert.Contains(t, org.Users, "new-user@example.com")
assert.Equal(t, "admin@example.com", org.ChangedBy)
assert.Equal(t, event.When(), org.ChangedAt)
}
func TestUserAddedToOrganization_UpdateOrganization_DuplicateUser(t *testing.T) {
event := &UserAddedToOrganization{
BaseEvent: eventsourced.BaseEvent{
EventTime: eventsourced.EventTime{
Time: time.Now(),
},
},
UserId: "existing-user@example.com",
Initiator: "admin@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
Users: []string{"existing-user@example.com"},
ChangedBy: "previous-admin@example.com",
}
originalChangedBy := org.ChangedBy
originalChangedAt := org.ChangedAt
event.UpdateOrganization(org)
// User should not be added twice
assert.Len(t, org.Users, 1)
assert.Equal(t, "existing-user@example.com", org.Users[0])
// ChangedBy and ChangedAt should NOT be updated when user already exists (idempotent)
assert.Equal(t, originalChangedBy, org.ChangedBy)
assert.Equal(t, originalChangedAt, org.ChangedAt)
}
func TestAPIKeyRemoved_UpdateOrganization(t *testing.T) {
event := &APIKeyRemoved{
BaseEvent: eventsourced.BaseEvent{
EventTime: eventsourced.EventTime{
Time: time.Now(),
},
},
KeyName: "production-key",
Initiator: "admin@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
APIKeys: []APIKey{
{Name: "dev-key", Key: "hashed-key-1"},
{Name: "production-key", Key: "hashed-key-2"},
{Name: "staging-key", Key: "hashed-key-3"},
},
}
event.UpdateOrganization(org)
assert.Len(t, org.APIKeys, 2)
assert.Equal(t, "dev-key", org.APIKeys[0].Name)
assert.Equal(t, "staging-key", org.APIKeys[1].Name)
assert.Equal(t, "admin@example.com", org.ChangedBy)
assert.Equal(t, event.When(), org.ChangedAt)
}
func TestAPIKeyRemoved_UpdateOrganization_KeyNotFound(t *testing.T) {
event := &APIKeyRemoved{
BaseEvent: eventsourced.BaseEvent{
EventTime: eventsourced.EventTime{
Time: time.Now(),
},
},
KeyName: "non-existent-key",
Initiator: "admin@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
APIKeys: []APIKey{
{Name: "dev-key", Key: "hashed-key-1"},
{Name: "production-key", Key: "hashed-key-2"},
},
}
event.UpdateOrganization(org)
// No keys should be removed
assert.Len(t, org.APIKeys, 2)
assert.Equal(t, "dev-key", org.APIKeys[0].Name)
assert.Equal(t, "production-key", org.APIKeys[1].Name)
// But metadata should still be updated
assert.Equal(t, "admin@example.com", org.ChangedBy)
assert.Equal(t, event.When(), org.ChangedAt)
}
func TestAPIKeyRemoved_UpdateOrganization_OnlyKey(t *testing.T) {
event := &APIKeyRemoved{
BaseEvent: eventsourced.BaseEvent{
EventTime: eventsourced.EventTime{
Time: time.Now(),
},
},
KeyName: "only-key",
Initiator: "admin@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
APIKeys: []APIKey{
{Name: "only-key", Key: "hashed-key"},
},
}
event.UpdateOrganization(org)
// All keys should be removed
assert.Len(t, org.APIKeys, 0)
assert.Equal(t, "admin@example.com", org.ChangedBy)
assert.Equal(t, event.When(), org.ChangedAt)
}
func TestOrganizationRemoved_UpdateOrganization(t *testing.T) {
event := &OrganizationRemoved{
BaseEvent: eventsourced.BaseEvent{
EventTime: eventsourced.EventTime{
Time: time.Now(),
},
},
Initiator: "admin@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
Name: "Test Organization",
Users: []string{"user1@example.com", "user2@example.com"},
APIKeys: []APIKey{
{Name: "key1", Key: "hashed-key-1"},
},
CreatedBy: "creator@example.com",
CreatedAt: time.Now().Add(-24 * time.Hour),
}
event.UpdateOrganization(org)
// Organization data remains (soft delete), but metadata is updated
assert.Equal(t, "Test Organization", org.Name)
assert.Len(t, org.Users, 2)
assert.Len(t, org.APIKeys, 1)
// Metadata should be updated to reflect removal
assert.Equal(t, "admin@example.com", org.ChangedBy)
assert.Equal(t, event.When(), org.ChangedAt)
}
func TestAPIKeyAdded_EnrichFromAggregate(t *testing.T) {
orgId := "org-123"
aggregate := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString(orgId),
}
event := &APIKeyAdded{
Name: "test-key",
Key: "hashed-key",
Refs: []string{"main"},
Read: true,
Publish: false,
Initiator: "user@example.com",
}
event.EnrichFromAggregate(aggregate)
assert.Equal(t, orgId, event.OrganizationId)
}
func TestSubGraphUpdated_Event(t *testing.T) {
// Verify SubGraphUpdated event structure
url := "http://service.example.com"
wsUrl := "ws://service.example.com"
event := &SubGraphUpdated{
OrganizationId: "org-123",
Ref: "main",
Service: "users-service",
Url: &url,
WSUrl: &wsUrl,
Sdl: "type Query { user: User }",
Initiator: "system",
}
require.NotNil(t, event)
assert.Equal(t, "org-123", event.OrganizationId)
assert.Equal(t, "main", event.Ref)
assert.Equal(t, "users-service", event.Service)
assert.Equal(t, url, *event.Url)
assert.Equal(t, wsUrl, *event.WSUrl)
assert.Equal(t, "type Query { user: User }", event.Sdl)
assert.Equal(t, "system", event.Initiator)
}
+56 -44
View File
@@ -1,38 +1,37 @@
module gitlab.com/unboundsoftware/schemas module gitea.unbound.se/unboundsoftware/schemas
go 1.25 go 1.25.0
require ( require (
github.com/99designs/gqlgen v0.17.83 github.com/99designs/gqlgen v0.17.89
github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/Khan/genqlient v0.8.1 github.com/Khan/genqlient v0.8.1
github.com/alecthomas/kong v1.13.0 github.com/alecthomas/kong v1.15.0
github.com/apex/log v1.9.0 github.com/apex/log v1.9.0
github.com/auth0/go-jwt-middleware/v2 v2.3.1 github.com/auth0/go-jwt-middleware/v3 v3.0.0
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/jmoiron/sqlx v1.4.0 github.com/jmoiron/sqlx v1.4.0
github.com/pkg/errors v0.9.1 github.com/pressly/goose/v3 v3.27.0
github.com/pressly/goose/v3 v3.26.0
github.com/rs/cors v1.11.1 github.com/rs/cors v1.11.1
github.com/sparetimecoders/goamqp v0.3.3 github.com/sparetimecoders/goamqp v0.3.3
github.com/stretchr/testify v1.11.1 github.com/stretchr/testify v1.11.1
github.com/vektah/gqlparser/v2 v2.5.31 github.com/vektah/gqlparser/v2 v2.5.32
github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.238 github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.267
gitlab.com/unboundsoftware/eventsourced/amqp v1.9.0 gitlab.com/unboundsoftware/eventsourced/amqp v1.9.1
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.19.3 gitlab.com/unboundsoftware/eventsourced/eventsourced v1.19.4
gitlab.com/unboundsoftware/eventsourced/pg v1.17.0 gitlab.com/unboundsoftware/eventsourced/pg v1.18.6
go.opentelemetry.io/contrib/bridges/otelslog v0.13.0 go.opentelemetry.io/contrib/bridges/otelslog v0.17.0
go.opentelemetry.io/otel v1.38.0 go.opentelemetry.io/otel v1.43.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.14.0 go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0
go.opentelemetry.io/otel/log v0.14.0 go.opentelemetry.io/otel/log v0.19.0
go.opentelemetry.io/otel/sdk v1.38.0 go.opentelemetry.io/otel/sdk v1.43.0
go.opentelemetry.io/otel/sdk/log v0.14.0 go.opentelemetry.io/otel/sdk/log v0.19.0
go.opentelemetry.io/otel/sdk/metric v1.38.0 go.opentelemetry.io/otel/sdk/metric v1.43.0
go.opentelemetry.io/otel/trace v1.38.0 go.opentelemetry.io/otel/trace v1.43.0
golang.org/x/crypto v0.45.0 golang.org/x/crypto v0.49.0
golang.org/x/sync v0.20.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
@@ -42,38 +41,51 @@ require (
github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/gorilla/websocket v1.5.1 // indirect github.com/gorilla/websocket v1.5.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/lib/pq v1.10.9 // indirect github.com/lestrrat-go/blackmagic v1.0.4 // indirect
github.com/lestrrat-go/dsig v1.0.0 // indirect
github.com/lestrrat-go/dsig-secp256k1 v1.0.0 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc/v3 v3.0.3 // indirect
github.com/lestrrat-go/jwx/v3 v3.0.12 // indirect
github.com/lestrrat-go/option/v2 v2.0.0 // indirect
github.com/lib/pq v1.12.1 // indirect
github.com/mfridman/interpolate v0.0.2 // indirect github.com/mfridman/interpolate v0.0.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rabbitmq/amqp091-go v1.10.0 // indirect github.com/rabbitmq/amqp091-go v1.10.0 // indirect
github.com/segmentio/asm v1.2.1 // indirect
github.com/sethvargo/go-retry v0.3.0 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect
github.com/sosodev/duration v1.3.1 // indirect github.com/sosodev/duration v1.4.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/objx v0.5.2 // indirect
github.com/tidwall/gjson v1.17.0 // indirect github.com/tidwall/gjson v1.17.0 // indirect
github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect github.com/tidwall/sjson v1.2.5 // indirect
github.com/urfave/cli/v3 v3.6.0 // indirect github.com/urfave/cli/v3 v3.7.0 // indirect
github.com/wundergraph/astjson v0.0.0-20250106123708-be463c97e083 // indirect github.com/valyala/fastjson v1.6.7 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect github.com/wundergraph/astjson v1.1.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect github.com/wundergraph/go-arena v1.1.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.29.0 // indirect golang.org/x/mod v0.33.0 // indirect
golang.org/x/net v0.47.0 // indirect golang.org/x/net v0.52.0 // indirect
golang.org/x/sync v0.18.0 // indirect golang.org/x/sys v0.42.0 // indirect
golang.org/x/sys v0.38.0 // indirect golang.org/x/text v0.35.0 // indirect
golang.org/x/text v0.31.0 // indirect golang.org/x/tools v0.42.0 // indirect
golang.org/x/tools v0.38.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/grpc v1.80.0 // indirect
google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.11 // indirect
google.golang.org/protobuf v1.36.10 // indirect
) )
+123 -98
View File
@@ -1,19 +1,20 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/99designs/gqlgen v0.17.83 h1:LZOd4Of2snK5V22/ZWfBAPa3WoAZkBO70dKXM0ODHQk= filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
github.com/99designs/gqlgen v0.17.83/go.mod h1:q6Lb64wknFqNFSbSUGzKRKupklvY/xgNr62g0GGWPB8= filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
github.com/99designs/gqlgen v0.17.89 h1:KzEcxPiMgQoMw3m/E85atUEHyZyt0PbAflMia5Kw8z8=
github.com/99designs/gqlgen v0.17.89/go.mod h1:GFqruTVGB7ZTdrf1uzOagpXbY7DrEt1pIxnTdhIbWvQ=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/Khan/genqlient v0.8.1 h1:wtOCc8N9rNynRLXN3k3CnfzheCUNKBcvXmVv5zt6WCs= github.com/Khan/genqlient v0.8.1 h1:wtOCc8N9rNynRLXN3k3CnfzheCUNKBcvXmVv5zt6WCs=
github.com/Khan/genqlient v0.8.1/go.mod h1:R2G6DzjBvCbhjsEajfRjbWdVglSH/73kSivC9TLWVjU= github.com/Khan/genqlient v0.8.1/go.mod h1:R2G6DzjBvCbhjsEajfRjbWdVglSH/73kSivC9TLWVjU=
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo= github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y= github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=
github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/kong v1.13.0 h1:5e/7XC3ugvhP1DQBmTS+WuHtCbcv44hsohMgcvVxSrA= github.com/alecthomas/kong v1.15.0 h1:BVJstKbpO73zKpmIu+m/aLRrNmWwxXPIGTNin9VmLVI=
github.com/alecthomas/kong v1.13.0/go.mod h1:wrlbXem1CWqUV5Vbmss5ISYhsVPkBb1Yo7YKJghju2I= github.com/alecthomas/kong v1.15.0/go.mod h1:wrlbXem1CWqUV5Vbmss5ISYhsVPkBb1Yo7YKJghju2I=
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
@@ -27,8 +28,8 @@ github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy
github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/auth0/go-jwt-middleware/v2 v2.3.1 h1:lbDyWE9aLydb3zrank+Gufb9qGJN9u//7EbJK07pRrw= github.com/auth0/go-jwt-middleware/v3 v3.0.0 h1:+rvUPCT+VbAuK4tpS13fWfZrMyqTwLopt3VoY0Y7kvA=
github.com/auth0/go-jwt-middleware/v2 v2.3.1/go.mod h1:mqVr0gdB5zuaFyQFWMJH/c/2hehNjbYUD4i8Dpyf+Hc= github.com/auth0/go-jwt-middleware/v3 v3.0.0/go.mod h1:iU42jqjRyeKbf9YYSnRnolr836gk6Ty/jnUNuVq2b0o=
github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
@@ -40,6 +41,8 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo=
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
@@ -55,10 +58,12 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
@@ -70,8 +75,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
@@ -92,8 +97,23 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=
github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
github.com/lestrrat-go/dsig v1.0.0 h1:OE09s2r9Z81kxzJYRn07TFM9XA4akrUdoMwr0L8xj38=
github.com/lestrrat-go/dsig v1.0.0/go.mod h1:dEgoOYYEJvW6XGbLasr8TFcAxoWrKlbQvmJgCR0qkDo=
github.com/lestrrat-go/dsig-secp256k1 v1.0.0 h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7gcrVVMFPOzY=
github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/httprc/v3 v3.0.3 h1:WjLHWkDkgWXeIUrKi/7lS/sGq2DjkSAwdTbH5RHXAKs=
github.com/lestrrat-go/httprc/v3 v3.0.3/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0=
github.com/lestrrat-go/jwx/v3 v3.0.12 h1:p25r68Y4KrbBdYjIsQweYxq794CtGCzcrc5dGzJIRjg=
github.com/lestrrat-go/jwx/v3 v3.0.12/go.mod h1:HiUSaNmMLXgZ08OmGBaPVvoZQgJVOQphSrGr5zMamS8=
github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss=
github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.12.1 h1:x1nbl/338GLqeDJ/FAiILallhAsqubLzEZu/pXtHUow=
github.com/lib/pq v1.12.1/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@@ -105,8 +125,8 @@ github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxU
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -114,8 +134,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pressly/goose/v3 v3.26.0 h1:KJakav68jdH0WDvoAcj8+n61WqOIaPGgH0bJWS6jpmM= github.com/pressly/goose/v3 v3.27.0 h1:/D30gVTuQhu0WsNZYbJi4DMOsx1lNq+6SkLe+Wp59BM=
github.com/pressly/goose/v3 v3.26.0/go.mod h1:4hC1KrritdCxtuFsqgs1R4AU5bWtTAf+cnWvfhf2DNY= github.com/pressly/goose/v3 v3.27.0/go.mod h1:3ZBeCXqzkgIRvrEMDkYh1guvtoJTU5oMMuDdkutoM78=
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw= github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
@@ -129,6 +149,8 @@ github.com/sanity-io/litter v1.5.8 h1:uM/2lKrWdGbRXDrIq08Lh9XtVYoeGtcQxk9rtQ7+rY
github.com/sanity-io/litter v1.5.8/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= github.com/sanity-io/litter v1.5.8/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/sebdah/goldie/v2 v2.7.1 h1:PkBHymaYdtvEkZV7TmyqKxdmn5/Vcj+8TpATWZjnG5E= github.com/sebdah/goldie/v2 v2.7.1 h1:PkBHymaYdtvEkZV7TmyqKxdmn5/Vcj+8TpATWZjnG5E=
github.com/sebdah/goldie/v2 v2.7.1/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= github.com/sebdah/goldie/v2 v2.7.1/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
@@ -137,8 +159,8 @@ github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLy
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= github.com/sosodev/duration v1.4.0 h1:35ed0KiVFriGHHzZZJaZLgmTEEICIyt8Sx0RQfj9IjE=
github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= github.com/sosodev/duration v1.4.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
github.com/sparetimecoders/goamqp v0.3.3 h1:z/nfTPmrjeU/rIVuNOgsVLCimp3WFoNFvS3ZzXRJ6HE= github.com/sparetimecoders/goamqp v0.3.3 h1:z/nfTPmrjeU/rIVuNOgsVLCimp3WFoNFvS3ZzXRJ6HE=
github.com/sparetimecoders/goamqp v0.3.3/go.mod h1:W9NRCpWLE+Vruv2dcRSbszNil2O826d2Nv6kAkETW5o= github.com/sparetimecoders/goamqp v0.3.3/go.mod h1:W9NRCpWLE+Vruv2dcRSbszNil2O826d2Nv6kAkETW5o=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -146,6 +168,7 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
@@ -165,100 +188,102 @@ github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao=
github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
github.com/urfave/cli/v3 v3.6.0 h1:oIdArVjkdIXHWg3iqxgmqwQGC8NM0JtdgwQAj2sRwFo= github.com/urfave/cli/v3 v3.7.0 h1:AGSnbUyjtLiM+WJUb4dzXKldl/gL+F8OwmRDtVr6g2U=
github.com/urfave/cli/v3 v3.6.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= github.com/urfave/cli/v3 v3.7.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
github.com/vektah/gqlparser/v2 v2.5.31 h1:YhWGA1mfTjID7qJhd1+Vxhpk5HTgydrGU9IgkWBTJ7k= github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM=
github.com/vektah/gqlparser/v2 v2.5.31/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts= github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
github.com/wundergraph/astjson v0.0.0-20250106123708-be463c97e083 h1:8/D7f8gKxTBjW+SZK4mhxTTBVpxcqeBgWF1Rfmltbfk= github.com/vektah/gqlparser/v2 v2.5.32 h1:k9QPJd4sEDTL+qB4ncPLflqTJ3MmjB9SrVzJrawpFSc=
github.com/wundergraph/astjson v0.0.0-20250106123708-be463c97e083/go.mod h1:eOTL6acwctsN4F3b7YE+eE2t8zcJ/doLm9sZzsxxxrE= github.com/vektah/gqlparser/v2 v2.5.32/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts=
github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.238 h1:ll0BtYVMziRa8v0T/f+DQOJ/1x3Dq5puifJNnxF0R+M= github.com/wundergraph/astjson v1.1.0 h1:xORDosrZ87zQFJwNGe/HIHXqzpdHOFmqWgykCLVL040=
github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.238/go.mod h1:ErOQH1ki2+SZB8JjpTyGVnoBpg5picIyjvuWQJP4abg= github.com/wundergraph/astjson v1.1.0/go.mod h1:h12D/dxxnedtLzsKyBLK7/Oe4TAoGpRVC9nDpDrZSWw=
gitlab.com/unboundsoftware/eventsourced/amqp v1.9.0 h1:TdBJnrnrxJrPhC4i6KTFUElZa3k/fFXiGwg0sds5aAo= github.com/wundergraph/go-arena v1.1.0 h1:9+wSRkJAkA2vbYHp6s8tEGhPViRGQNGXqPHT0QzhdIc=
gitlab.com/unboundsoftware/eventsourced/amqp v1.9.0/go.mod h1:VauAph7uCvEakYNdHkkSAoOOGKvEuUA/uhsR376ThbI= github.com/wundergraph/go-arena v1.1.0/go.mod h1:ROOysEHWJjLQ8FSfNxZCziagb7Qw2nXY3/vgKRh7eWw=
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.19.3 h1:0HbDHF4sHfoyDrbPLMFWvsQLbTl2ITrpI9PjDIZsV1Y= github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.267 h1:qMkYR0oq0Cw61aDZs9VsCCVwNVSxRxT13ytz6WqCwJg=
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.19.3/go.mod h1:LrA7I7etRmhIC1PjO8c26BHm+gWsy2rC3eSMe5+XUWE= github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.267/go.mod h1:HjTAO/cuICpu31IfHY9qmSPygx6Gza7Wt9hTSReTI+A=
gitlab.com/unboundsoftware/eventsourced/pg v1.17.0 h1:pUJzMpNPX0GVsffRZXlpKR1d7Ws96KTxJwbLFPpASSc= gitlab.com/unboundsoftware/eventsourced/amqp v1.9.1 h1:X6269JoAzHIKCVmtgMHZH3m7xOpACSp37ca3eODe9iU=
gitlab.com/unboundsoftware/eventsourced/pg v1.17.0/go.mod h1:WgPrZhyCbsZ3TG2tPUbh2MUjOEaANJjsWi/0hlIwRVU= gitlab.com/unboundsoftware/eventsourced/amqp v1.9.1/go.mod h1:EAs0d6Eh0aDiQkUJlSWErHqgHFQdxx0e8I7aG/2FarY=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= gitlab.com/unboundsoftware/eventsourced/eventsourced v1.19.4 h1:+yZkhi9/sTyBEN5vJTfvycyXgGrm07QKGSh3jiWiQdM=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= gitlab.com/unboundsoftware/eventsourced/eventsourced v1.19.4/go.mod h1:LrA7I7etRmhIC1PjO8c26BHm+gWsy2rC3eSMe5+XUWE=
go.opentelemetry.io/contrib/bridges/otelslog v0.13.0 h1:bwnLpizECbPr1RrQ27waeY2SPIPeccCx/xLuoYADZ9s= gitlab.com/unboundsoftware/eventsourced/pg v1.18.6 h1:UMnuwC2lUxABE6k28Zl3WTTiVhWjsNQHnWFEZ0Krzw0=
go.opentelemetry.io/contrib/bridges/otelslog v0.13.0/go.mod h1:3nWlOiiqA9UtUnrcNk82mYasNxD8ehOspL0gOfEo6Y4= gitlab.com/unboundsoftware/eventsourced/pg v1.18.6/go.mod h1:Lzb0Q3xV6MW63rukaa1R40VFgTAAFYmXIm2tPrYXEQY=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 h1:Oe2z/BCg5q7k4iXC3cqJxKYg0ieRiOqF0cecFYdPTwk= go.opentelemetry.io/contrib/bridges/otelslog v0.17.0 h1:NFIS6x7wyObQ7cR84x7bt1sr8nYBx89s3x3GwRjw40k=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0/go.mod h1:ZQM5lAJpOsKnYagGg/zV2krVqTtaVdYdDkhMoX6Oalg= go.opentelemetry.io/contrib/bridges/otelslog v0.17.0/go.mod h1:39SaByOyDMRMe872AE7uelMuQZidIw7LLFAnQi0FWTE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0/go.mod h1:HBy4BjzgVE8139ieRI75oXm3EcDN+6GhD88JT1Kjvxg=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.14.0 h1:B/g+qde6Mkzxbry5ZZag0l7QrQBCtVm7lVjaLgmpje8= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.14.0/go.mod h1:mOJK8eMmgW6ocDJn6Bn11CcZ05gi3P8GylBXEkZtbgA= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
go.opentelemetry.io/otel/log v0.14.0 h1:2rzJ+pOAZ8qmZ3DDHg73NEKzSZkhkGIua9gXtxNGgrM= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc=
go.opentelemetry.io/otel/log v0.14.0/go.mod h1:5jRG92fEAgx0SU/vFPxmJvhIuDU9E1SUnEQrMlJpOno= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 h1:GJkybS+crDMdExT/BUNCEgfrmfboztcS6PhvSo88HKM=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0/go.mod h1:NuAyxRYIG2lKX3YQkB+83StTxM7s52PUUkRRiC0wnYI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/log v0.19.0/go.mod h1:5DQYeGmxVIr4n0/BcJvF4upsraHjg6vudJJpnkL6Ipk=
go.opentelemetry.io/otel/sdk/log v0.14.0 h1:JU/U3O7N6fsAXj0+CXz21Czg532dW2V4gG1HE/e8Zrg= go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
go.opentelemetry.io/otel/sdk/log v0.14.0/go.mod h1:imQvII+0ZylXfKU7/wtOND8Hn4OpT3YUoIgqJVksUkM= go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/sdk/log/logtest v0.14.0 h1:Ijbtz+JKXl8T2MngiwqBlPaHqc4YCaP/i13Qrow6gAM= go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
go.opentelemetry.io/otel/sdk/log/logtest v0.14.0/go.mod h1:dCU8aEL6q+L9cYTqcVOk8rM9Tp8WdnHOPLiBgp0SGOA= go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/sdk/log v0.19.0/go.mod h1:vFBowwXGLlW9AvpuF7bMgnNI95LiW10szrOdvzBHlAg=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/sdk/log/logtest v0.19.0 h1:BEbF7ZBB6qQloV/Ub1+3NQoOUnVtcGkU3XX4Ws3GQfk=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/otel/sdk/log/logtest v0.19.0/go.mod h1:Lua81/3yM0wOmoHTokLj9y9ADeA02v1naRrVrkAZuKk=
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
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.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-jose/go-jose.v2 v2.6.3 h1:nt80fvSDlhKWQgSWyHyy5CfmlQr+asih51R8PTWNKKs=
gopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -266,11 +291,11 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ= modernc.org/libc v1.68.0 h1:PJ5ikFOV5pwpW+VqCK1hKJuEWsonkIJhhIXyuF/91pQ=
modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8= modernc.org/libc v1.68.0/go.mod h1:NnKCYeoYgsEqnY3PgvNgAeaJnso968ygU8Z0DxjoEc0=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek= modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU=
modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E= modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
+2 -2
View File
@@ -1,8 +1,8 @@
package graph package graph
import ( import (
"gitlab.com/unboundsoftware/schemas/domain" "gitea.unbound.se/unboundsoftware/schemas/domain"
"gitlab.com/unboundsoftware/schemas/graph/model" "gitea.unbound.se/unboundsoftware/schemas/graph/model"
) )
func ToGqlOrganizations(orgs []domain.Organization) []*model.Organization { func ToGqlOrganizations(orgs []domain.Organization) []*model.Organization {
+37 -1
View File
@@ -1,14 +1,17 @@
package graph package graph
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"time"
"golang.org/x/sync/semaphore"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"gitlab.com/unboundsoftware/schemas/graph/model" "gitea.unbound.se/unboundsoftware/schemas/graph/model"
) )
// CommandExecutor is an interface for executing external commands // CommandExecutor is an interface for executing external commands
@@ -123,3 +126,36 @@ func GenerateCosmoRouterConfigWithExecutor(subGraphs []*model.SubGraph, executor
return string(configJSON), nil return string(configJSON), nil
} }
// CosmoGenerator wraps config generation with a concurrency limit and timeout
// to prevent unbounded wgc process spawning under rapid schema updates.
type CosmoGenerator struct {
sem *semaphore.Weighted
executor CommandExecutor
timeout time.Duration
}
// NewCosmoGenerator creates a CosmoGenerator that allows at most one concurrent
// wgc process and applies the given timeout to each generation attempt.
func NewCosmoGenerator(executor CommandExecutor, timeout time.Duration) *CosmoGenerator {
return &CosmoGenerator{
sem: semaphore.NewWeighted(1),
executor: executor,
timeout: timeout,
}
}
// Generate produces a Cosmo Router config, blocking if another generation is
// already in progress. The provided context (plus the configured timeout)
// controls cancellation.
func (g *CosmoGenerator) Generate(ctx context.Context, subGraphs []*model.SubGraph) (string, error) {
ctx, cancel := context.WithTimeout(ctx, g.timeout)
defer cancel()
if err := g.sem.Acquire(ctx, 1); err != nil {
return "", fmt.Errorf("acquire cosmo generator: %w", err)
}
defer g.sem.Release(1)
return GenerateCosmoRouterConfigWithExecutor(subGraphs, g.executor)
}
+113 -1
View File
@@ -1,17 +1,21 @@
package graph package graph
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"strings" "strings"
"sync"
"sync/atomic"
"testing" "testing"
"time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"gitlab.com/unboundsoftware/schemas/graph/model" "gitea.unbound.se/unboundsoftware/schemas/graph/model"
) )
// MockCommandExecutor implements CommandExecutor for testing // MockCommandExecutor implements CommandExecutor for testing
@@ -459,6 +463,114 @@ func TestGenerateCosmoRouterConfig_MockError(t *testing.T) {
assert.Equal(t, 1, mockExecutor.CallCount, "Should have attempted to call executor") assert.Equal(t, 1, mockExecutor.CallCount, "Should have attempted to call executor")
} }
// SlowMockExecutor simulates a slow wgc command for concurrency testing.
type SlowMockExecutor struct {
MockCommandExecutor
delay time.Duration
mu sync.Mutex
concurrent atomic.Int32
maxSeen atomic.Int32
}
func (m *SlowMockExecutor) Execute(name string, args ...string) ([]byte, error) {
cur := m.concurrent.Add(1)
// Track the maximum concurrent executions observed.
for {
old := m.maxSeen.Load()
if cur <= old || m.maxSeen.CompareAndSwap(old, cur) {
break
}
}
defer m.concurrent.Add(-1)
time.Sleep(m.delay)
m.mu.Lock()
defer m.mu.Unlock()
return m.MockCommandExecutor.Execute(name, args...)
}
func TestCosmoGenerator_ConcurrencyLimit(t *testing.T) {
executor := &SlowMockExecutor{delay: 100 * time.Millisecond}
gen := NewCosmoGenerator(executor, 5*time.Second)
subGraphs := []*model.SubGraph{
{
Service: "svc",
URL: stringPtr("http://localhost:4001/query"),
Sdl: "type Query { hello: String }",
},
}
var wg sync.WaitGroup
for range 5 {
wg.Add(1)
go func() {
defer wg.Done()
_, _ = gen.Generate(context.Background(), subGraphs)
}()
}
wg.Wait()
assert.Equal(t, int32(1), executor.maxSeen.Load(),
"at most 1 wgc process should run concurrently")
}
func TestCosmoGenerator_Timeout(t *testing.T) {
// Executor that takes longer than the timeout.
executor := &SlowMockExecutor{delay: 500 * time.Millisecond}
gen := NewCosmoGenerator(executor, 50*time.Millisecond)
subGraphs := []*model.SubGraph{
{
Service: "svc",
URL: stringPtr("http://localhost:4001/query"),
Sdl: "type Query { hello: String }",
},
}
// First call: occupies the semaphore for 500ms.
go func() {
_, _ = gen.Generate(context.Background(), subGraphs)
}()
// Give the first goroutine time to acquire the semaphore.
time.Sleep(20 * time.Millisecond)
// Second call: should timeout waiting for the semaphore.
_, err := gen.Generate(context.Background(), subGraphs)
require.Error(t, err)
assert.Contains(t, err.Error(), "acquire cosmo generator")
}
func TestCosmoGenerator_ContextCancellation(t *testing.T) {
executor := &SlowMockExecutor{delay: 500 * time.Millisecond}
gen := NewCosmoGenerator(executor, 5*time.Second)
subGraphs := []*model.SubGraph{
{
Service: "svc",
URL: stringPtr("http://localhost:4001/query"),
Sdl: "type Query { hello: String }",
},
}
// First call: occupies the semaphore.
go func() {
_, _ = gen.Generate(context.Background(), subGraphs)
}()
time.Sleep(20 * time.Millisecond)
// Second call with an already-cancelled context.
ctx, cancel := context.WithCancel(context.Background())
cancel()
_, err := gen.Generate(ctx, subGraphs)
require.Error(t, err)
assert.Contains(t, err.Error(), "acquire cosmo generator")
}
// Helper function for tests // Helper function for tests
func stringPtr(s string) *string { func stringPtr(s string) *string {
return &s return &s
+42
View File
@@ -0,0 +1,42 @@
package graph
import (
"sync"
"time"
)
// Debouncer coalesces rapid calls with the same key, executing only the last
// one after a configurable delay. This prevents redundant work when multiple
// updates arrive in quick succession (e.g., rapid schema publishing).
type Debouncer struct {
mu sync.Mutex
delay time.Duration
timers map[string]*time.Timer
}
// NewDebouncer creates a Debouncer with the given delay window.
func NewDebouncer(delay time.Duration) *Debouncer {
return &Debouncer{
delay: delay,
timers: make(map[string]*time.Timer),
}
}
// Debounce resets the timer for key. When the timer fires (after delay with no
// new calls for the same key), fn is executed in a new goroutine.
func (d *Debouncer) Debounce(key string, fn func()) {
d.mu.Lock()
defer d.mu.Unlock()
if t, ok := d.timers[key]; ok {
t.Stop()
}
d.timers[key] = time.AfterFunc(d.delay, func() {
d.mu.Lock()
delete(d.timers, key)
d.mu.Unlock()
fn()
})
}
+69
View File
@@ -0,0 +1,69 @@
package graph
import (
"sync/atomic"
"testing"
"testing/synctest"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDebouncer_Coalesces(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
d := NewDebouncer(50 * time.Millisecond)
var calls atomic.Int32
// Fire 10 rapid calls for the same key — only the last should execute.
for range 10 {
d.Debounce("key1", func() {
calls.Add(1)
})
}
// Advance fake clock past the debounce delay and let goroutines settle.
time.Sleep(50 * time.Millisecond)
synctest.Wait()
assert.Equal(t, int32(1), calls.Load(), "rapid calls should coalesce into a single execution")
})
}
func TestDebouncer_DifferentKeys(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
d := NewDebouncer(50 * time.Millisecond)
var calls atomic.Int32
d.Debounce("key-a", func() { calls.Add(1) })
d.Debounce("key-b", func() { calls.Add(1) })
d.Debounce("key-c", func() { calls.Add(1) })
time.Sleep(50 * time.Millisecond)
synctest.Wait()
assert.Equal(t, int32(3), calls.Load(), "different keys should fire independently")
})
}
func TestDebouncer_TimerReset(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
d := NewDebouncer(100 * time.Millisecond)
var value atomic.Int32
// First call sets value to 1.
d.Debounce("key", func() { value.Store(1) })
// Advance 60ms (less than the 100ms delay) — first timer hasn't fired.
time.Sleep(60 * time.Millisecond)
// Replace with value 2 — resets the timer to fire at 60+100 = 160ms.
d.Debounce("key", func() { value.Store(2) })
// Advance another 100ms (total 160ms) to fire the reset timer.
time.Sleep(100 * time.Millisecond)
synctest.Wait()
require.Equal(t, int32(2), value.Load(), "later call should replace the earlier one")
})
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -3,7 +3,7 @@ package graph
import ( import (
"sync" "sync"
"gitlab.com/unboundsoftware/schemas/graph/model" "gitea.unbound.se/unboundsoftware/schemas/graph/model"
) )
// PubSub handles publishing schema updates to subscribers // PubSub handles publishing schema updates to subscribers
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gitlab.com/unboundsoftware/schemas/graph/model" "gitea.unbound.se/unboundsoftware/schemas/graph/model"
) )
func TestPubSub_SubscribeAndPublish(t *testing.T) { func TestPubSub_SubscribeAndPublish(t *testing.T) {
+10 -8
View File
@@ -7,13 +7,13 @@ import (
"gitlab.com/unboundsoftware/eventsourced/eventsourced" "gitlab.com/unboundsoftware/eventsourced/eventsourced"
"gitlab.com/unboundsoftware/schemas/cache" "gitea.unbound.se/unboundsoftware/schemas/cache"
"gitlab.com/unboundsoftware/schemas/middleware" "gitea.unbound.se/unboundsoftware/schemas/middleware"
) )
//go:generate go run github.com/99designs/gqlgen //go:generate go run github.com/99designs/gqlgen
//go:generate gofumpt -w . //go:generate gofumpt -w .
//go:generate goimports -w -local gitlab.com/unboundsoftware/schemas . //go:generate goimports -w -local gitea.unbound.se/unboundsoftware/schemas .
// This file will not be regenerated automatically. // This file will not be regenerated automatically.
// //
@@ -24,11 +24,13 @@ type Publisher interface {
} }
type Resolver struct { type Resolver struct {
EventStore eventsourced.EventStore EventStore eventsourced.EventStore
Publisher Publisher Publisher Publisher
Logger *slog.Logger Logger *slog.Logger
Cache *cache.Cache Cache *cache.Cache
PubSub *PubSub PubSub *PubSub
CosmoGenerator *CosmoGenerator
Debouncer *Debouncer
} }
func (r *Resolver) apiKeyCanAccessRef(ctx context.Context, ref string, publish bool) (string, error) { func (r *Resolver) apiKeyCanAccessRef(ctx context.Context, ref string, publish bool) (string, error) {
+6 -2
View File
@@ -1,12 +1,16 @@
type Query { type Query {
organizations: [Organization!]! @auth(user: true) organizations: [Organization!]! @auth(user: true)
supergraph(ref: String!, isAfter: String): Supergraph! @auth(organization: true) allOrganizations: [Organization!]! @auth(user: true)
latestSchema(ref: String!): SchemaUpdate! @auth(organization: true) supergraph(ref: String!, isAfter: String): Supergraph! @auth(user: true, organization: true)
latestSchema(ref: String!): SchemaUpdate! @auth(user: true, organization: true)
} }
type Mutation { type Mutation {
addOrganization(name: String!): Organization! @auth(user: true) addOrganization(name: String!): Organization! @auth(user: true)
addUserToOrganization(organizationId: ID!, userId: String!): Organization! @auth(user: true)
addAPIKey(input: InputAPIKey): APIKey! @auth(user: true) addAPIKey(input: InputAPIKey): APIKey! @auth(user: true)
removeAPIKey(organizationId: ID!, keyName: String!): Organization! @auth(user: true)
removeOrganization(organizationId: ID!): Boolean! @auth(user: true)
updateSubGraph(input: InputSubGraph!): SubGraph! @auth(organization: true) updateSubGraph(input: InputSubGraph!): SubGraph! @auth(organization: true)
} }
+2 -2
View File
@@ -5,8 +5,8 @@ import (
"gitlab.com/unboundsoftware/eventsourced/eventsourced" "gitlab.com/unboundsoftware/eventsourced/eventsourced"
"gitlab.com/unboundsoftware/schemas/domain" "gitea.unbound.se/unboundsoftware/schemas/domain"
"gitlab.com/unboundsoftware/schemas/graph/model" "gitea.unbound.se/unboundsoftware/schemas/graph/model"
) )
func (r *Resolver) fetchSubGraph(ctx context.Context, subGraphId string) (*domain.SubGraph, error) { func (r *Resolver) fetchSubGraph(ctx context.Context, subGraphId string) (*domain.SubGraph, error) {
+143 -63
View File
@@ -1,6 +1,7 @@
package graph package graph
// This file will be automatically regenerated based on the schema, any resolver implementations // This file will be automatically regenerated based on the schema, any resolver
// implementations
// will be copied through when generating and any unknown code will be moved to the end. // will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen // Code generated by github.com/99designs/gqlgen
@@ -11,12 +12,12 @@ import (
"gitlab.com/unboundsoftware/eventsourced/eventsourced" "gitlab.com/unboundsoftware/eventsourced/eventsourced"
"gitlab.com/unboundsoftware/schemas/domain" "gitea.unbound.se/unboundsoftware/schemas/domain"
"gitlab.com/unboundsoftware/schemas/graph/generated" "gitea.unbound.se/unboundsoftware/schemas/graph/generated"
"gitlab.com/unboundsoftware/schemas/graph/model" "gitea.unbound.se/unboundsoftware/schemas/graph/model"
"gitlab.com/unboundsoftware/schemas/middleware" "gitea.unbound.se/unboundsoftware/schemas/middleware"
"gitlab.com/unboundsoftware/schemas/rand" "gitea.unbound.se/unboundsoftware/schemas/rand"
"gitlab.com/unboundsoftware/schemas/sdlmerge" "gitea.unbound.se/unboundsoftware/schemas/sdlmerge"
) )
// AddOrganization is the resolver for the addOrganization field. // AddOrganization is the resolver for the addOrganization field.
@@ -37,6 +38,24 @@ func (r *mutationResolver) AddOrganization(ctx context.Context, name string) (*m
return ToGqlOrganization(*org), nil return ToGqlOrganization(*org), nil
} }
// AddUserToOrganization is the resolver for the addUserToOrganization field.
func (r *mutationResolver) AddUserToOrganization(ctx context.Context, organizationID string, userID string) (*model.Organization, error) {
sub := middleware.UserFromContext(ctx)
org := &domain.Organization{BaseAggregate: eventsourced.BaseAggregateFromString(organizationID)}
h, err := r.handler(ctx, org)
if err != nil {
return nil, err
}
_, err = h.Handle(ctx, &domain.AddUserToOrganization{
UserId: userID,
Initiator: sub,
})
if err != nil {
return nil, err
}
return ToGqlOrganization(*org), nil
}
// AddAPIKey is the resolver for the addAPIKey field. // AddAPIKey is the resolver for the addAPIKey field.
func (r *mutationResolver) AddAPIKey(ctx context.Context, input *model.InputAPIKey) (*model.APIKey, error) { func (r *mutationResolver) AddAPIKey(ctx context.Context, input *model.InputAPIKey) (*model.APIKey, error) {
sub := middleware.UserFromContext(ctx) sub := middleware.UserFromContext(ctx)
@@ -71,6 +90,41 @@ func (r *mutationResolver) AddAPIKey(ctx context.Context, input *model.InputAPIK
}, nil }, nil
} }
// RemoveAPIKey is the resolver for the removeAPIKey field.
func (r *mutationResolver) RemoveAPIKey(ctx context.Context, organizationID string, keyName string) (*model.Organization, error) {
sub := middleware.UserFromContext(ctx)
org := &domain.Organization{BaseAggregate: eventsourced.BaseAggregateFromString(organizationID)}
h, err := r.handler(ctx, org)
if err != nil {
return nil, err
}
_, err = h.Handle(ctx, &domain.RemoveAPIKey{
KeyName: keyName,
Initiator: sub,
})
if err != nil {
return nil, err
}
return ToGqlOrganization(*org), nil
}
// RemoveOrganization is the resolver for the removeOrganization field.
func (r *mutationResolver) RemoveOrganization(ctx context.Context, organizationID string) (bool, error) {
sub := middleware.UserFromContext(ctx)
org := &domain.Organization{BaseAggregate: eventsourced.BaseAggregateFromString(organizationID)}
h, err := r.handler(ctx, org)
if err != nil {
return false, err
}
_, err = h.Handle(ctx, &domain.RemoveOrganization{
Initiator: sub,
})
if err != nil {
return false, err
}
return true, nil
}
// UpdateSubGraph is the resolver for the updateSubGraph field. // UpdateSubGraph is the resolver for the updateSubGraph field.
func (r *mutationResolver) UpdateSubGraph(ctx context.Context, input model.InputSubGraph) (*model.SubGraph, error) { func (r *mutationResolver) UpdateSubGraph(ctx context.Context, input model.InputSubGraph) (*model.SubGraph, error) {
orgId := middleware.OrganizationFromContext(ctx) orgId := middleware.OrganizationFromContext(ctx)
@@ -120,8 +174,9 @@ func (r *mutationResolver) UpdateSubGraph(ctx context.Context, input model.Input
return nil, err return nil, err
} }
// Publish schema update to subscribers // Debounce schema update publishing so rapid successive updates for the
go func() { // same org+ref only trigger one config generation.
r.Debouncer.Debounce(orgId+":"+input.Ref, func() {
services, lastUpdate := r.Cache.Services(orgId, input.Ref, "") services, lastUpdate := r.Cache.Services(orgId, input.Ref, "")
r.Logger.Info("Publishing schema update after subgraph change", r.Logger.Info("Publishing schema update after subgraph change",
"ref", input.Ref, "ref", input.Ref,
@@ -137,19 +192,11 @@ func (r *mutationResolver) UpdateSubGraph(ctx context.Context, input model.Input
r.Logger.Error("fetch subgraph for update notification", "error", err) r.Logger.Error("fetch subgraph for update notification", "error", err)
continue continue
} }
subGraphs[i] = &model.SubGraph{ subGraphs[i] = r.toGqlSubGraph(sg)
ID: sg.ID.String(),
Service: sg.Service,
URL: sg.Url,
WsURL: sg.WSUrl,
Sdl: sg.Sdl,
ChangedBy: sg.ChangedBy,
ChangedAt: sg.ChangedAt,
}
} }
// Generate Cosmo router config // Generate Cosmo router config (concurrency-limited)
cosmoConfig, err := GenerateCosmoRouterConfig(subGraphs) cosmoConfig, err := r.CosmoGenerator.Generate(context.Background(), subGraphs)
if err != nil { if err != nil {
r.Logger.Error("generate cosmo config for update", "error", err) r.Logger.Error("generate cosmo config for update", "error", err)
cosmoConfig = "" // Send empty if generation fails cosmoConfig = "" // Send empty if generation fails
@@ -171,7 +218,7 @@ func (r *mutationResolver) UpdateSubGraph(ctx context.Context, input model.Input
) )
r.PubSub.Publish(input.Ref, update) r.PubSub.Publish(input.Ref, update)
}() })
return r.toGqlSubGraph(subGraph), nil return r.toGqlSubGraph(subGraph), nil
} }
@@ -183,13 +230,49 @@ func (r *queryResolver) Organizations(ctx context.Context) ([]*model.Organizatio
return ToGqlOrganizations(orgs), nil return ToGqlOrganizations(orgs), nil
} }
// AllOrganizations is the resolver for the allOrganizations field.
func (r *queryResolver) AllOrganizations(ctx context.Context) ([]*model.Organization, error) {
// Check if user has admin role
if !middleware.UserHasRole(ctx, "admin") {
return nil, fmt.Errorf("unauthorized: admin role required")
}
orgs := r.Cache.AllOrganizations()
return ToGqlOrganizations(orgs), nil
}
// Supergraph is the resolver for the supergraph field. // Supergraph is the resolver for the supergraph field.
func (r *queryResolver) Supergraph(ctx context.Context, ref string, isAfter *string) (model.Supergraph, error) { func (r *queryResolver) Supergraph(ctx context.Context, ref string, isAfter *string) (model.Supergraph, error) {
orgId := middleware.OrganizationFromContext(ctx) orgId := middleware.OrganizationFromContext(ctx)
_, err := r.apiKeyCanAccessRef(ctx, ref, false) userId := middleware.UserFromContext(ctx)
if err != nil {
return nil, err r.Logger.Info("Supergraph query",
"ref", ref,
"orgId", orgId,
"userId", userId,
)
// If authenticated with API key (organization), check access
if orgId != "" {
_, err := r.apiKeyCanAccessRef(ctx, ref, false)
if err != nil {
r.Logger.Error("API key cannot access ref", "error", err, "ref", ref)
return nil, err
}
} else if userId != "" {
// For user authentication, check if user has access to ref through their organizations
userOrgs := r.Cache.OrganizationsByUser(userId)
if len(userOrgs) == 0 {
r.Logger.Error("User has no organizations", "userId", userId)
return nil, fmt.Errorf("user has no access to any organizations")
}
// Use the first organization's ID for querying
orgId = userOrgs[0].ID.String()
r.Logger.Info("Using organization from user context", "orgId", orgId)
} else {
return nil, fmt.Errorf("no authentication provided")
} }
after := "" after := ""
if isAfter != nil { if isAfter != nil {
after = *isAfter after = *isAfter
@@ -202,30 +285,16 @@ func (r *queryResolver) Supergraph(ctx context.Context, ref string, isAfter *str
}, nil }, nil
} }
subGraphs := make([]*model.SubGraph, len(services)) subGraphs := make([]*model.SubGraph, len(services))
serviceSDLs := make([]string, len(services))
for i, id := range services { for i, id := range services {
sg, err := r.fetchSubGraph(ctx, id) sg, err := r.fetchSubGraph(ctx, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
subGraphs[i] = &model.SubGraph{ subGraphs[i] = r.toGqlSubGraph(sg)
ID: sg.ID.String(), serviceSDLs[i] = sg.Sdl
Service: sg.Service,
URL: sg.Url,
WsURL: sg.WSUrl,
Sdl: sg.Sdl,
ChangedBy: sg.ChangedBy,
ChangedAt: sg.ChangedAt,
}
} }
var serviceSDLs []string
for _, id := range services {
sg, err := r.fetchSubGraph(ctx, id)
if err != nil {
return nil, err
}
serviceSDLs = append(serviceSDLs, sg.Sdl)
}
sdl, err := sdlmerge.MergeSDLs(serviceSDLs...) sdl, err := sdlmerge.MergeSDLs(serviceSDLs...)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -241,16 +310,34 @@ func (r *queryResolver) Supergraph(ctx context.Context, ref string, isAfter *str
// LatestSchema is the resolver for the latestSchema field. // LatestSchema is the resolver for the latestSchema field.
func (r *queryResolver) LatestSchema(ctx context.Context, ref string) (*model.SchemaUpdate, error) { func (r *queryResolver) LatestSchema(ctx context.Context, ref string) (*model.SchemaUpdate, error) {
orgId := middleware.OrganizationFromContext(ctx) orgId := middleware.OrganizationFromContext(ctx)
userId := middleware.UserFromContext(ctx)
r.Logger.Info("LatestSchema query", r.Logger.Info("LatestSchema query",
"ref", ref, "ref", ref,
"orgId", orgId, "orgId", orgId,
"userId", userId,
) )
_, err := r.apiKeyCanAccessRef(ctx, ref, false) // If authenticated with API key (organization), check access
if err != nil { if orgId != "" {
r.Logger.Error("API key cannot access ref", "error", err, "ref", ref) _, err := r.apiKeyCanAccessRef(ctx, ref, false)
return nil, err if err != nil {
r.Logger.Error("API key cannot access ref", "error", err, "ref", ref)
return nil, err
}
} else if userId != "" {
// For user authentication, check if user has access to ref through their organizations
userOrgs := r.Cache.OrganizationsByUser(userId)
if len(userOrgs) == 0 {
r.Logger.Error("User has no organizations", "userId", userId)
return nil, fmt.Errorf("user has no access to any organizations")
}
// Use the first organization's ID for querying
// In a real-world scenario, you might want to check which org has access to this ref
orgId = userOrgs[0].ID.String()
r.Logger.Info("Using organization from user context", "orgId", orgId)
} else {
return nil, fmt.Errorf("no authentication provided")
} }
// Get current services and schema // Get current services and schema
@@ -280,8 +367,8 @@ func (r *queryResolver) LatestSchema(ctx context.Context, ref string) (*model.Sc
} }
} }
// Generate Cosmo router config // Generate Cosmo router config (concurrency-limited)
cosmoConfig, err := GenerateCosmoRouterConfig(subGraphs) cosmoConfig, err := r.CosmoGenerator.Generate(ctx, subGraphs)
if err != nil { if err != nil {
r.Logger.Error("generate cosmo config", "error", err) r.Logger.Error("generate cosmo config", "error", err)
cosmoConfig = "" // Return empty if generation fails cosmoConfig = "" // Return empty if generation fails
@@ -324,9 +411,6 @@ func (r *subscriptionResolver) SchemaUpdates(ctx context.Context, ref string) (<
// Send initial state immediately // Send initial state immediately
go func() { go func() {
// Use background context for async operation
bgCtx := context.Background()
services, lastUpdate := r.Cache.Services(orgId, ref, "") services, lastUpdate := r.Cache.Services(orgId, ref, "")
r.Logger.Info("Preparing initial schema update", r.Logger.Info("Preparing initial schema update",
"ref", ref, "ref", ref,
@@ -337,24 +421,16 @@ func (r *subscriptionResolver) SchemaUpdates(ctx context.Context, ref string) (<
subGraphs := make([]*model.SubGraph, len(services)) subGraphs := make([]*model.SubGraph, len(services))
for i, id := range services { for i, id := range services {
sg, err := r.fetchSubGraph(bgCtx, id) sg, err := r.fetchSubGraph(ctx, id)
if err != nil { if err != nil {
r.Logger.Error("fetch subgraph for initial update", "error", err, "id", id) r.Logger.Error("fetch subgraph for initial update", "error", err, "id", id)
continue continue
} }
subGraphs[i] = &model.SubGraph{ subGraphs[i] = r.toGqlSubGraph(sg)
ID: sg.ID.String(),
Service: sg.Service,
URL: sg.Url,
WsURL: sg.WSUrl,
Sdl: sg.Sdl,
ChangedBy: sg.ChangedBy,
ChangedAt: sg.ChangedAt,
}
} }
// Generate Cosmo router config // Generate Cosmo router config (concurrency-limited)
cosmoConfig, err := GenerateCosmoRouterConfig(subGraphs) cosmoConfig, err := r.CosmoGenerator.Generate(ctx, subGraphs)
if err != nil { if err != nil {
r.Logger.Error("generate cosmo config", "error", err) r.Logger.Error("generate cosmo config", "error", err)
cosmoConfig = "" // Send empty if generation fails cosmoConfig = "" // Send empty if generation fails
@@ -375,7 +451,11 @@ func (r *subscriptionResolver) SchemaUpdates(ctx context.Context, ref string) (<
"cosmoConfigLength", len(cosmoConfig), "cosmoConfigLength", len(cosmoConfig),
) )
ch <- update select {
case ch <- update:
case <-ctx.Done():
return
}
}() }()
// Clean up subscription when context is done // Clean up subscription when context is done
+1 -1
View File
@@ -61,7 +61,7 @@ spec:
timeoutSeconds: 5 timeoutSeconds: 5
failureThreshold: 3 failureThreshold: 3
imagePullPolicy: IfNotPresent imagePullPolicy: IfNotPresent
image: registry.gitlab.com/unboundsoftware/schemas:${COMMIT} image: oci.unbound.se/unboundsoftware/schemas:${COMMIT}
ports: ports:
- name: api - name: api
containerPort: 8080 containerPort: 8080
+1 -1
View File
@@ -3,7 +3,6 @@ kind: Ingress
metadata: metadata:
name: schemas-ingress name: schemas-ingress
annotations: annotations:
kubernetes.io/ingress.class: "alb"
alb.ingress.kubernetes.io/group.name: "default" alb.ingress.kubernetes.io/group.name: "default"
alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: instance alb.ingress.kubernetes.io/target-type: instance
@@ -11,6 +10,7 @@ metadata:
alb.ingress.kubernetes.io/ssl-redirect: "443" alb.ingress.kubernetes.io/ssl-redirect: "443"
alb.ingress.kubernetes.io/healthcheck-path: '/health' alb.ingress.kubernetes.io/healthcheck-path: '/health'
spec: spec:
ingressClassName: "alb"
rules: rules:
- host: "schemas.unbound.se" - host: "schemas.unbound.se"
http: http:
+55 -14
View File
@@ -6,9 +6,8 @@ import (
"net/http" "net/http"
"github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql"
"github.com/golang-jwt/jwt/v5"
"gitlab.com/unboundsoftware/schemas/domain" "gitea.unbound.se/unboundsoftware/schemas/domain"
) )
const ( const (
@@ -33,14 +32,9 @@ type AuthMiddleware struct {
func (m *AuthMiddleware) Handler(next http.Handler) http.Handler { func (m *AuthMiddleware) Handler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
token, err := TokenFromContext(r.Context()) claims := ClaimsFromContext(r.Context())
if err != nil { if claims != nil {
w.WriteHeader(http.StatusInternalServerError) ctx = context.WithValue(ctx, UserKey, claims.RegisteredClaims.Subject)
_, _ = w.Write([]byte("Invalid JWT token format"))
return
}
if token != nil {
ctx = context.WithValue(ctx, UserKey, token.Claims.(jwt.MapClaims)["sub"])
} }
apiKey, err := ApiKeyFromContext(r.Context()) apiKey, err := ApiKeyFromContext(r.Context())
if err != nil { if err != nil {
@@ -67,6 +61,26 @@ func UserFromContext(ctx context.Context) string {
return "" return ""
} }
func UserHasRole(ctx context.Context, role string) bool {
claims := ClaimsFromContext(ctx)
if claims == nil {
return false
}
customClaims, ok := claims.CustomClaims.(*CustomClaims)
if !ok || customClaims == nil {
return false
}
for _, r := range customClaims.Roles {
if r == role {
return true
}
}
return false
}
func OrganizationFromContext(ctx context.Context) string { func OrganizationFromContext(ctx context.Context) string {
if value := ctx.Value(OrganizationKey); value != nil { if value := ctx.Value(OrganizationKey); value != nil {
if u, ok := value.(domain.Organization); ok { if u, ok := value.(domain.Organization); ok {
@@ -77,15 +91,42 @@ func OrganizationFromContext(ctx context.Context) string {
} }
func (m *AuthMiddleware) Directive(ctx context.Context, _ interface{}, next graphql.Resolver, user *bool, organization *bool) (res interface{}, err error) { func (m *AuthMiddleware) Directive(ctx context.Context, _ interface{}, next graphql.Resolver, user *bool, organization *bool) (res interface{}, err error) {
if user != nil && *user { userRequired := user != nil && *user
if u := UserFromContext(ctx); u == "" { orgRequired := organization != nil && *organization
u := UserFromContext(ctx)
orgId := OrganizationFromContext(ctx)
fmt.Printf("[Auth Directive] userRequired=%v, orgRequired=%v, hasUser=%v, hasOrg=%v\n",
userRequired, orgRequired, u != "", orgId != "")
// If both are required, it means EITHER is acceptable (OR logic)
if userRequired && orgRequired {
if u == "" && orgId == "" {
fmt.Printf("[Auth Directive] REJECTED: Neither user nor organization available\n")
return nil, fmt.Errorf("authentication required: provide either user token or organization API key")
}
fmt.Printf("[Auth Directive] ACCEPTED: Has user=%v OR organization=%v\n", u != "", orgId != "")
return next(ctx)
}
// Only user required
if userRequired {
if u == "" {
fmt.Printf("[Auth Directive] REJECTED: No user available\n")
return nil, fmt.Errorf("no user available in request") return nil, fmt.Errorf("no user available in request")
} }
fmt.Printf("[Auth Directive] ACCEPTED: User authenticated\n")
} }
if organization != nil && *organization {
if orgId := OrganizationFromContext(ctx); orgId == "" { // Only organization required
if orgRequired {
if orgId == "" {
fmt.Printf("[Auth Directive] REJECTED: No organization available\n")
return nil, fmt.Errorf("no organization available in request") return nil, fmt.Errorf("no organization available in request")
} }
fmt.Printf("[Auth Directive] ACCEPTED: Organization authenticated\n")
} }
return next(ctx) return next(ctx)
} }
+50 -141
View File
@@ -2,39 +2,34 @@ package middleware
import ( import (
"context" "context"
"crypto/tls"
"encoding/json"
"fmt" "fmt"
"net/http" "log"
"strings" "net/url"
"sync"
"time"
mw "github.com/auth0/go-jwt-middleware/v2" jwtmiddleware "github.com/auth0/go-jwt-middleware/v3"
"github.com/golang-jwt/jwt/v5" "github.com/auth0/go-jwt-middleware/v3/jwks"
"github.com/pkg/errors" "github.com/auth0/go-jwt-middleware/v3/validator"
) )
// CustomClaims contains custom claims from the JWT token.
type CustomClaims struct {
Roles []string `json:"https://unbound.se/roles"`
}
// Validate implements the validator.CustomClaims interface.
func (c CustomClaims) Validate(_ context.Context) error {
return nil
}
type Auth0 struct { type Auth0 struct {
domain string domain string
audience string audience string
client *http.Client
cache JwksCache
} }
func NewAuth0(audience, domain string, strictSsl bool) *Auth0 { func NewAuth0(audience, domain string, _ bool) *Auth0 {
customTransport := http.DefaultTransport.(*http.Transport).Clone()
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: !strictSsl}
client := &http.Client{Transport: customTransport}
return &Auth0{ return &Auth0{
domain: domain, domain: domain,
audience: audience, audience: audience,
client: client,
cache: JwksCache{
RWMutex: &sync.RWMutex{},
cache: make(map[string]cacheItem),
},
} }
} }
@@ -42,133 +37,47 @@ type Response struct {
Message string `json:"message"` Message string `json:"message"`
} }
type Jwks struct { func (a *Auth0) Middleware() *jwtmiddleware.JWTMiddleware {
Keys []JSONWebKeys `json:"keys"`
}
type JSONWebKeys struct {
Kty string `json:"kty"`
Kid string `json:"kid"`
Use string `json:"use"`
N string `json:"n"`
E string `json:"e"`
X5c []string `json:"x5c"`
}
func (a *Auth0) ValidationKeyGetter() func(token *jwt.Token) (interface{}, error) {
return func(token *jwt.Token) (interface{}, error) {
// Verify 'aud' claim
cert, err := a.getPemCert(token)
if err != nil {
panic(err.Error())
}
result, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(cert))
return result, nil
}
}
func (a *Auth0) Middleware() *mw.JWTMiddleware {
issuer := fmt.Sprintf("https://%s/", a.domain) issuer := fmt.Sprintf("https://%s/", a.domain)
jwtMiddleware := mw.New(func(ctx context.Context, token string) (interface{}, error) {
jwtToken, err := jwt.Parse(token, a.ValidationKeyGetter(), jwt.WithAudience(a.audience), jwt.WithIssuer(issuer)) issuerURL, err := url.Parse(issuer)
if err != nil { if err != nil {
return nil, err log.Fatalf("failed to parse issuer URL: %v", err)
} }
if _, ok := jwtToken.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", jwtToken.Header["alg"]) provider, err := jwks.NewCachingProvider(jwks.WithIssuerURL(issuerURL))
} if err != nil {
return jwtToken, nil log.Fatalf("failed to create JWKS provider: %v", err)
}, }
mw.WithTokenExtractor(func(r *http.Request) (string, error) {
token := r.Header.Get("Authorization") jwtValidator, err := validator.New(
if strings.HasPrefix(token, "Bearer ") { validator.WithKeyFunc(provider.KeyFunc),
return token[7:], nil validator.WithAlgorithm(validator.RS256),
} validator.WithIssuer(issuer),
return "", nil validator.WithAudience(a.audience),
validator.WithCustomClaims(func() validator.CustomClaims {
return &CustomClaims{}
}), }),
mw.WithCredentialsOptional(true),
) )
if err != nil {
log.Fatalf("failed to create JWT validator: %v", err)
}
jwtMiddleware, err := jwtmiddleware.New(
jwtmiddleware.WithValidator(jwtValidator),
jwtmiddleware.WithCredentialsOptional(true),
)
if err != nil {
log.Fatalf("failed to create JWT middleware: %v", err)
}
return jwtMiddleware return jwtMiddleware
} }
func TokenFromContext(ctx context.Context) (*jwt.Token, error) { func ClaimsFromContext(ctx context.Context) *validator.ValidatedClaims {
if value := ctx.Value(mw.ContextKey{}); value != nil { claims, err := jwtmiddleware.GetClaims[*validator.ValidatedClaims](ctx)
if u, ok := value.(*jwt.Token); ok {
return u, nil
}
return nil, fmt.Errorf("token is in wrong format")
}
return nil, nil
}
func (a *Auth0) cacheGetWellknown(url string) (*Jwks, error) {
if value := a.cache.get(url); value != nil {
return value, nil
}
jwks := &Jwks{}
resp, err := a.client.Get(url)
if err != nil { if err != nil {
return jwks, err return nil
}
defer func() {
_ = resp.Body.Close()
}()
err = json.NewDecoder(resp.Body).Decode(jwks)
if err == nil && jwks != nil {
a.cache.put(url, jwks)
}
return jwks, err
}
func (a *Auth0) getPemCert(token *jwt.Token) (string, error) {
jwks, err := a.cacheGetWellknown(fmt.Sprintf("https://%s/.well-known/jwks.json", a.domain))
if err != nil {
return "", err
}
var cert string
for k := range jwks.Keys {
if token.Header["kid"] == jwks.Keys[k].Kid {
cert = "-----BEGIN CERTIFICATE-----\n" + jwks.Keys[k].X5c[0] + "\n-----END CERTIFICATE-----"
}
}
if cert == "" {
err := errors.New("Unable to find appropriate key.")
return cert, err
}
return cert, nil
}
type JwksCache struct {
*sync.RWMutex
cache map[string]cacheItem
}
type cacheItem struct {
data *Jwks
expiration time.Time
}
func (c *JwksCache) get(url string) *Jwks {
c.RLock()
defer c.RUnlock()
if value, ok := c.cache[url]; ok {
if time.Now().After(value.expiration) {
return nil
}
return value.data
}
return nil
}
func (c *JwksCache) put(url string, jwks *Jwks) {
c.Lock()
defer c.Unlock()
c.cache[url] = cacheItem{
data: jwks,
expiration: time.Now().Add(time.Minute * 60),
} }
return claims
} }
+126 -24
View File
@@ -6,15 +6,15 @@ import (
"net/http/httptest" "net/http/httptest"
"testing" "testing"
mw "github.com/auth0/go-jwt-middleware/v2" "github.com/auth0/go-jwt-middleware/v3/core"
"github.com/golang-jwt/jwt/v5" "github.com/auth0/go-jwt-middleware/v3/validator"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gitlab.com/unboundsoftware/eventsourced/eventsourced" "gitlab.com/unboundsoftware/eventsourced/eventsourced"
"gitlab.com/unboundsoftware/schemas/domain" "gitea.unbound.se/unboundsoftware/schemas/domain"
) )
// MockCache is a mock implementation of the Cache interface // MockCache is a mock implementation of the Cache interface
@@ -155,9 +155,11 @@ func TestAuthMiddleware_Handler_WithValidJWT(t *testing.T) {
mockCache.On("OrganizationByAPIKey", "").Return(nil) mockCache.On("OrganizationByAPIKey", "").Return(nil)
userID := "user-123" userID := "user-123"
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ claims := &validator.ValidatedClaims{
"sub": userID, RegisteredClaims: validator.RegisteredClaims{
}) Subject: userID,
},
}
// Create a test handler that checks the context // Create a test handler that checks the context
var capturedUser string var capturedUser string
@@ -170,9 +172,9 @@ func TestAuthMiddleware_Handler_WithValidJWT(t *testing.T) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
}) })
// Create request with JWT token in context // Create request with JWT claims in context
req := httptest.NewRequest(http.MethodGet, "/test", nil) req := httptest.NewRequest(http.MethodGet, "/test", nil)
ctx := context.WithValue(req.Context(), mw.ContextKey{}, token) ctx := core.SetClaims(req.Context(), claims)
req = req.WithContext(ctx) req = req.WithContext(ctx)
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
@@ -209,28 +211,35 @@ func TestAuthMiddleware_Handler_APIKeyErrorHandling(t *testing.T) {
assert.Contains(t, rec.Body.String(), "Invalid API Key format") assert.Contains(t, rec.Body.String(), "Invalid API Key format")
} }
func TestAuthMiddleware_Handler_JWTErrorHandling(t *testing.T) { func TestAuthMiddleware_Handler_JWTMissingClaims(t *testing.T) {
// Setup // Setup
mockCache := new(MockCache) mockCache := new(MockCache)
authMiddleware := NewAuth(mockCache) authMiddleware := NewAuth(mockCache)
// The middleware passes the plaintext API key (cache handles hashing)
mockCache.On("OrganizationByAPIKey", "").Return(nil)
// Create a test handler that checks the context
var capturedUser string
testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if user := r.Context().Value(UserKey); user != nil {
if u, ok := user.(string); ok {
capturedUser = u
}
}
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
}) })
// Create request with invalid JWT token type in context // Create request without JWT claims - user should not be set
req := httptest.NewRequest(http.MethodGet, "/test", nil) req := httptest.NewRequest(http.MethodGet, "/test", nil)
ctx := context.WithValue(req.Context(), mw.ContextKey{}, "not-a-token") // Invalid type
req = req.WithContext(ctx)
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
// Execute // Execute
authMiddleware.Handler(testHandler).ServeHTTP(rec, req) authMiddleware.Handler(testHandler).ServeHTTP(rec, req)
// Assert // Assert
assert.Equal(t, http.StatusInternalServerError, rec.Code) assert.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "Invalid JWT token format") assert.Empty(t, capturedUser, "User should not be set when no claims in context")
} }
func TestAuthMiddleware_Handler_BothJWTAndAPIKey(t *testing.T) { func TestAuthMiddleware_Handler_BothJWTAndAPIKey(t *testing.T) {
@@ -249,9 +258,11 @@ func TestAuthMiddleware_Handler_BothJWTAndAPIKey(t *testing.T) {
userID := "user-123" userID := "user-123"
apiKey := "test-api-key-123" apiKey := "test-api-key-123"
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ claims := &validator.ValidatedClaims{
"sub": userID, RegisteredClaims: validator.RegisteredClaims{
}) Subject: userID,
},
}
// Mock expects plaintext key (cache handles hashing internally) // Mock expects plaintext key (cache handles hashing internally)
mockCache.On("OrganizationByAPIKey", apiKey).Return(expectedOrg) mockCache.On("OrganizationByAPIKey", apiKey).Return(expectedOrg)
@@ -273,9 +284,9 @@ func TestAuthMiddleware_Handler_BothJWTAndAPIKey(t *testing.T) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
}) })
// Create request with both JWT and API key in context // Create request with both JWT claims and API key in context
req := httptest.NewRequest(http.MethodGet, "/test", nil) req := httptest.NewRequest(http.MethodGet, "/test", nil)
ctx := context.WithValue(req.Context(), mw.ContextKey{}, token) ctx := core.SetClaims(req.Context(), claims)
ctx = context.WithValue(ctx, ApiKey, apiKey) ctx = context.WithValue(ctx, ApiKey, apiKey)
req = req.WithContext(ctx) req = req.WithContext(ctx)
@@ -427,7 +438,10 @@ func TestAuthMiddleware_Directive_RequiresBoth(t *testing.T) {
Name: "Test Org", Name: "Test Org",
} }
// Test with both present // When both user and organization are marked as acceptable,
// the directive uses OR logic - either one is sufficient
// Test with both present - should succeed
ctx := context.WithValue(context.Background(), UserKey, "user-123") ctx := context.WithValue(context.Background(), UserKey, "user-123")
ctx = context.WithValue(ctx, OrganizationKey, org) ctx = context.WithValue(ctx, OrganizationKey, org)
_, err := authMiddleware.Directive(ctx, nil, func(ctx context.Context) (interface{}, error) { _, err := authMiddleware.Directive(ctx, nil, func(ctx context.Context) (interface{}, error) {
@@ -435,19 +449,27 @@ func TestAuthMiddleware_Directive_RequiresBoth(t *testing.T) {
}, &requireUser, &requireOrg) }, &requireUser, &requireOrg)
assert.NoError(t, err) assert.NoError(t, err)
// Test with only user // Test with only user - should succeed (OR logic)
ctx = context.WithValue(context.Background(), UserKey, "user-123") ctx = context.WithValue(context.Background(), UserKey, "user-123")
_, err = authMiddleware.Directive(ctx, nil, func(ctx context.Context) (interface{}, error) { _, err = authMiddleware.Directive(ctx, nil, func(ctx context.Context) (interface{}, error) {
return "success", nil return "success", nil
}, &requireUser, &requireOrg) }, &requireUser, &requireOrg)
assert.Error(t, err) assert.NoError(t, err)
// Test with only organization // Test with only organization - should succeed (OR logic)
ctx = context.WithValue(context.Background(), OrganizationKey, org) ctx = context.WithValue(context.Background(), OrganizationKey, org)
_, err = authMiddleware.Directive(ctx, nil, func(ctx context.Context) (interface{}, error) { _, err = authMiddleware.Directive(ctx, nil, func(ctx context.Context) (interface{}, error) {
return "success", nil return "success", nil
}, &requireUser, &requireOrg) }, &requireUser, &requireOrg)
assert.NoError(t, err)
// Test with neither - should fail
ctx = context.Background()
_, err = authMiddleware.Directive(ctx, nil, func(ctx context.Context) (interface{}, error) {
return "success", nil
}, &requireUser, &requireOrg)
assert.Error(t, err) assert.Error(t, err)
assert.Contains(t, err.Error(), "authentication required")
} }
func TestAuthMiddleware_Directive_NoRequirements(t *testing.T) { func TestAuthMiddleware_Directive_NoRequirements(t *testing.T) {
@@ -462,3 +484,83 @@ func TestAuthMiddleware_Directive_NoRequirements(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "success", result) assert.Equal(t, "success", result)
} }
func TestUserHasRole_WithValidRole(t *testing.T) {
// Create claims with roles
claims := &validator.ValidatedClaims{
RegisteredClaims: validator.RegisteredClaims{
Subject: "user-123",
},
CustomClaims: &CustomClaims{
Roles: []string{"admin", "user"},
},
}
ctx := core.SetClaims(context.Background(), claims)
// Test for existing role
hasRole := UserHasRole(ctx, "admin")
assert.True(t, hasRole)
hasRole = UserHasRole(ctx, "user")
assert.True(t, hasRole)
}
func TestUserHasRole_WithoutRole(t *testing.T) {
// Create claims with roles
claims := &validator.ValidatedClaims{
RegisteredClaims: validator.RegisteredClaims{
Subject: "user-123",
},
CustomClaims: &CustomClaims{
Roles: []string{"user"},
},
}
ctx := core.SetClaims(context.Background(), claims)
// Test for non-existing role
hasRole := UserHasRole(ctx, "admin")
assert.False(t, hasRole)
}
func TestUserHasRole_WithoutRolesClaim(t *testing.T) {
// Create claims without custom claims
claims := &validator.ValidatedClaims{
RegisteredClaims: validator.RegisteredClaims{
Subject: "user-123",
},
}
ctx := core.SetClaims(context.Background(), claims)
// Test should return false when custom claims is missing
hasRole := UserHasRole(ctx, "admin")
assert.False(t, hasRole)
}
func TestUserHasRole_WithoutClaims(t *testing.T) {
ctx := context.Background()
// Test should return false when no claims in context
hasRole := UserHasRole(ctx, "admin")
assert.False(t, hasRole)
}
func TestUserHasRole_WithEmptyRoles(t *testing.T) {
// Create claims with empty roles
claims := &validator.ValidatedClaims{
RegisteredClaims: validator.RegisteredClaims{
Subject: "user-123",
},
CustomClaims: &CustomClaims{
Roles: []string{},
},
}
ctx := core.SetClaims(context.Background(), claims)
// Test should return false when roles array is empty
hasRole := UserHasRole(ctx, "admin")
assert.False(t, hasRole)
}
+1 -1
View File
@@ -9,7 +9,7 @@
"kubernetes" "kubernetes"
], ],
"matchPackageNames": [ "matchPackageNames": [
"registry.gitlab.com/unboundsoftware/schemas" "oci.unbound.se/unboundsoftware/schemas"
], ],
"enabled": false "enabled": false
}, },
+5 -3
View File
@@ -1,6 +1,7 @@
package sdlmerge package sdlmerge
import ( import (
"bytes"
"fmt" "fmt"
"strings" "strings"
@@ -61,12 +62,13 @@ func MergeSDLs(SDLs ...string) (string, error) {
return "", fmt.Errorf("merge ast: %w", err) return "", fmt.Errorf("merge ast: %w", err)
} }
out, err := astprinter.PrintString(&doc) // Format with indentation for better readability
if err != nil { buf := &bytes.Buffer{}
if err := astprinter.PrintIndent(&doc, []byte(" "), buf); err != nil {
return "", fmt.Errorf("stringify schema: %w", err) return "", fmt.Errorf("stringify schema: %w", err)
} }
return out, nil return buf.String(), nil
} }
func validateSubgraphs(subgraphs []string) error { func validateSubgraphs(subgraphs []string) error {
+434
View File
@@ -0,0 +1,434 @@
package sdlmerge
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMergeSDLs_Success(t *testing.T) {
// Both types need to be in the same subgraph or properly federated
sdl1 := `
type User {
id: ID!
name: String!
}
type Post {
id: ID!
title: String!
}
`
result, err := MergeSDLs(sdl1)
require.NoError(t, err)
assert.Contains(t, result, "User")
assert.Contains(t, result, "Post")
assert.Contains(t, result, "id")
assert.Contains(t, result, "name")
assert.Contains(t, result, "title")
}
func TestMergeSDLs_SingleSchema(t *testing.T) {
sdl := `
type Query {
hello: String
}
`
result, err := MergeSDLs(sdl)
require.NoError(t, err)
assert.Contains(t, result, "Query")
assert.Contains(t, result, "hello")
}
func TestMergeSDLs_EmptySchemas(t *testing.T) {
result, err := MergeSDLs()
require.NoError(t, err)
// With no schemas, result will be empty after processing
// This is valid - just verifies no crash
_ = result
}
func TestMergeSDLs_InvalidSyntax(t *testing.T) {
invalidSDL := `
type User {
id: ID!
name: String!
// Missing closing brace
`
_, err := MergeSDLs(invalidSDL)
require.Error(t, err)
assert.Contains(t, err.Error(), "parse graphql document string")
}
func TestMergeSDLs_UnknownType(t *testing.T) {
sdl := `
type User {
id: ID!
profile: UnknownType
}
`
_, err := MergeSDLs(sdl)
require.Error(t, err)
assert.Contains(t, err.Error(), "validate schema")
}
func TestMergeSDLs_DuplicateTypes_DifferentFields(t *testing.T) {
// Same type with different fields in different subgraphs - should fail
// In federation, shared types must be identical
sdl1 := `
type User {
id: ID!
}
`
sdl2 := `
type User {
name: String!
}
`
_, err := MergeSDLs(sdl1, sdl2)
require.Error(t, err)
assert.Contains(t, err.Error(), "shared type")
}
func TestMergeSDLs_ExtendType(t *testing.T) {
sdl1 := `
type User {
id: ID!
}
`
sdl2 := `
extend type User {
email: String!
}
`
result, err := MergeSDLs(sdl1, sdl2)
require.NoError(t, err)
assert.Contains(t, result, "User")
assert.Contains(t, result, "id")
assert.Contains(t, result, "email")
}
func TestMergeSDLs_Scalars(t *testing.T) {
sdl := `
scalar DateTime
type Event {
id: ID!
createdAt: DateTime!
}
`
result, err := MergeSDLs(sdl)
require.NoError(t, err)
assert.Contains(t, result, "DateTime")
assert.Contains(t, result, "Event")
}
func TestMergeSDLs_Enums(t *testing.T) {
sdl := `
enum Role {
ADMIN
USER
GUEST
}
type User {
id: ID!
role: Role!
}
`
result, err := MergeSDLs(sdl)
require.NoError(t, err)
assert.Contains(t, result, "Role")
assert.Contains(t, result, "ADMIN")
assert.Contains(t, result, "USER")
}
func TestMergeSDLs_Interfaces(t *testing.T) {
sdl := `
interface Node {
id: ID!
}
type User implements Node {
id: ID!
name: String!
}
`
result, err := MergeSDLs(sdl)
require.NoError(t, err)
assert.Contains(t, result, "Node")
assert.Contains(t, result, "implements")
}
func TestMergeSDLs_Unions(t *testing.T) {
sdl := `
type User {
id: ID!
name: String!
}
type Bot {
id: ID!
version: String!
}
union Actor = User | Bot
`
result, err := MergeSDLs(sdl)
require.NoError(t, err)
assert.Contains(t, result, "Actor")
assert.Contains(t, result, "User")
assert.Contains(t, result, "Bot")
}
func TestMergeSDLs_InputTypes(t *testing.T) {
sdl := `
input CreateUserInput {
name: String!
email: String!
}
type Mutation {
createUser(input: CreateUserInput!): User
}
type User {
id: ID!
name: String!
}
`
result, err := MergeSDLs(sdl)
require.NoError(t, err)
assert.Contains(t, result, "CreateUserInput")
assert.Contains(t, result, "createUser")
}
func TestMergeSDLs_Directives(t *testing.T) {
sdl := `
type User {
id: ID!
name: String! @deprecated(reason: "Use fullName instead")
fullName: String!
}
`
result, err := MergeSDLs(sdl)
require.NoError(t, err)
assert.Contains(t, result, "User")
assert.Contains(t, result, "name")
assert.Contains(t, result, "fullName")
}
func TestMergeSDLs_FederationKeys(t *testing.T) {
// Federation @key directive
sdl := `
type User @key(fields: "id") {
id: ID!
name: String!
}
`
result, err := MergeSDLs(sdl)
require.NoError(t, err)
assert.Contains(t, result, "User")
// @key directive should be removed during merge
assert.NotContains(t, result, "@key")
}
func TestMergeSDLs_ExternalFields(t *testing.T) {
// Federation @external directive
sdl1 := `
type User @key(fields: "id") {
id: ID!
name: String!
}
`
sdl2 := `
extend type User @key(fields: "id") {
id: ID! @external
posts: [Post!]!
}
type Post {
id: ID!
title: String!
}
`
result, err := MergeSDLs(sdl1, sdl2)
require.NoError(t, err)
assert.Contains(t, result, "User")
assert.Contains(t, result, "Post")
// @external fields should be removed
assert.NotContains(t, result, "@external")
}
func TestMergeSDLs_ComplexSchema(t *testing.T) {
// Multiple subgraphs with various types - simplified to avoid cross-references
users := `
type User @key(fields: "id") {
id: ID!
username: String!
email: String!
}
type Query {
user(id: ID!): User
users: [User!]!
}
`
posts := `
extend type User @key(fields: "id") {
id: ID! @external
posts: [Post!]!
}
type Post @key(fields: "id") {
id: ID!
title: String!
content: String!
}
extend type Query {
post(id: ID!): Post
posts: [Post!]!
}
`
comments := `
extend type Post @key(fields: "id") {
id: ID! @external
comments: [Comment!]!
}
type Comment {
id: ID!
text: String!
}
extend type Query {
comment(id: ID!): Comment
}
`
result, err := MergeSDLs(users, posts, comments)
require.NoError(t, err)
// Verify all types are present
assert.Contains(t, result, "User")
assert.Contains(t, result, "Post")
assert.Contains(t, result, "Comment")
assert.Contains(t, result, "Query")
// Verify fields from all subgraphs
assert.Contains(t, result, "username")
assert.Contains(t, result, "posts")
assert.Contains(t, result, "comments")
}
func TestMergeSDLs_EmptyTypeDefinition(t *testing.T) {
sdl := `
type Empty {}
`
_, err := MergeSDLs(sdl)
require.Error(t, err)
// Empty types are invalid in GraphQL
assert.Contains(t, err.Error(), "empty body")
}
func TestMergeSDLs_MultipleValidationErrors(t *testing.T) {
// Schema with multiple errors
sdl := `
type User {
id: ID!
profile: NonExistentType1
settings: NonExistentType2
}
`
_, err := MergeSDLs(sdl)
require.Error(t, err)
}
func TestMergeSDLs_ListTypes(t *testing.T) {
sdl := `
type User {
id: ID!
tags: [String!]!
friends: [User!]
}
`
result, err := MergeSDLs(sdl)
require.NoError(t, err)
assert.Contains(t, result, "User")
assert.Contains(t, result, "tags")
assert.Contains(t, result, "friends")
}
func TestMergeSDLs_NonNullTypes(t *testing.T) {
sdl := `
type User {
id: ID!
name: String!
email: String
}
`
result, err := MergeSDLs(sdl)
require.NoError(t, err)
assert.Contains(t, result, "User")
assert.Contains(t, result, "id")
assert.Contains(t, result, "name")
assert.Contains(t, result, "email")
}
func TestMergeSDLs_Comments(t *testing.T) {
sdl := `
# This is a user type
type User {
# User ID
id: ID!
# User name
name: String!
}
`
result, err := MergeSDLs(sdl)
require.NoError(t, err)
assert.Contains(t, result, "User")
}
func TestMergeSDLs_LargeSchema(t *testing.T) {
// Test with a reasonably large schema to ensure performance
var sdlBuilder strings.Builder
for i := 0; i < 50; i++ {
sdlBuilder.WriteString("type Type")
sdlBuilder.WriteString(strings.Repeat(string(rune('A'+i%26)), 1))
sdlBuilder.WriteString(string(rune('0' + i/26)))
sdlBuilder.WriteString(" { id: ID }\n")
}
result, err := MergeSDLs(sdlBuilder.String())
require.NoError(t, err)
// Verify some types are present
assert.Contains(t, result, "TypeA0")
assert.Contains(t, result, "TypeB0")
assert.Contains(t, result, "TypeC0")
}