Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 554a6c252f | |||
| 504f40902e | |||
| e943f08746 | |||
| 264b818f92 | |||
| f2a0fb42f9 | |||
| 90a1894c16 | |||
| ee2107b525 | |||
| 6e661f7bd1 | |||
| 6714ced6de | |||
| fcbf357617 | |||
| 38dd3fe879 | |||
| 01fb97e5b1 | |||
| f05b808980 | |||
| 02c0b909da | |||
| 6f5c3d68c4 | |||
| ff3197c4e3 | |||
| 10552d5486 | |||
| c60e23a5a0 | |||
| d187d50a4c | |||
| 3511c79acd | |||
| e4a300a0da | |||
| 5a83e2bf39 | |||
| 3407383f1f | |||
| fda1e2d2fd | |||
| 2dbfc81000 | |||
| d72a2f5024 | |||
| 3bce4ac85f | |||
| 74ed79c342 | |||
| e26e158e68 | |||
| 74d73ce78f | |||
| 43992ff3b6 | |||
| e11102baf7 | |||
| 64a39df110 | |||
| 28a5fdf48e | |||
| 19a9ffabb1 | |||
| ed63264241 | |||
| 47ae698135 | |||
| 9909aa726e | |||
| d3ec947991 | |||
| bd34c505ab | |||
| 7bcef10b38 | |||
| 98a679d2d3 | |||
| 80d3c44cb0 | |||
| 6541016676 | |||
| d524305bb5 | |||
| b8a8783838 | |||
| 577ad601ca | |||
| c5e43bd0a9 | |||
| 27c78d8070 | |||
| 658305e60a | |||
| 93162e03d7 | |||
| 66a29713cf | |||
| d23423c8dc | |||
| 6b454ca8bf | |||
| 8a2410fae6 | |||
| b1124d6350 | |||
| eb41e24002 |
@@ -0,0 +1,9 @@
|
|||||||
|
.gitignore
|
||||||
|
/.gitlab
|
||||||
|
.gitlab-ci.yml
|
||||||
|
.graphqlconfig
|
||||||
|
/exported
|
||||||
|
/k8s
|
||||||
|
/release
|
||||||
|
.envrc
|
||||||
|
README.md
|
||||||
@@ -4,3 +4,4 @@
|
|||||||
coverage.html
|
coverage.html
|
||||||
/exported
|
/exported
|
||||||
/release
|
/release
|
||||||
|
/schemactl
|
||||||
|
|||||||
+17
-3
@@ -16,7 +16,21 @@ variables:
|
|||||||
.buildtools:
|
.buildtools:
|
||||||
image: buildtool/build-tools:${BUILDTOOLS_VERSION}
|
image: buildtool/build-tools:${BUILDTOOLS_VERSION}
|
||||||
services:
|
services:
|
||||||
- docker:dind
|
- docker:${DOCKER_DIND_VERSION}
|
||||||
|
|
||||||
|
run-pre-commit:
|
||||||
|
stage: .pre
|
||||||
|
image: unbound/pre-commit
|
||||||
|
variables:
|
||||||
|
PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
|
||||||
|
cache:
|
||||||
|
- key:
|
||||||
|
files:
|
||||||
|
- .pre-commit-config.yaml
|
||||||
|
paths:
|
||||||
|
- ${PRE_COMMIT_HOME}
|
||||||
|
script:
|
||||||
|
- pre-commit run --all-files
|
||||||
|
|
||||||
build:
|
build:
|
||||||
extends: .buildtools
|
extends: .buildtools
|
||||||
@@ -30,7 +44,7 @@ build:
|
|||||||
|
|
||||||
vulnerabilities:
|
vulnerabilities:
|
||||||
stage: build
|
stage: build
|
||||||
image: golang:1.19.2
|
image: golang:1.20.3
|
||||||
script:
|
script:
|
||||||
- go install golang.org/x/vuln/cmd/govulncheck@latest
|
- go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||||
- govulncheck ./...
|
- govulncheck ./...
|
||||||
@@ -51,7 +65,7 @@ release:
|
|||||||
stage: release
|
stage: release
|
||||||
image: docker:stable
|
image: docker:stable
|
||||||
services:
|
services:
|
||||||
- docker:dind
|
- docker:${DOCKER_DIND_VERSION}
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
GORELEASER_IMAGE: goreleaser/goreleaser:v1.11.5-amd64
|
GORELEASER_IMAGE: goreleaser/goreleaser:v1.11.5-amd64
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "gomod"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
open-pull-requests-limit: 5
|
||||||
|
rebase-strategy: none
|
||||||
|
- package-ecosystem: "docker"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
open-pull-requests-limit: 5
|
||||||
|
rebase-strategy: none
|
||||||
+14
-6
@@ -2,7 +2,7 @@
|
|||||||
# See https://pre-commit.com/hooks.html for more hooks
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.3.0
|
rev: v4.4.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
@@ -18,19 +18,27 @@ repos:
|
|||||||
rev: v1.0.3
|
rev: v1.0.3
|
||||||
hooks:
|
hooks:
|
||||||
- id: gitlab-ci-linter
|
- 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.1.0
|
rev: v9.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: commitlint
|
- id: commitlint
|
||||||
stages: [ commit-msg ]
|
stages: [ commit-msg ]
|
||||||
additional_dependencies: [ '@commitlint/config-conventional' ]
|
additional_dependencies: [ '@commitlint/config-conventional' ]
|
||||||
|
- repo: https://github.com/dnephin/pre-commit-golang
|
||||||
|
rev: v0.5.1
|
||||||
|
hooks:
|
||||||
|
- id: go-mod-tidy
|
||||||
|
- id: go-imports
|
||||||
|
args:
|
||||||
|
- -local
|
||||||
|
- gitlab.com/unboundsoftware/schemas
|
||||||
- repo: https://github.com/lietu/go-pre-commit
|
- repo: https://github.com/lietu/go-pre-commit
|
||||||
rev: v0.0.1
|
rev: v0.0.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: errcheck
|
|
||||||
- id: go-fmt-goimports
|
|
||||||
- id: go-test
|
- id: go-test
|
||||||
- id: go-vet
|
|
||||||
- id: golangci-lint
|
- id: golangci-lint
|
||||||
- id: gofumpt
|
- id: gofumpt
|
||||||
- id: staticcheck
|
exclude: '^graph/generated/.*$|^graph/model/models_gen.go|^tools/.*$$'
|
||||||
|
|||||||
+7
-5
@@ -1,12 +1,14 @@
|
|||||||
FROM golang:1.19.2 as build
|
FROM golang:1.20.3 as modules
|
||||||
|
WORKDIR /build
|
||||||
|
ADD go.* /build
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
FROM modules as build
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
ENV CGO_ENABLED=0
|
ENV CGO_ENABLED=0
|
||||||
ADD . /build
|
ADD . /build
|
||||||
RUN if [ $(go mod tidy -v 2>&1 | grep -c unused) != 0 ]; then echo "Unused modules, please run 'go mod tidy'"; exit 1; fi
|
|
||||||
RUN go fmt ./...
|
|
||||||
RUN go vet ./...
|
|
||||||
RUN CGO_ENABLED=1 go test -mod=readonly -race -coverprofile=coverage.txt.tmp -covermode=atomic -coverpkg=$(go list ./... | tr '\n' , | sed 's/,$//') ./...
|
RUN CGO_ENABLED=1 go test -mod=readonly -race -coverprofile=coverage.txt.tmp -covermode=atomic -coverpkg=$(go list ./... | tr '\n' , | sed 's/,$//') ./...
|
||||||
RUN ["/bin/bash", "-c", "cat coverage.txt.tmp | grep -v testing.go | grep -v -f <(find . -type f | xargs grep -l 'Code generated') > coverage.txt"]
|
RUN ["/bin/bash", "-c", "cat coverage.txt.tmp | grep -v testing.go | grep -v -f <(find . -type f | xargs grep -l 'Code generated by github.com/99designs/gqlgen, DO NOT EDIT') > coverage.txt"]
|
||||||
RUN go tool cover -html=coverage.txt -o coverage.html
|
RUN go tool cover -html=coverage.txt -o coverage.html
|
||||||
RUN go tool cover -func=coverage.txt
|
RUN go tool cover -func=coverage.txt
|
||||||
RUN rm coverage.txt.tmp
|
RUN rm coverage.txt.tmp
|
||||||
|
|||||||
Vendored
+113
-26
@@ -2,57 +2,144 @@ package cache
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/sparetimecoders/goamqp"
|
"github.com/sparetimecoders/goamqp"
|
||||||
|
|
||||||
"gitlab.com/unboundsoftware/schemas/domain"
|
"gitlab.com/unboundsoftware/schemas/domain"
|
||||||
|
"gitlab.com/unboundsoftware/schemas/hash"
|
||||||
)
|
)
|
||||||
|
|
||||||
const subGraphKey = "%s<->%s"
|
|
||||||
|
|
||||||
type Cache struct {
|
type Cache struct {
|
||||||
services map[string]map[string]struct{}
|
organizations map[string]domain.Organization
|
||||||
subGraphs map[string]string
|
users map[string][]string
|
||||||
logger log.Interface
|
apiKeys map[string]domain.APIKey
|
||||||
|
services map[string]map[string]map[string]struct{}
|
||||||
|
subGraphs map[string]string
|
||||||
|
lastUpdate map[string]string
|
||||||
|
logger log.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) Services(ref string) []string {
|
func (c *Cache) OrganizationByAPIKey(apiKey string) *domain.Organization {
|
||||||
var services []string
|
key, exists := c.apiKeys[apiKey]
|
||||||
for k := range c.services[ref] {
|
if !exists {
|
||||||
services = append(services, k)
|
return nil
|
||||||
}
|
}
|
||||||
return services
|
org, exists := c.organizations[key.OrganizationId]
|
||||||
|
if !exists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &org
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) SubGraphId(ref, service string) string {
|
func (c *Cache) OrganizationsByUser(sub string) []domain.Organization {
|
||||||
return c.subGraphs[fmt.Sprintf(subGraphKey, ref, service)]
|
orgIds := c.users[sub]
|
||||||
|
orgs := make([]domain.Organization, len(orgIds))
|
||||||
|
for i, id := range orgIds {
|
||||||
|
orgs[i] = c.organizations[id]
|
||||||
|
}
|
||||||
|
return orgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) ApiKeyByKey(key string) *domain.APIKey {
|
||||||
|
k, exists := c.apiKeys[hash.String(key)]
|
||||||
|
if !exists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &k
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Services(orgId, ref, lastUpdate string) ([]string, string) {
|
||||||
|
key := refKey(orgId, ref)
|
||||||
|
var services []string
|
||||||
|
if lastUpdate == "" || c.lastUpdate[key] > lastUpdate {
|
||||||
|
for k := range c.services[orgId][ref] {
|
||||||
|
services = append(services, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return services, c.lastUpdate[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) SubGraphId(orgId, ref, service string) string {
|
||||||
|
return c.subGraphs[subGraphKey(orgId, ref, service)]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) Update(msg any, _ goamqp.Headers) (any, error) {
|
func (c *Cache) Update(msg any, _ goamqp.Headers) (any, error) {
|
||||||
switch m := msg.(type) {
|
switch m := msg.(type) {
|
||||||
|
case *domain.OrganizationAdded:
|
||||||
|
o := domain.Organization{}
|
||||||
|
m.UpdateOrganization(&o)
|
||||||
|
c.organizations[m.ID.String()] = o
|
||||||
|
c.addUser(m.Initiator, o)
|
||||||
|
case *domain.APIKeyAdded:
|
||||||
|
key := domain.APIKey{
|
||||||
|
Name: m.Name,
|
||||||
|
OrganizationId: m.OrganizationId,
|
||||||
|
Key: m.Key,
|
||||||
|
Refs: m.Refs,
|
||||||
|
Read: m.Read,
|
||||||
|
Publish: m.Publish,
|
||||||
|
CreatedBy: m.Initiator,
|
||||||
|
CreatedAt: m.When(),
|
||||||
|
}
|
||||||
|
c.apiKeys[m.Key] = key
|
||||||
|
org := c.organizations[m.OrganizationId]
|
||||||
|
org.APIKeys = append(org.APIKeys, key)
|
||||||
|
c.organizations[m.OrganizationId] = org
|
||||||
case *domain.SubGraphUpdated:
|
case *domain.SubGraphUpdated:
|
||||||
if _, exists := c.services[m.Ref]; !exists {
|
c.updateSubGraph(m.OrganizationId, m.Ref, m.ID.String(), m.Service, m.Time)
|
||||||
c.services[m.Ref] = make(map[string]struct{})
|
case *domain.Organization:
|
||||||
|
c.organizations[m.ID.String()] = *m
|
||||||
|
c.addUser(m.CreatedBy, *m)
|
||||||
|
for _, k := range m.APIKeys {
|
||||||
|
c.apiKeys[k.Key] = k
|
||||||
}
|
}
|
||||||
c.services[m.Ref][m.ID.String()] = struct{}{}
|
|
||||||
c.subGraphs[fmt.Sprintf(subGraphKey, m.Ref, m.Service)] = m.ID.String()
|
|
||||||
case *domain.SubGraph:
|
case *domain.SubGraph:
|
||||||
if _, exists := c.services[m.Ref]; !exists {
|
c.updateSubGraph(m.OrganizationId, m.Ref, m.ID.String(), m.Service, m.ChangedAt)
|
||||||
c.services[m.Ref] = make(map[string]struct{})
|
|
||||||
}
|
|
||||||
c.services[m.Ref][m.ID.String()] = struct{}{}
|
|
||||||
c.subGraphs[fmt.Sprintf(subGraphKey, m.Ref, m.Service)] = m.ID.String()
|
|
||||||
default:
|
default:
|
||||||
c.logger.Warnf("unexpected message received: %+v", msg)
|
c.logger.Warnf("unexpected message received: %+v", msg)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(logger log.Interface) *Cache {
|
func (c *Cache) updateSubGraph(orgId string, ref string, subGraphId string, service string, updated time.Time) {
|
||||||
return &Cache{
|
if _, exists := c.services[orgId]; !exists {
|
||||||
subGraphs: make(map[string]string),
|
c.services[orgId] = make(map[string]map[string]struct{})
|
||||||
services: make(map[string]map[string]struct{}),
|
}
|
||||||
logger: logger,
|
if _, exists := c.services[orgId][ref]; !exists {
|
||||||
|
c.services[orgId][ref] = make(map[string]struct{})
|
||||||
|
}
|
||||||
|
c.services[orgId][ref][subGraphId] = struct{}{}
|
||||||
|
c.subGraphs[subGraphKey(orgId, ref, service)] = subGraphId
|
||||||
|
c.lastUpdate[refKey(orgId, ref)] = updated.Format(time.RFC3339Nano)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) addUser(sub string, organization domain.Organization) {
|
||||||
|
user, exists := c.users[sub]
|
||||||
|
if !exists {
|
||||||
|
c.users[sub] = []string{organization.ID.String()}
|
||||||
|
} else {
|
||||||
|
c.users[sub] = append(user, organization.ID.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func New(logger log.Interface) *Cache {
|
||||||
|
return &Cache{
|
||||||
|
organizations: make(map[string]domain.Organization),
|
||||||
|
users: make(map[string][]string),
|
||||||
|
apiKeys: make(map[string]domain.APIKey),
|
||||||
|
services: make(map[string]map[string]map[string]struct{}),
|
||||||
|
subGraphs: make(map[string]string),
|
||||||
|
lastUpdate: make(map[string]string),
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func refKey(orgId string, ref string) string {
|
||||||
|
return fmt.Sprintf("%s<->%s", orgId, ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
func subGraphKey(orgId string, ref string, service string) string {
|
||||||
|
return fmt.Sprintf("%s<->%s<->%s", orgId, ref, service)
|
||||||
|
}
|
||||||
|
|||||||
+140
-31
@@ -9,12 +9,14 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/99designs/gqlgen/graphql/handler"
|
"github.com/99designs/gqlgen/graphql/handler"
|
||||||
"github.com/99designs/gqlgen/graphql/playground"
|
"github.com/99designs/gqlgen/graphql/playground"
|
||||||
"github.com/alecthomas/kong"
|
"github.com/alecthomas/kong"
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"github.com/apex/log/handlers/json"
|
"github.com/apex/log/handlers/json"
|
||||||
|
"github.com/getsentry/sentry-go"
|
||||||
sentryhttp "github.com/getsentry/sentry-go/http"
|
sentryhttp "github.com/getsentry/sentry-go/http"
|
||||||
"github.com/rs/cors"
|
"github.com/rs/cors"
|
||||||
"github.com/sparetimecoders/goamqp"
|
"github.com/sparetimecoders/goamqp"
|
||||||
@@ -30,21 +32,31 @@ import (
|
|||||||
"gitlab.com/unboundsoftware/schemas/store"
|
"gitlab.com/unboundsoftware/schemas/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
var CLI struct {
|
type CLI struct {
|
||||||
AmqpURL string `name:"amqp-url" env:"AMQP_URL" help:"URL to use to connect to RabbitMQ" default:"amqp://user:password@localhost:5672/"`
|
AmqpURL string `name:"amqp-url" env:"AMQP_URL" help:"URL to use to connect to RabbitMQ" default:"amqp://user:password@localhost:5672/"`
|
||||||
Port int `name:"port" env:"PORT" help:"Listen-port for GraphQL API" default:"8080"`
|
Port int `name:"port" env:"PORT" help:"Listen-port for GraphQL API" default:"8080"`
|
||||||
APIKey string `name:"api-key" env:"API_KEY" help:"The API-key that is required"`
|
|
||||||
LogLevel string `name:"log-level" env:"LOG_LEVEL" help:"The level of logging to use (debug, info, warn, error, fatal)" default:"info"`
|
LogLevel string `name:"log-level" env:"LOG_LEVEL" help:"The level of logging to use (debug, info, warn, error, fatal)" default:"info"`
|
||||||
DatabaseURL string `name:"postgres-url" env:"POSTGRES_URL" help:"URL to use to connect to Postgres" default:"postgres://postgres:postgres@:5432/schemas?sslmode=disable"`
|
DatabaseURL string `name:"postgres-url" env:"POSTGRES_URL" help:"URL to use to connect to Postgres" default:"postgres://postgres:postgres@:5432/schemas?sslmode=disable"`
|
||||||
DatabaseDriverName string `name:"db-driver" env:"DB_DRIVER" help:"Driver to use to connect to db" default:"postgres"`
|
DatabaseDriverName string `name:"db-driver" env:"DB_DRIVER" help:"Driver to use to connect to db" default:"postgres"`
|
||||||
|
Issuer string `name:"issuer" env:"ISSUER" help:"The JWT token issuer to use" default:"unbound.eu.auth0.com"`
|
||||||
|
StrictSSL bool `name:"strict-ssl" env:"STRICT_SSL" help:"Should strict SSL handling be enabled" default:"true"`
|
||||||
|
SentryConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SentryConfig struct {
|
||||||
|
DSN string `name:"sentry-dsn" env:"SENTRY_DSN" help:"Sentry dsn" default:""`
|
||||||
|
Environment string `name:"sentry-environment" env:"SENTRY_ENVIRONMENT" help:"Sentry environment" default:"development"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var buildVersion = "none"
|
||||||
|
|
||||||
const serviceName = "schemas"
|
const serviceName = "schemas"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
_ = kong.Parse(&CLI)
|
var cli CLI
|
||||||
|
_ = kong.Parse(&cli)
|
||||||
log.SetHandler(json.New(os.Stdout))
|
log.SetHandler(json.New(os.Stdout))
|
||||||
log.SetLevelFromString(CLI.LogLevel)
|
log.SetLevelFromString(cli.LogLevel)
|
||||||
logger := log.WithField("service", serviceName)
|
logger := log.WithField("service", serviceName)
|
||||||
closeEvents := make(chan error)
|
closeEvents := make(chan error)
|
||||||
|
|
||||||
@@ -52,67 +64,86 @@ func main() {
|
|||||||
closeEvents,
|
closeEvents,
|
||||||
logger,
|
logger,
|
||||||
ConnectAMQP,
|
ConnectAMQP,
|
||||||
|
cli,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
logger.WithError(err).Error("process error")
|
logger.WithError(err).Error("process error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func start(closeEvents chan error, logger *log.Entry, connectToAmqpFunc func(url string) (Connection, error)) error {
|
func start(closeEvents chan error, logger *log.Entry, connectToAmqpFunc func(url string) (Connection, error), cli CLI) error {
|
||||||
|
if err := setupSentry(logger, cli.SentryConfig); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer sentry.Flush(2 * time.Second)
|
||||||
|
|
||||||
rootCtx, rootCancel := context.WithCancel(context.Background())
|
rootCtx, rootCancel := context.WithCancel(context.Background())
|
||||||
defer rootCancel()
|
defer rootCancel()
|
||||||
|
|
||||||
db, err := store.SetupDB(CLI.DatabaseDriverName, CLI.DatabaseURL)
|
db, err := store.SetupDB(cli.DatabaseDriverName, cli.DatabaseURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to setup DB: %v", err)
|
return fmt.Errorf("failed to setup DB: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
eventStore, err := pg.New(
|
eventStore, err := pg.New(
|
||||||
|
rootCtx,
|
||||||
db.DB,
|
db.DB,
|
||||||
pg.WithEventTypes(
|
pg.WithEventTypes(
|
||||||
&domain.SubGraphUpdated{},
|
&domain.SubGraphUpdated{},
|
||||||
|
&domain.OrganizationAdded{},
|
||||||
|
&domain.APIKeyAdded{},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create eventstore: %v", err)
|
return fmt.Errorf("failed to create eventstore: %v", err)
|
||||||
}
|
}
|
||||||
eventPublisher, err := goamqp.NewPublisher(
|
|
||||||
|
if err := store.RunEventStoreMigrations(db); err != nil {
|
||||||
|
return fmt.Errorf("event migrations: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
publisher, err := goamqp.NewPublisher(
|
||||||
goamqp.Route{
|
goamqp.Route{
|
||||||
Type: domain.SubGraphUpdated{},
|
Type: domain.SubGraphUpdated{},
|
||||||
Key: "SubGraph.Updated",
|
Key: "SubGraph.Updated",
|
||||||
},
|
},
|
||||||
|
goamqp.Route{
|
||||||
|
Type: domain.OrganizationAdded{},
|
||||||
|
Key: "Organization.Added",
|
||||||
|
},
|
||||||
|
goamqp.Route{
|
||||||
|
Type: domain.APIKeyAdded{},
|
||||||
|
Key: "Organization.APIKeyAdded",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create publisher: %v", err)
|
||||||
|
}
|
||||||
|
eventPublisher, err := amqp.New(publisher)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create event publisher: %v", err)
|
return fmt.Errorf("failed to create event publisher: %v", err)
|
||||||
}
|
}
|
||||||
amqp.New(eventPublisher)
|
conn, err := connectToAmqpFunc(cli.AmqpURL)
|
||||||
conn, err := connectToAmqpFunc(CLI.AmqpURL)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to connect to AMQP: %v", err)
|
return fmt.Errorf("failed to connect to AMQP: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceCache := cache.New(logger)
|
serviceCache := cache.New(logger)
|
||||||
roots, err := eventStore.GetAggregateRoots(reflect.TypeOf(domain.SubGraph{}))
|
if err := loadOrganizations(rootCtx, eventStore, serviceCache); err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("caching organizations: %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
for _, root := range roots {
|
if err := loadSubGraphs(rootCtx, eventStore, serviceCache); err != nil {
|
||||||
subGraph := &domain.SubGraph{BaseAggregate: eventsourced.BaseAggregateFromString(root.String())}
|
return fmt.Errorf("caching subgraphs: %w", err)
|
||||||
if _, err := eventsourced.NewHandler(subGraph, eventStore); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err := serviceCache.Update(subGraph, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
setups := []goamqp.Setup{
|
setups := []goamqp.Setup{
|
||||||
goamqp.UseLogger(logger.Errorf),
|
goamqp.UseLogger(logger.Errorf),
|
||||||
goamqp.CloseListener(closeEvents),
|
goamqp.CloseListener(closeEvents),
|
||||||
goamqp.WithPrefetchLimit(20),
|
goamqp.WithPrefetchLimit(20),
|
||||||
goamqp.EventStreamPublisher(eventPublisher),
|
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.APIKeyAdded", serviceCache.Update, domain.APIKeyAdded{}),
|
||||||
}
|
}
|
||||||
if err := conn.Start(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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +152,7 @@ func start(closeEvents chan error, logger *log.Entry, connectToAmqpFunc func(url
|
|||||||
logger.Info("Started")
|
logger.Info("Started")
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
httpSrv := &http.Server{Addr: fmt.Sprintf(":%d", CLI.Port), Handler: mux}
|
httpSrv := &http.Server{Addr: fmt.Sprintf(":%d", cli.Port), Handler: mux}
|
||||||
|
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
@@ -170,7 +201,7 @@ func start(closeEvents chan error, logger *log.Entry, connectToAmqpFunc func(url
|
|||||||
|
|
||||||
resolver := &graph.Resolver{
|
resolver := &graph.Resolver{
|
||||||
EventStore: eventStore,
|
EventStore: eventStore,
|
||||||
Publisher: amqp.New(eventPublisher),
|
Publisher: eventPublisher,
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
Cache: serviceCache,
|
Cache: serviceCache,
|
||||||
}
|
}
|
||||||
@@ -179,18 +210,28 @@ func start(closeEvents chan error, logger *log.Entry, connectToAmqpFunc func(url
|
|||||||
Resolvers: resolver,
|
Resolvers: resolver,
|
||||||
Complexity: generated.ComplexityRoot{},
|
Complexity: generated.ComplexityRoot{},
|
||||||
}
|
}
|
||||||
apiKeyMiddleware := middleware.NewApiKey(CLI.APIKey, logger)
|
apiKeyMiddleware := middleware.NewApiKey()
|
||||||
config.Directives.HasApiKey = apiKeyMiddleware.Directive
|
mw := middleware.NewAuth0("https://schemas.unbound.se", cli.Issuer, cli.StrictSSL)
|
||||||
|
authMiddleware := middleware.NewAuth(serviceCache)
|
||||||
|
config.Directives.Auth = authMiddleware.Directive
|
||||||
srv := handler.NewDefaultServer(generated.NewExecutableSchema(
|
srv := handler.NewDefaultServer(generated.NewExecutableSchema(
|
||||||
config,
|
config,
|
||||||
))
|
))
|
||||||
|
|
||||||
sentryHandler := sentryhttp.New(sentryhttp.Options{Repanic: true})
|
sentryHandler := sentryhttp.New(sentryhttp.Options{Repanic: true})
|
||||||
mux.Handle("/", sentryHandler.HandleFunc(playground.Handler("GraphQL playground", "/query")))
|
mux.Handle("/", sentryHandler.HandleFunc(playground.Handler("GraphQL playground", "/query")))
|
||||||
mux.Handle("/health", sentryHandler.HandleFunc(healthFunc))
|
mux.Handle("/health", http.HandlerFunc(healthFunc))
|
||||||
mux.Handle("/query", cors.AllowAll().Handler(sentryHandler.Handle(apiKeyMiddleware.Handler(srv))))
|
mux.Handle("/query", cors.AllowAll().Handler(
|
||||||
|
sentryHandler.Handle(
|
||||||
|
mw.Middleware().CheckJWT(
|
||||||
|
apiKeyMiddleware.Handler(
|
||||||
|
authMiddleware.Handler(srv),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
|
||||||
logger.Infof("connect to http://localhost:%d/ for GraphQL playground", CLI.Port)
|
logger.Infof("connect to http://localhost:%d/ for GraphQL playground", cli.Port)
|
||||||
|
|
||||||
if err := httpSrv.ListenAndServe(); err != nil {
|
if err := httpSrv.ListenAndServe(); err != nil {
|
||||||
logger.WithError(err).Error("listen http")
|
logger.WithError(err).Error("listen http")
|
||||||
@@ -202,15 +243,83 @@ func start(closeEvents chan error, logger *log.Entry, connectToAmqpFunc func(url
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadOrganizations(ctx context.Context, eventStore eventsourced.EventStore, serviceCache *cache.Cache) error {
|
||||||
|
roots, err := eventStore.GetAggregateRoots(ctx, reflect.TypeOf(domain.Organization{}))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, root := range roots {
|
||||||
|
organization := &domain.Organization{BaseAggregate: eventsourced.BaseAggregateFromString(root.String())}
|
||||||
|
if _, err := eventsourced.NewHandler(ctx, organization, eventStore); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := serviceCache.Update(organization, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadSubGraphs(ctx context.Context, eventStore eventsourced.EventStore, serviceCache *cache.Cache) error {
|
||||||
|
roots, err := eventStore.GetAggregateRoots(ctx, reflect.TypeOf(domain.SubGraph{}))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, root := range roots {
|
||||||
|
subGraph := &domain.SubGraph{BaseAggregate: eventsourced.BaseAggregateFromString(root.String())}
|
||||||
|
if _, err := eventsourced.NewHandler(ctx, subGraph, eventStore); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := serviceCache.Update(subGraph, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func healthFunc(w http.ResponseWriter, _ *http.Request) {
|
func healthFunc(w http.ResponseWriter, _ *http.Request) {
|
||||||
_, _ = w.Write([]byte("OK"))
|
_, _ = w.Write([]byte("OK"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupSentry(logger log.Interface, args SentryConfig) error {
|
||||||
|
if args.Environment == "" {
|
||||||
|
return fmt.Errorf("no Sentry environment supplied, exiting")
|
||||||
|
}
|
||||||
|
cfg := sentry.ClientOptions{
|
||||||
|
Dsn: args.DSN,
|
||||||
|
Environment: args.Environment,
|
||||||
|
Release: fmt.Sprintf("%s-%s", serviceName, buildVersion),
|
||||||
|
}
|
||||||
|
switch args.Environment {
|
||||||
|
case "development":
|
||||||
|
cfg.Debug = true
|
||||||
|
cfg.EnableTracing = false
|
||||||
|
cfg.TracesSampleRate = 0.0
|
||||||
|
case "production":
|
||||||
|
if args.DSN == "" {
|
||||||
|
return fmt.Errorf("no DSN supplied for non-dev environment, exiting")
|
||||||
|
}
|
||||||
|
cfg.Debug = false
|
||||||
|
cfg.EnableTracing = true
|
||||||
|
cfg.TracesSampleRate = 0.01
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("illegal environment %s", args.Environment)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sentry.Init(cfg); err != nil {
|
||||||
|
return fmt.Errorf("sentry setup: %w", err)
|
||||||
|
}
|
||||||
|
logger.Infof("configured Sentry for env: %s", args.Environment)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func ConnectAMQP(url string) (Connection, error) {
|
func ConnectAMQP(url string) (Connection, error) {
|
||||||
return goamqp.NewFromURL(serviceName, url)
|
return goamqp.NewFromURL(serviceName, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Connection interface {
|
type Connection interface {
|
||||||
Start(opts ...goamqp.Setup) error
|
Start(ctx context.Context, opts ...goamqp.Setup) error
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-6
@@ -1,9 +1,13 @@
|
|||||||
query SubGraphs($ref: String!) {
|
query SubGraphs($ref: String!) {
|
||||||
subGraphs(ref: $ref) {
|
supergraph(ref: $ref) {
|
||||||
service
|
... on SubGraphs {
|
||||||
url
|
subGraphs {
|
||||||
wsUrl
|
service
|
||||||
changedBy
|
url
|
||||||
changedAt
|
wsUrl
|
||||||
|
changedBy
|
||||||
|
changedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-9
@@ -2,6 +2,7 @@ package ctl
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
@@ -47,15 +48,19 @@ func List(apiKey, schemaRef, service string, schemasUrl url.URL) ([]*SubGraph, e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
subGraphs := make([]*SubGraph, len(response.SubGraphs))
|
var subGraphs []*SubGraph
|
||||||
for i, subGraph := range response.SubGraphs {
|
if s, ok := response.Supergraph.(*SubGraphsSupergraphSubGraphs); ok {
|
||||||
subGraphs[i] = &SubGraph{
|
subGraphs = make([]*SubGraph, len(s.SubGraphs))
|
||||||
Service: subGraph.Service,
|
for i, subGraph := range s.SubGraphs {
|
||||||
URL: subGraph.Url,
|
subGraphs[i] = &SubGraph{
|
||||||
WSUrl: subGraph.WsUrl,
|
Service: subGraph.Service,
|
||||||
ChangedBy: subGraph.ChangedBy,
|
URL: subGraph.Url,
|
||||||
ChangedAt: subGraph.ChangedAt,
|
WSUrl: subGraph.WsUrl,
|
||||||
|
ChangedBy: subGraph.ChangedBy,
|
||||||
|
ChangedAt: subGraph.ChangedAt,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return subGraphs, nil
|
||||||
}
|
}
|
||||||
return subGraphs, nil
|
return nil, fmt.Errorf("unexpected response type")
|
||||||
}
|
}
|
||||||
|
|||||||
+202
-35
@@ -4,6 +4,8 @@ package ctl
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Khan/genqlient/graphql"
|
"github.com/Khan/genqlient/graphql"
|
||||||
@@ -34,14 +36,160 @@ func (v *InputSubGraph) GetSdl() string { return v.Sdl }
|
|||||||
|
|
||||||
// SubGraphsResponse is returned by SubGraphs on success.
|
// SubGraphsResponse is returned by SubGraphs on success.
|
||||||
type SubGraphsResponse struct {
|
type SubGraphsResponse struct {
|
||||||
SubGraphs []*SubGraphsSubGraphsSubGraph `json:"subGraphs"`
|
Supergraph SubGraphsSupergraph `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSubGraphs returns SubGraphsResponse.SubGraphs, and is useful for accessing the field via an interface.
|
// GetSupergraph returns SubGraphsResponse.Supergraph, and is useful for accessing the field via an interface.
|
||||||
func (v *SubGraphsResponse) GetSubGraphs() []*SubGraphsSubGraphsSubGraph { return v.SubGraphs }
|
func (v *SubGraphsResponse) GetSupergraph() SubGraphsSupergraph { return v.Supergraph }
|
||||||
|
|
||||||
// SubGraphsSubGraphsSubGraph includes the requested fields of the GraphQL type SubGraph.
|
func (v *SubGraphsResponse) UnmarshalJSON(b []byte) error {
|
||||||
type SubGraphsSubGraphsSubGraph struct {
|
if string(b) == "null" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstPass struct {
|
||||||
|
*SubGraphsResponse
|
||||||
|
Supergraph json.RawMessage `json:"supergraph"`
|
||||||
|
graphql.NoUnmarshalJSON
|
||||||
|
}
|
||||||
|
firstPass.SubGraphsResponse = v
|
||||||
|
|
||||||
|
err := json.Unmarshal(b, &firstPass)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
dst := &v.Supergraph
|
||||||
|
src := firstPass.Supergraph
|
||||||
|
if len(src) != 0 && string(src) != "null" {
|
||||||
|
err = __unmarshalSubGraphsSupergraph(
|
||||||
|
src, dst)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"unable to unmarshal SubGraphsResponse.Supergraph: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type __premarshalSubGraphsResponse struct {
|
||||||
|
Supergraph json.RawMessage `json:"supergraph"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *SubGraphsResponse) MarshalJSON() ([]byte, error) {
|
||||||
|
premarshaled, err := v.__premarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return json.Marshal(premarshaled)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *SubGraphsResponse) __premarshalJSON() (*__premarshalSubGraphsResponse, error) {
|
||||||
|
var retval __premarshalSubGraphsResponse
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
dst := &retval.Supergraph
|
||||||
|
src := v.Supergraph
|
||||||
|
var err error
|
||||||
|
*dst, err = __marshalSubGraphsSupergraph(
|
||||||
|
&src)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"unable to marshal SubGraphsResponse.Supergraph: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &retval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubGraphsSupergraph includes the requested fields of the GraphQL interface Supergraph.
|
||||||
|
//
|
||||||
|
// SubGraphsSupergraph is implemented by the following types:
|
||||||
|
// SubGraphsSupergraphSubGraphs
|
||||||
|
// SubGraphsSupergraphUnchanged
|
||||||
|
type SubGraphsSupergraph interface {
|
||||||
|
implementsGraphQLInterfaceSubGraphsSupergraph()
|
||||||
|
// GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values).
|
||||||
|
GetTypename() *string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *SubGraphsSupergraphSubGraphs) implementsGraphQLInterfaceSubGraphsSupergraph() {}
|
||||||
|
func (v *SubGraphsSupergraphUnchanged) implementsGraphQLInterfaceSubGraphsSupergraph() {}
|
||||||
|
|
||||||
|
func __unmarshalSubGraphsSupergraph(b []byte, v *SubGraphsSupergraph) error {
|
||||||
|
if string(b) == "null" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var tn struct {
|
||||||
|
TypeName string `json:"__typename"`
|
||||||
|
}
|
||||||
|
err := json.Unmarshal(b, &tn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tn.TypeName {
|
||||||
|
case "SubGraphs":
|
||||||
|
*v = new(SubGraphsSupergraphSubGraphs)
|
||||||
|
return json.Unmarshal(b, *v)
|
||||||
|
case "Unchanged":
|
||||||
|
*v = new(SubGraphsSupergraphUnchanged)
|
||||||
|
return json.Unmarshal(b, *v)
|
||||||
|
case "":
|
||||||
|
return fmt.Errorf(
|
||||||
|
"response was missing Supergraph.__typename")
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(
|
||||||
|
`unexpected concrete type for SubGraphsSupergraph: "%v"`, tn.TypeName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func __marshalSubGraphsSupergraph(v *SubGraphsSupergraph) ([]byte, error) {
|
||||||
|
var typename string
|
||||||
|
switch v := (*v).(type) {
|
||||||
|
case *SubGraphsSupergraphSubGraphs:
|
||||||
|
typename = "SubGraphs"
|
||||||
|
|
||||||
|
result := struct {
|
||||||
|
TypeName string `json:"__typename"`
|
||||||
|
*SubGraphsSupergraphSubGraphs
|
||||||
|
}{typename, v}
|
||||||
|
return json.Marshal(result)
|
||||||
|
case *SubGraphsSupergraphUnchanged:
|
||||||
|
typename = "Unchanged"
|
||||||
|
|
||||||
|
result := struct {
|
||||||
|
TypeName string `json:"__typename"`
|
||||||
|
*SubGraphsSupergraphUnchanged
|
||||||
|
}{typename, v}
|
||||||
|
return json.Marshal(result)
|
||||||
|
case nil:
|
||||||
|
return []byte("null"), nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
`unexpected concrete type for SubGraphsSupergraph: "%T"`, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubGraphsSupergraphSubGraphs includes the requested fields of the GraphQL type SubGraphs.
|
||||||
|
type SubGraphsSupergraphSubGraphs struct {
|
||||||
|
Typename *string `json:"__typename"`
|
||||||
|
SubGraphs []*SubGraphsSupergraphSubGraphsSubGraphsSubGraph `json:"subGraphs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTypename returns SubGraphsSupergraphSubGraphs.Typename, and is useful for accessing the field via an interface.
|
||||||
|
func (v *SubGraphsSupergraphSubGraphs) GetTypename() *string { return v.Typename }
|
||||||
|
|
||||||
|
// GetSubGraphs returns SubGraphsSupergraphSubGraphs.SubGraphs, and is useful for accessing the field via an interface.
|
||||||
|
func (v *SubGraphsSupergraphSubGraphs) GetSubGraphs() []*SubGraphsSupergraphSubGraphsSubGraphsSubGraph {
|
||||||
|
return v.SubGraphs
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubGraphsSupergraphSubGraphsSubGraphsSubGraph includes the requested fields of the GraphQL type SubGraph.
|
||||||
|
type SubGraphsSupergraphSubGraphsSubGraphsSubGraph struct {
|
||||||
Service string `json:"service"`
|
Service string `json:"service"`
|
||||||
Url *string `json:"url"`
|
Url *string `json:"url"`
|
||||||
WsUrl *string `json:"wsUrl"`
|
WsUrl *string `json:"wsUrl"`
|
||||||
@@ -49,20 +197,28 @@ type SubGraphsSubGraphsSubGraph struct {
|
|||||||
ChangedAt time.Time `json:"changedAt"`
|
ChangedAt time.Time `json:"changedAt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetService returns SubGraphsSubGraphsSubGraph.Service, and is useful for accessing the field via an interface.
|
// GetService returns SubGraphsSupergraphSubGraphsSubGraphsSubGraph.Service, and is useful for accessing the field via an interface.
|
||||||
func (v *SubGraphsSubGraphsSubGraph) GetService() string { return v.Service }
|
func (v *SubGraphsSupergraphSubGraphsSubGraphsSubGraph) GetService() string { return v.Service }
|
||||||
|
|
||||||
// GetUrl returns SubGraphsSubGraphsSubGraph.Url, and is useful for accessing the field via an interface.
|
// GetUrl returns SubGraphsSupergraphSubGraphsSubGraphsSubGraph.Url, and is useful for accessing the field via an interface.
|
||||||
func (v *SubGraphsSubGraphsSubGraph) GetUrl() *string { return v.Url }
|
func (v *SubGraphsSupergraphSubGraphsSubGraphsSubGraph) GetUrl() *string { return v.Url }
|
||||||
|
|
||||||
// GetWsUrl returns SubGraphsSubGraphsSubGraph.WsUrl, and is useful for accessing the field via an interface.
|
// GetWsUrl returns SubGraphsSupergraphSubGraphsSubGraphsSubGraph.WsUrl, and is useful for accessing the field via an interface.
|
||||||
func (v *SubGraphsSubGraphsSubGraph) GetWsUrl() *string { return v.WsUrl }
|
func (v *SubGraphsSupergraphSubGraphsSubGraphsSubGraph) GetWsUrl() *string { return v.WsUrl }
|
||||||
|
|
||||||
// GetChangedBy returns SubGraphsSubGraphsSubGraph.ChangedBy, and is useful for accessing the field via an interface.
|
// GetChangedBy returns SubGraphsSupergraphSubGraphsSubGraphsSubGraph.ChangedBy, and is useful for accessing the field via an interface.
|
||||||
func (v *SubGraphsSubGraphsSubGraph) GetChangedBy() string { return v.ChangedBy }
|
func (v *SubGraphsSupergraphSubGraphsSubGraphsSubGraph) GetChangedBy() string { return v.ChangedBy }
|
||||||
|
|
||||||
// GetChangedAt returns SubGraphsSubGraphsSubGraph.ChangedAt, and is useful for accessing the field via an interface.
|
// GetChangedAt returns SubGraphsSupergraphSubGraphsSubGraphsSubGraph.ChangedAt, and is useful for accessing the field via an interface.
|
||||||
func (v *SubGraphsSubGraphsSubGraph) GetChangedAt() time.Time { return v.ChangedAt }
|
func (v *SubGraphsSupergraphSubGraphsSubGraphsSubGraph) GetChangedAt() time.Time { return v.ChangedAt }
|
||||||
|
|
||||||
|
// SubGraphsSupergraphUnchanged includes the requested fields of the GraphQL type Unchanged.
|
||||||
|
type SubGraphsSupergraphUnchanged struct {
|
||||||
|
Typename *string `json:"__typename"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTypename returns SubGraphsSupergraphUnchanged.Typename, and is useful for accessing the field via an interface.
|
||||||
|
func (v *SubGraphsSupergraphUnchanged) GetTypename() *string { return v.Typename }
|
||||||
|
|
||||||
// UpdateSubGraphResponse is returned by UpdateSubGraph on success.
|
// UpdateSubGraphResponse is returned by UpdateSubGraph on success.
|
||||||
type UpdateSubGraphResponse struct {
|
type UpdateSubGraphResponse struct {
|
||||||
@@ -114,6 +270,24 @@ type __UpdateSubGraphInput struct {
|
|||||||
// GetInput returns __UpdateSubGraphInput.Input, and is useful for accessing the field via an interface.
|
// GetInput returns __UpdateSubGraphInput.Input, and is useful for accessing the field via an interface.
|
||||||
func (v *__UpdateSubGraphInput) GetInput() *InputSubGraph { return v.Input }
|
func (v *__UpdateSubGraphInput) GetInput() *InputSubGraph { return v.Input }
|
||||||
|
|
||||||
|
// The query or mutation executed by SubGraphs.
|
||||||
|
const SubGraphs_Operation = `
|
||||||
|
query SubGraphs ($ref: String!) {
|
||||||
|
supergraph(ref: $ref) {
|
||||||
|
__typename
|
||||||
|
... on SubGraphs {
|
||||||
|
subGraphs {
|
||||||
|
service
|
||||||
|
url
|
||||||
|
wsUrl
|
||||||
|
changedBy
|
||||||
|
changedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
func SubGraphs(
|
func SubGraphs(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
client graphql.Client,
|
client graphql.Client,
|
||||||
@@ -121,17 +295,7 @@ func SubGraphs(
|
|||||||
) (*SubGraphsResponse, error) {
|
) (*SubGraphsResponse, error) {
|
||||||
req := &graphql.Request{
|
req := &graphql.Request{
|
||||||
OpName: "SubGraphs",
|
OpName: "SubGraphs",
|
||||||
Query: `
|
Query: SubGraphs_Operation,
|
||||||
query SubGraphs ($ref: String!) {
|
|
||||||
subGraphs(ref: $ref) {
|
|
||||||
service
|
|
||||||
url
|
|
||||||
wsUrl
|
|
||||||
changedBy
|
|
||||||
changedAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
Variables: &__SubGraphsInput{
|
Variables: &__SubGraphsInput{
|
||||||
Ref: ref,
|
Ref: ref,
|
||||||
},
|
},
|
||||||
@@ -150,14 +314,8 @@ query SubGraphs ($ref: String!) {
|
|||||||
return &data, err
|
return &data, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateSubGraph(
|
// The query or mutation executed by UpdateSubGraph.
|
||||||
ctx context.Context,
|
const UpdateSubGraph_Operation = `
|
||||||
client graphql.Client,
|
|
||||||
input *InputSubGraph,
|
|
||||||
) (*UpdateSubGraphResponse, error) {
|
|
||||||
req := &graphql.Request{
|
|
||||||
OpName: "UpdateSubGraph",
|
|
||||||
Query: `
|
|
||||||
mutation UpdateSubGraph ($input: InputSubGraph!) {
|
mutation UpdateSubGraph ($input: InputSubGraph!) {
|
||||||
updateSubGraph(input: $input) {
|
updateSubGraph(input: $input) {
|
||||||
service
|
service
|
||||||
@@ -167,7 +325,16 @@ mutation UpdateSubGraph ($input: InputSubGraph!) {
|
|||||||
changedAt
|
changedAt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`
|
||||||
|
|
||||||
|
func UpdateSubGraph(
|
||||||
|
ctx context.Context,
|
||||||
|
client graphql.Client,
|
||||||
|
input *InputSubGraph,
|
||||||
|
) (*UpdateSubGraphResponse, error) {
|
||||||
|
req := &graphql.Request{
|
||||||
|
OpName: "UpdateSubGraph",
|
||||||
|
Query: UpdateSubGraph_Operation,
|
||||||
Variables: &__UpdateSubGraphInput{
|
Variables: &__UpdateSubGraphInput{
|
||||||
Input: input,
|
Input: input,
|
||||||
},
|
},
|
||||||
|
|||||||
+57
-6
@@ -8,19 +8,67 @@ import (
|
|||||||
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SubGraph struct {
|
type Organization struct {
|
||||||
eventsourced.BaseAggregate
|
eventsourced.BaseAggregate
|
||||||
Ref string
|
Name string
|
||||||
Service string
|
Users []string
|
||||||
Url *string
|
APIKeys []APIKey
|
||||||
WSUrl *string
|
|
||||||
Sdl string
|
|
||||||
CreatedBy string
|
CreatedBy string
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
ChangedBy string
|
ChangedBy string
|
||||||
ChangedAt time.Time
|
ChangedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Organization) Apply(event eventsourced.Event) error {
|
||||||
|
switch e := event.(type) {
|
||||||
|
case *OrganizationAdded:
|
||||||
|
e.UpdateOrganization(o)
|
||||||
|
case *APIKeyAdded:
|
||||||
|
o.APIKeys = append(o.APIKeys, APIKey{
|
||||||
|
Name: e.Name,
|
||||||
|
OrganizationId: o.ID.String(),
|
||||||
|
Key: e.Key,
|
||||||
|
Refs: e.Refs,
|
||||||
|
Read: e.Read,
|
||||||
|
Publish: e.Publish,
|
||||||
|
CreatedBy: e.Initiator,
|
||||||
|
CreatedAt: e.When(),
|
||||||
|
})
|
||||||
|
o.ChangedBy = e.Initiator
|
||||||
|
o.ChangedAt = e.When()
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unexpected event type: %+v", event)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ eventsourced.Aggregate = &Organization{}
|
||||||
|
|
||||||
|
type APIKey struct {
|
||||||
|
Name string
|
||||||
|
OrganizationId string
|
||||||
|
Key string
|
||||||
|
Refs []string
|
||||||
|
Read bool
|
||||||
|
Publish bool
|
||||||
|
CreatedBy string
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubGraph struct {
|
||||||
|
eventsourced.BaseAggregate
|
||||||
|
OrganizationId string
|
||||||
|
Ref string
|
||||||
|
Service string
|
||||||
|
Url *string
|
||||||
|
WSUrl *string
|
||||||
|
Sdl string
|
||||||
|
CreatedBy string
|
||||||
|
CreatedAt time.Time
|
||||||
|
ChangedBy string
|
||||||
|
ChangedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SubGraph) Apply(event eventsourced.Event) error {
|
func (s *SubGraph) Apply(event eventsourced.Event) error {
|
||||||
switch e := event.(type) {
|
switch e := event.(type) {
|
||||||
case *SubGraphUpdated:
|
case *SubGraphUpdated:
|
||||||
@@ -28,6 +76,9 @@ func (s *SubGraph) Apply(event eventsourced.Event) error {
|
|||||||
s.CreatedBy = e.Initiator
|
s.CreatedBy = e.Initiator
|
||||||
s.CreatedAt = e.When()
|
s.CreatedAt = e.When()
|
||||||
}
|
}
|
||||||
|
if s.OrganizationId == "" {
|
||||||
|
s.OrganizationId = e.OrganizationId
|
||||||
|
}
|
||||||
s.ChangedBy = e.Initiator
|
s.ChangedBy = e.Initiator
|
||||||
s.ChangedAt = e.When()
|
s.ChangedAt = e.When()
|
||||||
s.Ref = e.Ref
|
s.Ref = e.Ref
|
||||||
|
|||||||
+74
-12
@@ -6,17 +6,78 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
||||||
|
|
||||||
|
"gitlab.com/unboundsoftware/schemas/hash"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UpdateSubGraph struct {
|
type AddOrganization struct {
|
||||||
Ref string
|
Name string
|
||||||
Service string
|
|
||||||
Url *string
|
|
||||||
WSUrl *string
|
|
||||||
Sdl string
|
|
||||||
Initiator string
|
Initiator string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a AddOrganization) Validate(_ context.Context, aggregate eventsourced.Aggregate) error {
|
||||||
|
if aggregate.Identity() != nil {
|
||||||
|
return fmt.Errorf("organization already exists")
|
||||||
|
}
|
||||||
|
if len(a.Name) == 0 {
|
||||||
|
return fmt.Errorf("name is required")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AddOrganization) Event(context.Context) eventsourced.Event {
|
||||||
|
return &OrganizationAdded{
|
||||||
|
Name: a.Name,
|
||||||
|
Initiator: a.Initiator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ eventsourced.Command = AddOrganization{}
|
||||||
|
|
||||||
|
type AddAPIKey struct {
|
||||||
|
Name string
|
||||||
|
Key string
|
||||||
|
Refs []string
|
||||||
|
Read bool
|
||||||
|
Publish bool
|
||||||
|
Initiator string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AddAPIKey) Validate(_ context.Context, aggregate eventsourced.Aggregate) error {
|
||||||
|
if aggregate.Identity() == nil {
|
||||||
|
return fmt.Errorf("organization does not exist")
|
||||||
|
}
|
||||||
|
for _, k := range aggregate.(*Organization).APIKeys {
|
||||||
|
if k.Name == a.Name {
|
||||||
|
return fmt.Errorf("a key named '%s' already exist", a.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AddAPIKey) Event(context.Context) eventsourced.Event {
|
||||||
|
return &APIKeyAdded{
|
||||||
|
Name: a.Name,
|
||||||
|
Key: hash.String(a.Key),
|
||||||
|
Refs: a.Refs,
|
||||||
|
Read: a.Read,
|
||||||
|
Publish: a.Publish,
|
||||||
|
Initiator: a.Initiator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ eventsourced.Command = AddAPIKey{}
|
||||||
|
|
||||||
|
type UpdateSubGraph struct {
|
||||||
|
OrganizationId string
|
||||||
|
Ref string
|
||||||
|
Service string
|
||||||
|
Url *string
|
||||||
|
WSUrl *string
|
||||||
|
Sdl string
|
||||||
|
Initiator string
|
||||||
|
}
|
||||||
|
|
||||||
func (u UpdateSubGraph) Validate(_ context.Context, aggregate eventsourced.Aggregate) error {
|
func (u UpdateSubGraph) Validate(_ context.Context, aggregate eventsourced.Aggregate) error {
|
||||||
switch a := aggregate.(type) {
|
switch a := aggregate.(type) {
|
||||||
case *SubGraph:
|
case *SubGraph:
|
||||||
@@ -40,12 +101,13 @@ func (u UpdateSubGraph) Validate(_ context.Context, aggregate eventsourced.Aggre
|
|||||||
|
|
||||||
func (u UpdateSubGraph) Event(context.Context) eventsourced.Event {
|
func (u UpdateSubGraph) Event(context.Context) eventsourced.Event {
|
||||||
return &SubGraphUpdated{
|
return &SubGraphUpdated{
|
||||||
Ref: u.Ref,
|
OrganizationId: u.OrganizationId,
|
||||||
Service: u.Service,
|
Ref: u.Ref,
|
||||||
Url: u.Url,
|
Service: u.Service,
|
||||||
WSUrl: u.WSUrl,
|
Url: u.Url,
|
||||||
Sdl: u.Sdl,
|
WSUrl: u.WSUrl,
|
||||||
Initiator: u.Initiator,
|
Sdl: u.Sdl,
|
||||||
|
Initiator: u.Initiator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddAPIKey_Event(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
Name string
|
||||||
|
Key string
|
||||||
|
Refs []string
|
||||||
|
Read bool
|
||||||
|
Publish bool
|
||||||
|
Initiator string
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
in0 context.Context
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want eventsourced.Event
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "event",
|
||||||
|
fields: fields{
|
||||||
|
Name: "test",
|
||||||
|
Key: "us_ak_1234567890123456",
|
||||||
|
Refs: []string{"Example@dev"},
|
||||||
|
Read: true,
|
||||||
|
Publish: true,
|
||||||
|
Initiator: "jim@example.org",
|
||||||
|
},
|
||||||
|
args: args{},
|
||||||
|
want: &APIKeyAdded{
|
||||||
|
Name: "test",
|
||||||
|
Key: "dXNfYWtfMTIzNDU2Nzg5MDEyMzQ1NuOwxEKY/BwUmvv0yJlvuSQnrkHkZJuTTKSVmRt4UrhV",
|
||||||
|
Refs: []string{"Example@dev"},
|
||||||
|
Read: true,
|
||||||
|
Publish: true,
|
||||||
|
Initiator: "jim@example.org",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
a := AddAPIKey{
|
||||||
|
Name: tt.fields.Name,
|
||||||
|
Key: tt.fields.Key,
|
||||||
|
Refs: tt.fields.Refs,
|
||||||
|
Read: tt.fields.Read,
|
||||||
|
Publish: tt.fields.Publish,
|
||||||
|
Initiator: tt.fields.Initiator,
|
||||||
|
}
|
||||||
|
assert.Equalf(t, tt.want, a.Event(tt.args.in0), "Event(%v)", tt.args.in0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
+41
-6
@@ -2,13 +2,48 @@ package domain
|
|||||||
|
|
||||||
import "gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
import "gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
||||||
|
|
||||||
|
type OrganizationAdded struct {
|
||||||
|
eventsourced.EventAggregateId
|
||||||
|
eventsourced.EventTime
|
||||||
|
Name string `json:"name"`
|
||||||
|
Initiator string `json:"initiator"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *OrganizationAdded) UpdateOrganization(o *Organization) {
|
||||||
|
o.Name = a.Name
|
||||||
|
o.Users = []string{a.Initiator}
|
||||||
|
o.CreatedBy = a.Initiator
|
||||||
|
o.CreatedAt = a.When()
|
||||||
|
o.ChangedBy = a.Initiator
|
||||||
|
o.ChangedAt = a.When()
|
||||||
|
}
|
||||||
|
|
||||||
|
type APIKeyAdded struct {
|
||||||
|
eventsourced.EventAggregateId
|
||||||
|
eventsourced.EventTime
|
||||||
|
OrganizationId string `json:"organizationId"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
Refs []string `json:"refs"`
|
||||||
|
Read bool `json:"read"`
|
||||||
|
Publish bool `json:"publish"`
|
||||||
|
Initiator string `json:"initiator"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *APIKeyAdded) EnrichFromAggregate(aggregate eventsourced.Aggregate) {
|
||||||
|
a.OrganizationId = aggregate.Identity().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ eventsourced.EnrichableEvent = &APIKeyAdded{}
|
||||||
|
|
||||||
type SubGraphUpdated struct {
|
type SubGraphUpdated struct {
|
||||||
eventsourced.EventAggregateId
|
eventsourced.EventAggregateId
|
||||||
eventsourced.EventTime
|
eventsourced.EventTime
|
||||||
Ref string
|
OrganizationId string `json:"organizationId"`
|
||||||
Service string
|
Ref string `json:"ref"`
|
||||||
Url *string
|
Service string `json:"service"`
|
||||||
WSUrl *string
|
Url *string `json:"url"`
|
||||||
Sdl string
|
WSUrl *string `json:"wsUrl"`
|
||||||
Initiator string
|
Sdl string `json:"sdl"`
|
||||||
|
Initiator string `json:"initiator"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,20 +3,24 @@ module gitlab.com/unboundsoftware/schemas
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/99designs/gqlgen v0.17.20
|
github.com/99designs/gqlgen v0.17.30
|
||||||
github.com/Khan/genqlient v0.5.0
|
github.com/Khan/genqlient v0.5.0
|
||||||
github.com/alecthomas/kong v0.6.1
|
github.com/alecthomas/kong v0.7.1
|
||||||
github.com/apex/log v1.9.0
|
github.com/apex/log v1.9.0
|
||||||
github.com/getsentry/sentry-go v0.14.0
|
github.com/auth0/go-jwt-middleware/v2 v2.1.0
|
||||||
|
github.com/getsentry/sentry-go v0.20.0
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/jmoiron/sqlx v1.3.5
|
github.com/jmoiron/sqlx v1.3.5
|
||||||
github.com/rs/cors v1.8.2
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/sparetimecoders/goamqp v0.1.1
|
github.com/pressly/goose/v3 v3.10.0
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/rs/cors v1.9.0
|
||||||
|
github.com/sparetimecoders/goamqp v0.1.3
|
||||||
|
github.com/stretchr/testify v1.8.2
|
||||||
github.com/vektah/gqlparser/v2 v2.5.1
|
github.com/vektah/gqlparser/v2 v2.5.1
|
||||||
github.com/wundergraph/graphql-go-tools v1.57.2-0.20221005155749-a4fdba38990b
|
github.com/wundergraph/graphql-go-tools v1.57.2-0.20221005155749-a4fdba38990b
|
||||||
gitlab.com/unboundsoftware/eventsourced/amqp v1.5.0
|
gitlab.com/unboundsoftware/eventsourced/amqp v1.6.4
|
||||||
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.9.3
|
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.13.0
|
||||||
gitlab.com/unboundsoftware/eventsourced/pg v1.9.0
|
gitlab.com/unboundsoftware/eventsourced/pg v1.11.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -28,26 +32,25 @@ require (
|
|||||||
github.com/golang/mock v1.4.4 // indirect
|
github.com/golang/mock v1.4.4 // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
github.com/gorilla/websocket v1.5.0 // indirect
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/lib/pq v1.10.6 // indirect
|
github.com/lib/pq v1.10.8 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // 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/qri-io/jsonpointer v0.1.1 // indirect
|
github.com/qri-io/jsonpointer v0.1.1 // indirect
|
||||||
github.com/qri-io/jsonschema v0.2.1 // indirect
|
github.com/qri-io/jsonschema v0.2.1 // indirect
|
||||||
github.com/rabbitmq/amqp091-go v1.3.4 // indirect
|
github.com/rabbitmq/amqp091-go v1.5.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/tidwall/gjson v1.14.3 // indirect
|
github.com/tidwall/gjson v1.14.3 // 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/v2 v2.8.1 // indirect
|
github.com/urfave/cli/v2 v2.24.4 // indirect
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
golang.org/x/mod v0.9.0 // indirect
|
||||||
golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 // indirect
|
golang.org/x/sys v0.6.0 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.8.0 // indirect
|
||||||
golang.org/x/tools v0.1.12 // indirect
|
golang.org/x/tools v0.7.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
|
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
github.com/99designs/gqlgen v0.17.2/go.mod h1:K5fzLKwtph+FFgh9j7nFbRUdBKvTcGnsta51fsMTn3o=
|
github.com/99designs/gqlgen v0.17.2/go.mod h1:K5fzLKwtph+FFgh9j7nFbRUdBKvTcGnsta51fsMTn3o=
|
||||||
github.com/99designs/gqlgen v0.17.20 h1:O7WzccIhKB1dm+7g6dhQcULINftfiLSBg2l/mwbpJMw=
|
github.com/99designs/gqlgen v0.17.30 h1:aRCHdy0mWZ41gdlHctJmR05bi3on3Alj4YNRMBZG5UA=
|
||||||
github.com/99designs/gqlgen v0.17.20/go.mod h1:Mja2HI23kWT1VRH09hvWshFgOzKswpO20o4ScpJIES4=
|
github.com/99designs/gqlgen v0.17.30/go.mod h1:i4rEatMrzzu6RXaHydq1nmEPZkb3bKQsnxNRHS4DQB4=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||||
github.com/Khan/genqlient v0.5.0 h1:TMZJ+tl/BpbmGyIBiXzKzUftDhw4ZWxQZ+1ydn0gyII=
|
github.com/Khan/genqlient v0.5.0 h1:TMZJ+tl/BpbmGyIBiXzKzUftDhw4ZWxQZ+1ydn0gyII=
|
||||||
@@ -11,10 +10,10 @@ github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4Rq
|
|||||||
github.com/agnivade/levenshtein v1.1.0/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
github.com/agnivade/levenshtein v1.1.0/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
||||||
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
|
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
|
||||||
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
||||||
github.com/alecthomas/kong v0.6.1 h1:1kNhcFepkR+HmasQpbiKDLylIL8yh5B5y1zPp5bJimA=
|
github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0=
|
||||||
github.com/alecthomas/kong v0.6.1/go.mod h1:JfHWDzLmbh/puW6I3V7uWenoh56YNVONW+w8eKeUr9I=
|
github.com/alecthomas/kong v0.7.1 h1:azoTh0IOfwlAX3qN9sHWTxACE2oV8Bg2gAwBsMwDQY4=
|
||||||
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 h1:8Uy0oSf5co/NZXje7U1z8Mpep++QJOldL2hs/sBQf48=
|
github.com/alecthomas/kong v0.7.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U=
|
||||||
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
|
||||||
github.com/alexflint/go-arg v1.4.2/go.mod h1:9iRbDxne7LcR/GSvEr7ma++GLpdIU1zrghf2y2768kM=
|
github.com/alexflint/go-arg v1.4.2/go.mod h1:9iRbDxne7LcR/GSvEr7ma++GLpdIU1zrghf2y2768kM=
|
||||||
github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxjy3MxYW0gjYw=
|
github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxjy3MxYW0gjYw=
|
||||||
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=
|
||||||
@@ -26,13 +25,13 @@ 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.1.0 h1:VU4LsC3aFPoqXVyEp8EixU6FNM+ZNIjECszRTvtGQI8=
|
||||||
|
github.com/auth0/go-jwt-middleware/v2 v2.1.0/go.mod h1:CpzcJoleayAACpv+vt0AP8/aYn5TDngsqzLapV1nM4c=
|
||||||
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/bradleyjkemp/cupaloy/v2 v2.6.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
|
github.com/bradleyjkemp/cupaloy/v2 v2.6.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
|
||||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||||
github.com/caarlos0/env v3.5.0+incompatible h1:Yy0UN8o9Wtr/jGHZDpCBLpNrzcFLLM2yixi/rBrKyJs=
|
|
||||||
github.com/caarlos0/env v3.5.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
@@ -46,21 +45,22 @@ 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/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
|
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
|
||||||
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/evanphx/json-patch/v5 v5.1.0 h1:B0aXl1o/1cP8NbviYiBMkcHBtUjIJ1/Ccg6b+SwCLQg=
|
github.com/evanphx/json-patch/v5 v5.1.0 h1:B0aXl1o/1cP8NbviYiBMkcHBtUjIJ1/Ccg6b+SwCLQg=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70=
|
github.com/getsentry/sentry-go v0.20.0 h1:bwXW98iMRIWxn+4FgPW7vMrjmbym6HblXALmhjHmQaQ=
|
||||||
github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I=
|
github.com/getsentry/sentry-go v0.20.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
|
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
|
||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
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.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
@@ -69,14 +69,16 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
|
|||||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4=
|
||||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
|
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/jensneuse/diffview v1.0.0 h1:4b6FQJ7y3295JUHU3tRko6euyEboL825ZsXeZZM47Z4=
|
github.com/jensneuse/diffview v1.0.0 h1:4b6FQJ7y3295JUHU3tRko6euyEboL825ZsXeZZM47Z4=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||||
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
|
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
|
||||||
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3roToPzKNM8dtdM=
|
github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3roToPzKNM8dtdM=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
@@ -86,24 +88,21 @@ 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.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
|
github.com/lib/pq v1.10.8 h1:3fdt97i/cwSU83+E0hZTC/Xpc9mTZxc6UWSCRcSbxiE=
|
||||||
github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.8/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc=
|
github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc=
|
||||||
github.com/matryer/moq v0.2.3/go.mod h1:9RtPYjTnH1bSBIkpvtHkFN7nbWAnO7oRpdJkEIn6UtE=
|
github.com/matryer/moq v0.2.3/go.mod h1:9RtPYjTnH1bSBIkpvtHkFN7nbWAnO7oRpdJkEIn6UtE=
|
||||||
github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/VUBk=
|
|
||||||
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-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
|
||||||
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=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||||
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
|
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
|
||||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
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/mitchellh/mapstructure v1.2.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.2.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
@@ -117,15 +116,18 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
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.10.0 h1:Gn5E9CkPqTtWvfaDVqtJqMjYtsrZ9K5mU/8wzTsvg04=
|
||||||
|
github.com/pressly/goose/v3 v3.10.0/go.mod h1:c5D3a7j66cT0fhRPj7KsXolfduVrhLlxKZjmCVSey5w=
|
||||||
github.com/qri-io/jsonpointer v0.1.1 h1:prVZBZLL6TW5vsSB9fFHFAMBLI4b0ri5vribQlTJiBA=
|
github.com/qri-io/jsonpointer v0.1.1 h1:prVZBZLL6TW5vsSB9fFHFAMBLI4b0ri5vribQlTJiBA=
|
||||||
github.com/qri-io/jsonpointer v0.1.1/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64=
|
github.com/qri-io/jsonpointer v0.1.1/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64=
|
||||||
github.com/qri-io/jsonschema v0.2.1 h1:NNFoKms+kut6ABPf6xiKNM5214jzxAhDBrPHCJ97Wg0=
|
github.com/qri-io/jsonschema v0.2.1 h1:NNFoKms+kut6ABPf6xiKNM5214jzxAhDBrPHCJ97Wg0=
|
||||||
github.com/qri-io/jsonschema v0.2.1/go.mod h1:g7DPkiOsK1xv6T/Ao5scXRkd+yTFygcANPBaaqW+VrI=
|
github.com/qri-io/jsonschema v0.2.1/go.mod h1:g7DPkiOsK1xv6T/Ao5scXRkd+yTFygcANPBaaqW+VrI=
|
||||||
github.com/rabbitmq/amqp091-go v1.3.4 h1:tXuIslN1nhDqs2t6Jrz3BAoqvt4qIZzxvdbdcxWtHYU=
|
github.com/rabbitmq/amqp091-go v1.5.0 h1:VouyHPBu1CrKyJVfteGknGOGCzmOz0zcv/tONLkb7rg=
|
||||||
github.com/rabbitmq/amqp091-go v1.3.4/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM=
|
github.com/rabbitmq/amqp091-go v1.5.0/go.mod h1:JsV0ofX5f1nwOGafb8L5rBItt9GyhfQfcJj+oyz0dGg=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U=
|
github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
|
||||||
github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
@@ -139,11 +141,12 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
|
|||||||
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/sparetimecoders/goamqp v0.1.1 h1:+TSwlrrnfJIyM+MKpnzk+4mnIvt6M8gdEFNRN4Q0wQA=
|
github.com/sparetimecoders/goamqp v0.1.3 h1:+i9gtgFm4ffSgX20/xkCxSBiM5cZ+/13hzVFcpLrCz4=
|
||||||
github.com/sparetimecoders/goamqp v0.1.1/go.mod h1:JIydmIgCqETEHIiGYmN03gNSs2bghWBHEqnR/Lfmzb0=
|
github.com/sparetimecoders/goamqp v0.1.3/go.mod h1:BKUl32yHsxpKEZEn7oEgyKB8Y0C4dk5n+17FModO6iM=
|
||||||
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=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
@@ -151,10 +154,10 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
|||||||
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.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.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.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
|
||||||
github.com/stretchr/testify v1.7.3/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
|
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
|
||||||
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
@@ -173,8 +176,8 @@ github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eN
|
|||||||
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/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||||
github.com/urfave/cli/v2 v2.8.1 h1:CGuYNZF9IKZY/rfBe3lJpccSoIY1ytfvmgQT90cNOl4=
|
github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU=
|
||||||
github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY=
|
github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
|
||||||
github.com/vektah/gqlparser/v2 v2.4.0/go.mod h1:flJWIR04IMQPGz+BXLrORkrARBxv/rtyIAFvd/MceW0=
|
github.com/vektah/gqlparser/v2 v2.4.0/go.mod h1:flJWIR04IMQPGz+BXLrORkrARBxv/rtyIAFvd/MceW0=
|
||||||
github.com/vektah/gqlparser/v2 v2.4.5/go.mod h1:flJWIR04IMQPGz+BXLrORkrARBxv/rtyIAFvd/MceW0=
|
github.com/vektah/gqlparser/v2 v2.4.5/go.mod h1:flJWIR04IMQPGz+BXLrORkrARBxv/rtyIAFvd/MceW0=
|
||||||
github.com/vektah/gqlparser/v2 v2.5.1 h1:ZGu+bquAY23jsxDRcYpWjttRZrUz07LbiY77gUOHcr4=
|
github.com/vektah/gqlparser/v2 v2.5.1 h1:ZGu+bquAY23jsxDRcYpWjttRZrUz07LbiY77gUOHcr4=
|
||||||
@@ -184,39 +187,43 @@ github.com/wundergraph/graphql-go-tools v1.57.2-0.20221005155749-a4fdba38990b/go
|
|||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
gitlab.com/unboundsoftware/eventsourced/amqp v1.6.4 h1:k9xA5fo3zvP2W5GZseAytAivJvVvBKezn5acZssrgvk=
|
||||||
gitlab.com/unboundsoftware/eventsourced/amqp v1.5.0 h1:YJu6oD8vzxuSZZqPv2N3UyQzvWDWW0h/XTHiy0aiWSo=
|
gitlab.com/unboundsoftware/eventsourced/amqp v1.6.4/go.mod h1:XHg6Men3GHsA/x9ln+atApW4ST2ZHVMp3NPnxW51JoA=
|
||||||
gitlab.com/unboundsoftware/eventsourced/amqp v1.5.0/go.mod h1:dDShzDLym/gM7Mad26Y0K2TTLUy97hO46XC69Zi91qQ=
|
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.11.2/go.mod h1:vGYGhwwjQjal7d+niWo4wKZ6ZI1zc1ehHPBfPbc2ICg=
|
||||||
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.9.0/go.mod h1:nm3W8Gr8shALbgBepOocFRqDenG/oZFBON8WLIlOvck=
|
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.13.0 h1:hGJzgND2DQ+ONiettDmk6VaAGyUTqsPv2oT3AEGZsLo=
|
||||||
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.9.2/go.mod h1:i1Woh2JHIgAK27nFxS7q6/fAceSqX4VR7PYTizY5EMI=
|
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.13.0/go.mod h1:vGYGhwwjQjal7d+niWo4wKZ6ZI1zc1ehHPBfPbc2ICg=
|
||||||
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.9.3 h1:Khls+eq34tovtDBnSPHpC4tKswUlNDCYHH9Vl5A+hTo=
|
gitlab.com/unboundsoftware/eventsourced/pg v1.11.2 h1:Yf9ZzzxoU98nM377QBQaoeNFQ349d3/04cETZF/UX/U=
|
||||||
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.9.3/go.mod h1:c4rRdyIFW2s49hnLv5c5HsfL3I144mLnvvIOb0JZdpk=
|
gitlab.com/unboundsoftware/eventsourced/pg v1.11.2/go.mod h1:+cfXfP8PUR1DbICf3gV/UQijK2Tbajjvkj4BV6CJeHs=
|
||||||
gitlab.com/unboundsoftware/eventsourced/pg v1.9.0 h1:Hr8kgACHTFICDMV71waf/+CZVKkKJWzXqPGbomMUs70=
|
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
||||||
gitlab.com/unboundsoftware/eventsourced/pg v1.9.0/go.mod h1:tfwAGkvmnCpJCAIr94hljJzZXm+W1aHAXjuYJXbXYiI=
|
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||||
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.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-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.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
|
||||||
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.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
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=
|
||||||
@@ -224,44 +231,45 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 h1:AzgQNqF+FKwyQ5LbVrVqOcuuFB67N47F9+htZYH0wFM=
|
|
||||||
golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
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.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||||
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200815165600-90abf76919f3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200815165600-90abf76919f3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618=
|
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618=
|
||||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
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-20180628173108-788fd7840127/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-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||||
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=
|
||||||
@@ -274,3 +282,13 @@ 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=
|
||||||
|
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||||
|
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
|
||||||
|
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
||||||
|
modernc.org/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY=
|
||||||
|
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||||
|
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||||
|
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||||
|
modernc.org/sqlite v1.21.0 h1:4aP4MdUf15i3R3M2mx6Q90WHKz3nZLoz96zlB6tNdow=
|
||||||
|
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||||
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ resolver:
|
|||||||
# Optional: set to speed up generation time by not performing a final validation pass.
|
# Optional: set to speed up generation time by not performing a final validation pass.
|
||||||
# skip_validation: true
|
# skip_validation: true
|
||||||
|
|
||||||
|
omit_gqlgen_version_in_file_notice: true
|
||||||
|
|
||||||
# gqlgen will search for any type names in the schema in these go packages
|
# gqlgen will search for any type names in the schema in these go packages
|
||||||
# if they match it will use them, otherwise it will generate them.
|
# if they match it will use them, otherwise it will generate them.
|
||||||
autobind:
|
autobind:
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package graph
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitlab.com/unboundsoftware/schemas/domain"
|
||||||
|
"gitlab.com/unboundsoftware/schemas/graph/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ToGqlOrganizations(orgs []domain.Organization) []*model.Organization {
|
||||||
|
result := make([]*model.Organization, len(orgs))
|
||||||
|
for i, o := range orgs {
|
||||||
|
result[i] = ToGqlOrganization(o)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToGqlOrganization(o domain.Organization) *model.Organization {
|
||||||
|
users := ToGqlUsers(o.Users)
|
||||||
|
apiKeys := ToGqlAPIKeys(o.APIKeys)
|
||||||
|
return &model.Organization{
|
||||||
|
ID: o.ID.String(),
|
||||||
|
Name: o.Name,
|
||||||
|
Users: users,
|
||||||
|
APIKeys: apiKeys,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToGqlUsers(users []string) []*model.User {
|
||||||
|
result := make([]*model.User, len(users))
|
||||||
|
for i, u := range users {
|
||||||
|
result[i] = &model.User{ID: u}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToGqlAPIKeys(keys []domain.APIKey) []*model.APIKey {
|
||||||
|
result := make([]*model.APIKey, len(keys))
|
||||||
|
for i, k := range keys {
|
||||||
|
result[i] = &model.APIKey{
|
||||||
|
ID: apiKeyId(k.OrganizationId, k.Name),
|
||||||
|
Name: k.Name,
|
||||||
|
Key: &k.Key,
|
||||||
|
Organization: nil,
|
||||||
|
Refs: k.Refs,
|
||||||
|
Read: k.Read,
|
||||||
|
Publish: k.Publish,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
+1965
-43
File diff suppressed because it is too large
Load Diff
@@ -6,20 +6,68 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Supergraph interface {
|
||||||
|
IsSupergraph()
|
||||||
|
}
|
||||||
|
|
||||||
|
type APIKey struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Key *string `json:"key,omitempty"`
|
||||||
|
Organization *Organization `json:"organization"`
|
||||||
|
Refs []string `json:"refs"`
|
||||||
|
Read bool `json:"read"`
|
||||||
|
Publish bool `json:"publish"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InputAPIKey struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
OrganizationID string `json:"organizationId"`
|
||||||
|
Refs []string `json:"refs"`
|
||||||
|
Read bool `json:"read"`
|
||||||
|
Publish bool `json:"publish"`
|
||||||
|
}
|
||||||
|
|
||||||
type InputSubGraph struct {
|
type InputSubGraph struct {
|
||||||
Ref string `json:"ref"`
|
Ref string `json:"ref"`
|
||||||
Service string `json:"service"`
|
Service string `json:"service"`
|
||||||
URL *string `json:"url"`
|
URL *string `json:"url,omitempty"`
|
||||||
WsURL *string `json:"wsUrl"`
|
WsURL *string `json:"wsUrl,omitempty"`
|
||||||
Sdl string `json:"sdl"`
|
Sdl string `json:"sdl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Organization struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Users []*User `json:"users"`
|
||||||
|
APIKeys []*APIKey `json:"apiKeys"`
|
||||||
|
}
|
||||||
|
|
||||||
type SubGraph struct {
|
type SubGraph struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Service string `json:"service"`
|
Service string `json:"service"`
|
||||||
URL *string `json:"url"`
|
URL *string `json:"url,omitempty"`
|
||||||
WsURL *string `json:"wsUrl"`
|
WsURL *string `json:"wsUrl,omitempty"`
|
||||||
Sdl string `json:"sdl"`
|
Sdl string `json:"sdl"`
|
||||||
ChangedBy string `json:"changedBy"`
|
ChangedBy string `json:"changedBy"`
|
||||||
ChangedAt time.Time `json:"changedAt"`
|
ChangedAt time.Time `json:"changedAt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SubGraphs struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
MinDelaySeconds int `json:"minDelaySeconds"`
|
||||||
|
SubGraphs []*SubGraph `json:"subGraphs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (SubGraphs) IsSupergraph() {}
|
||||||
|
|
||||||
|
type Unchanged struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
MinDelaySeconds int `json:"minDelaySeconds"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Unchanged) IsSupergraph() {}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
|||||||
+10
-4
@@ -1,6 +1,9 @@
|
|||||||
package graph
|
package graph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
||||||
|
|
||||||
@@ -14,8 +17,7 @@ import (
|
|||||||
// It serves as dependency injection for your app, add any dependencies you require here.
|
// It serves as dependency injection for your app, add any dependencies you require here.
|
||||||
|
|
||||||
type Publisher interface {
|
type Publisher interface {
|
||||||
Publish(event eventsourced.Event) error
|
Publish(ctx context.Context, event eventsourced.Event) error
|
||||||
Stop() error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Resolver struct {
|
type Resolver struct {
|
||||||
@@ -25,6 +27,10 @@ type Resolver struct {
|
|||||||
Cache *cache.Cache
|
Cache *cache.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) handler(aggregate eventsourced.Aggregate) (eventsourced.CommandHandler, error) {
|
func (r *Resolver) handler(ctx context.Context, aggregate eventsourced.Aggregate) (eventsourced.CommandHandler, error) {
|
||||||
return eventsourced.NewHandler(aggregate, r.EventStore, eventsourced.WithEventPublisher(r.Publisher))
|
return eventsourced.NewHandler(ctx, aggregate, r.EventStore, eventsourced.WithEventPublisher(r.Publisher))
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiKeyId(orgId, name string) string {
|
||||||
|
return fmt.Sprintf("%s-%s", orgId, name)
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-3
@@ -1,9 +1,46 @@
|
|||||||
type Query {
|
type Query {
|
||||||
subGraphs(ref: String!): [SubGraph!]! @hasApiKey
|
organizations: [Organization!]! @auth(user: true)
|
||||||
|
supergraph(ref: String!, isAfter: String): Supergraph! @auth(organization: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
updateSubGraph(input: InputSubGraph!): SubGraph! @hasApiKey
|
addOrganization(name: String!): Organization! @auth(user: true)
|
||||||
|
addAPIKey(input: InputAPIKey): APIKey! @auth(user: true)
|
||||||
|
updateSubGraph(input: InputSubGraph!): SubGraph! @auth(organization: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Organization {
|
||||||
|
id: ID!
|
||||||
|
name: String!
|
||||||
|
users: [User!]!
|
||||||
|
apiKeys: [APIKey!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
type User {
|
||||||
|
id: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
type APIKey {
|
||||||
|
id: ID!
|
||||||
|
name: String!
|
||||||
|
key: String
|
||||||
|
organization: Organization!
|
||||||
|
refs: [String!]!
|
||||||
|
read: Boolean!
|
||||||
|
publish: Boolean!
|
||||||
|
}
|
||||||
|
|
||||||
|
union Supergraph = Unchanged | SubGraphs
|
||||||
|
|
||||||
|
type Unchanged {
|
||||||
|
id: ID!
|
||||||
|
minDelaySeconds: Int!
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubGraphs {
|
||||||
|
id: ID!
|
||||||
|
minDelaySeconds: Int!
|
||||||
|
subGraphs: [SubGraph!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type SubGraph {
|
type SubGraph {
|
||||||
@@ -16,6 +53,14 @@ type SubGraph {
|
|||||||
changedAt: Time!
|
changedAt: Time!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input InputAPIKey {
|
||||||
|
name: String!
|
||||||
|
organizationId: ID!
|
||||||
|
refs: [String!]!
|
||||||
|
read: Boolean!
|
||||||
|
publish: Boolean!
|
||||||
|
}
|
||||||
|
|
||||||
input InputSubGraph {
|
input InputSubGraph {
|
||||||
ref: String!
|
ref: String!
|
||||||
service: String!
|
service: String!
|
||||||
@@ -26,4 +71,4 @@ input InputSubGraph {
|
|||||||
|
|
||||||
scalar Time
|
scalar Time
|
||||||
|
|
||||||
directive @hasApiKey on FIELD_DEFINITION
|
directive @auth(user: Boolean, organization: Boolean) on FIELD_DEFINITION
|
||||||
|
|||||||
+27
-2
@@ -1,16 +1,41 @@
|
|||||||
package graph
|
package graph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
||||||
|
|
||||||
"gitlab.com/unboundsoftware/schemas/domain"
|
"gitlab.com/unboundsoftware/schemas/domain"
|
||||||
|
"gitlab.com/unboundsoftware/schemas/graph/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *Resolver) fetchSubGraph(subGraphId string) (*domain.SubGraph, error) {
|
func (r *Resolver) fetchSubGraph(ctx context.Context, subGraphId string) (*domain.SubGraph, error) {
|
||||||
subGraph := &domain.SubGraph{BaseAggregate: eventsourced.BaseAggregateFromString(subGraphId)}
|
subGraph := &domain.SubGraph{BaseAggregate: eventsourced.BaseAggregateFromString(subGraphId)}
|
||||||
_, err := r.handler(subGraph)
|
_, err := r.handler(ctx, subGraph)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return subGraph, nil
|
return subGraph, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) toGqlSubGraph(subGraph *domain.SubGraph) *model.SubGraph {
|
||||||
|
return &model.SubGraph{
|
||||||
|
ID: subGraph.ID.String(),
|
||||||
|
Service: subGraph.Service,
|
||||||
|
URL: subGraph.Url,
|
||||||
|
WsURL: subGraph.WSUrl,
|
||||||
|
Sdl: subGraph.Sdl,
|
||||||
|
ChangedBy: subGraph.ChangedBy,
|
||||||
|
ChangedAt: subGraph.ChangedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) stringEqual(s1, s2 *string) bool {
|
||||||
|
if s1 == nil && s2 == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if s1 != nil && s2 != nil && *s1 == *s2 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
+109
-25
@@ -2,9 +2,12 @@ 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
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/wundergraph/graphql-go-tools/pkg/federation/sdlmerge"
|
"github.com/wundergraph/graphql-go-tools/pkg/federation/sdlmerge"
|
||||||
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
||||||
@@ -12,22 +15,88 @@ import (
|
|||||||
"gitlab.com/unboundsoftware/schemas/domain"
|
"gitlab.com/unboundsoftware/schemas/domain"
|
||||||
"gitlab.com/unboundsoftware/schemas/graph/generated"
|
"gitlab.com/unboundsoftware/schemas/graph/generated"
|
||||||
"gitlab.com/unboundsoftware/schemas/graph/model"
|
"gitlab.com/unboundsoftware/schemas/graph/model"
|
||||||
|
"gitlab.com/unboundsoftware/schemas/middleware"
|
||||||
|
"gitlab.com/unboundsoftware/schemas/rand"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AddOrganization is the resolver for the addOrganization field.
|
||||||
|
func (r *mutationResolver) AddOrganization(ctx context.Context, name string) (*model.Organization, error) {
|
||||||
|
sub := middleware.UserFromContext(ctx)
|
||||||
|
org := &domain.Organization{}
|
||||||
|
h, err := r.handler(ctx, org)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = h.Handle(ctx, &domain.AddOrganization{
|
||||||
|
Name: name,
|
||||||
|
Initiator: sub,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ToGqlOrganization(*org), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAPIKey is the resolver for the addAPIKey field.
|
||||||
|
func (r *mutationResolver) AddAPIKey(ctx context.Context, input *model.InputAPIKey) (*model.APIKey, error) {
|
||||||
|
sub := middleware.UserFromContext(ctx)
|
||||||
|
org := &domain.Organization{BaseAggregate: eventsourced.BaseAggregateFromString(input.OrganizationID)}
|
||||||
|
h, err := r.handler(ctx, org)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key := fmt.Sprintf("us_ak_%s", rand.String(16))
|
||||||
|
_, err = h.Handle(ctx, &domain.AddAPIKey{
|
||||||
|
Name: input.Name,
|
||||||
|
Key: key,
|
||||||
|
Refs: input.Refs,
|
||||||
|
Read: input.Read,
|
||||||
|
Publish: input.Publish,
|
||||||
|
Initiator: sub,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &model.APIKey{
|
||||||
|
ID: apiKeyId(input.OrganizationID, input.Name),
|
||||||
|
Name: input.Name,
|
||||||
|
Key: &key,
|
||||||
|
Organization: &model.Organization{
|
||||||
|
ID: input.OrganizationID,
|
||||||
|
Name: org.Name,
|
||||||
|
},
|
||||||
|
Refs: input.Refs,
|
||||||
|
Read: input.Read,
|
||||||
|
Publish: input.Publish,
|
||||||
|
}, 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) {
|
||||||
subGraphId := r.Cache.SubGraphId(input.Ref, input.Service)
|
orgId := middleware.OrganizationFromContext(ctx)
|
||||||
|
key, err := middleware.ApiKeyFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
apiKey := r.Cache.ApiKeyByKey(key)
|
||||||
|
subGraphId := r.Cache.SubGraphId(orgId, input.Ref, input.Service)
|
||||||
subGraph := &domain.SubGraph{}
|
subGraph := &domain.SubGraph{}
|
||||||
if subGraphId != "" {
|
if subGraphId != "" {
|
||||||
subGraph.BaseAggregate = eventsourced.BaseAggregateFromString(subGraphId)
|
subGraph.BaseAggregate = eventsourced.BaseAggregateFromString(subGraphId)
|
||||||
}
|
}
|
||||||
handler, err := r.handler(subGraph)
|
handler, err := r.handler(ctx, subGraph)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if strings.TrimSpace(input.Sdl) == strings.TrimSpace(subGraph.Sdl) &&
|
||||||
|
r.stringEqual(input.URL, subGraph.Url) &&
|
||||||
|
r.stringEqual(input.WsURL, subGraph.WSUrl) {
|
||||||
|
return r.toGqlSubGraph(subGraph), nil
|
||||||
|
}
|
||||||
serviceSDLs := []string{input.Sdl}
|
serviceSDLs := []string{input.Sdl}
|
||||||
for _, id := range r.Cache.Services(input.Ref) {
|
services, _ := r.Cache.Services(orgId, input.Ref, "")
|
||||||
sg, err := r.fetchSubGraph(id)
|
for _, id := range services {
|
||||||
|
sg, err := r.fetchSubGraph(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -39,34 +108,45 @@ func (r *mutationResolver) UpdateSubGraph(ctx context.Context, input model.Input
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, err = handler.Handle(domain.UpdateSubGraph{
|
_, err = handler.Handle(ctx, domain.UpdateSubGraph{
|
||||||
Ref: input.Ref,
|
OrganizationId: orgId,
|
||||||
Service: input.Service,
|
Ref: input.Ref,
|
||||||
Url: input.URL,
|
Service: input.Service,
|
||||||
WSUrl: input.WsURL,
|
Url: input.URL,
|
||||||
Sdl: input.Sdl,
|
WSUrl: input.WsURL,
|
||||||
Initiator: "Fetch name from API-key?",
|
Sdl: input.Sdl,
|
||||||
|
Initiator: apiKey.Name,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &model.SubGraph{
|
return r.toGqlSubGraph(subGraph), nil
|
||||||
ID: subGraph.ID.String(),
|
|
||||||
Service: subGraph.Service,
|
|
||||||
URL: subGraph.Url,
|
|
||||||
WsURL: subGraph.WSUrl,
|
|
||||||
Sdl: subGraph.Sdl,
|
|
||||||
ChangedBy: subGraph.ChangedBy,
|
|
||||||
ChangedAt: subGraph.ChangedAt,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubGraphs is the resolver for the subGraphs field.
|
// Organizations is the resolver for the organizations field.
|
||||||
func (r *queryResolver) SubGraphs(ctx context.Context, ref string) ([]*model.SubGraph, error) {
|
func (r *queryResolver) Organizations(ctx context.Context) ([]*model.Organization, error) {
|
||||||
services := r.Cache.Services(ref)
|
sub := middleware.UserFromContext(ctx)
|
||||||
|
orgs := r.Cache.OrganizationsByUser(sub)
|
||||||
|
return ToGqlOrganizations(orgs), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supergraph is the resolver for the supergraph field.
|
||||||
|
func (r *queryResolver) Supergraph(ctx context.Context, ref string, isAfter *string) (model.Supergraph, error) {
|
||||||
|
orgId := middleware.OrganizationFromContext(ctx)
|
||||||
|
after := ""
|
||||||
|
if isAfter != nil {
|
||||||
|
after = *isAfter
|
||||||
|
}
|
||||||
|
services, lastUpdate := r.Cache.Services(orgId, ref, after)
|
||||||
|
if after == lastUpdate {
|
||||||
|
return &model.Unchanged{
|
||||||
|
ID: lastUpdate,
|
||||||
|
MinDelaySeconds: 10,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
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(id)
|
sg, err := r.fetchSubGraph(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -80,7 +160,11 @@ func (r *queryResolver) SubGraphs(ctx context.Context, ref string) ([]*model.Sub
|
|||||||
ChangedAt: sg.ChangedAt,
|
ChangedAt: sg.ChangedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return subGraphs, nil
|
return &model.SubGraphs{
|
||||||
|
ID: lastUpdate,
|
||||||
|
SubGraphs: subGraphs,
|
||||||
|
MinDelaySeconds: 10,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mutation returns generated.MutationResolver implementation.
|
// Mutation returns generated.MutationResolver implementation.
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package hash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
)
|
||||||
|
|
||||||
|
func String(s string) string {
|
||||||
|
encoded := sha256.New().Sum([]byte(s))
|
||||||
|
return base64.StdEncoding.EncodeToString(encoded)
|
||||||
|
}
|
||||||
@@ -4,11 +4,12 @@ metadata:
|
|||||||
name: schemas-ingress
|
name: schemas-ingress
|
||||||
annotations:
|
annotations:
|
||||||
kubernetes.io/ingress.class: "alb"
|
kubernetes.io/ingress.class: "alb"
|
||||||
alb.ingress.kubernetes.io/group.name: "unbound"
|
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
|
||||||
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80},{"HTTPS": 443}]'
|
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80},{"HTTPS": 443}]'
|
||||||
alb.ingress.kubernetes.io/ssl-redirect: "443"
|
alb.ingress.kubernetes.io/ssl-redirect: "443"
|
||||||
|
alb.ingress.kubernetes.io/healthcheck-path: '/health'
|
||||||
spec:
|
spec:
|
||||||
rules:
|
rules:
|
||||||
- host: "schemas.unbound.se"
|
- host: "schemas.unbound.se"
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ spec:
|
|||||||
data:
|
data:
|
||||||
POSTGRES_URL: "postgres://{{ .DB_USERNAME }}:{{ .DB_PASSWORD }}@{{ .DB_HOST }}:{{ .DB_PORT }}/schemas?sslmode=disable"
|
POSTGRES_URL: "postgres://{{ .DB_USERNAME }}:{{ .DB_PASSWORD }}@{{ .DB_HOST }}:{{ .DB_PORT }}/schemas?sslmode=disable"
|
||||||
API_KEY: "{{ .API_KEY }}"
|
API_KEY: "{{ .API_KEY }}"
|
||||||
|
SENTRY_DSN: "{{ .SENTRY_DSN }}"
|
||||||
|
SENTRY_ENVIRONMENT: "{{ .SENTRY_ENVIRONMENT }}"
|
||||||
dataFrom:
|
dataFrom:
|
||||||
- extract:
|
- extract:
|
||||||
key: services/schemas
|
key: services/schemas
|
||||||
|
|||||||
+5
-25
@@ -4,26 +4,17 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/99designs/gqlgen/graphql"
|
|
||||||
"github.com/apex/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ContextKey string
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ApiKey = ContextKey("apikey")
|
ApiKey = ContextKey("apikey")
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewApiKey(apiKey string, logger log.Interface) *ApiKeyMiddleware {
|
func NewApiKey() *ApiKeyMiddleware {
|
||||||
return &ApiKeyMiddleware{
|
return &ApiKeyMiddleware{}
|
||||||
apiKey: apiKey,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ApiKeyMiddleware struct {
|
type ApiKeyMiddleware struct{}
|
||||||
apiKey string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ApiKeyMiddleware) Handler(next http.Handler) http.Handler {
|
func (m *ApiKeyMiddleware) 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) {
|
||||||
@@ -37,23 +28,12 @@ func (m *ApiKeyMiddleware) Handler(next http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ApiKeyMiddleware) Directive(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err error) {
|
func ApiKeyFromContext(ctx context.Context) (string, error) {
|
||||||
key, err := m.fromContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if key != m.apiKey {
|
|
||||||
return nil, fmt.Errorf("invalid API-key")
|
|
||||||
}
|
|
||||||
return next(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ApiKeyMiddleware) fromContext(ctx context.Context) (string, error) {
|
|
||||||
if value := ctx.Value(ApiKey); value != nil {
|
if value := ctx.Value(ApiKey); value != nil {
|
||||||
if u, ok := value.(string); ok {
|
if u, ok := value.(string); ok {
|
||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("current API-key is in wrong format")
|
return "", fmt.Errorf("current API-key is in wrong format")
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("no API-key found")
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/99designs/gqlgen/graphql"
|
||||||
|
"github.com/golang-jwt/jwt/v4"
|
||||||
|
|
||||||
|
"gitlab.com/unboundsoftware/schemas/domain"
|
||||||
|
"gitlab.com/unboundsoftware/schemas/hash"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UserKey = ContextKey("user")
|
||||||
|
OrganizationKey = ContextKey("organization")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cache interface {
|
||||||
|
OrganizationByAPIKey(apiKey string) *domain.Organization
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuth(cache Cache) *AuthMiddleware {
|
||||||
|
return &AuthMiddleware{
|
||||||
|
cache: cache,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthMiddleware struct {
|
||||||
|
cache Cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AuthMiddleware) Handler(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
token, err := TokenFromContext(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
_, _ = 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())
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
_, _ = w.Write([]byte("Invalid API Key format"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if organization := m.cache.OrganizationByAPIKey(hash.String(apiKey)); organization != nil {
|
||||||
|
ctx = context.WithValue(ctx, OrganizationKey, *organization)
|
||||||
|
}
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserFromContext(ctx context.Context) string {
|
||||||
|
if value := ctx.Value(UserKey); value != nil {
|
||||||
|
if u, ok := value.(string); ok {
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func OrganizationFromContext(ctx context.Context) string {
|
||||||
|
if value := ctx.Value(OrganizationKey); value != nil {
|
||||||
|
if u, ok := value.(domain.Organization); ok {
|
||||||
|
return u.ID.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AuthMiddleware) Directive(ctx context.Context, _ interface{}, next graphql.Resolver, user *bool, organization *bool) (res interface{}, err error) {
|
||||||
|
if user != nil && *user {
|
||||||
|
if u := UserFromContext(ctx); u == "" {
|
||||||
|
return nil, fmt.Errorf("no user available in request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if organization != nil && *organization {
|
||||||
|
if orgId := OrganizationFromContext(ctx); orgId == "" {
|
||||||
|
return nil, fmt.Errorf("no organization available in request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return next(ctx)
|
||||||
|
}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
mw "github.com/auth0/go-jwt-middleware/v2"
|
||||||
|
"github.com/golang-jwt/jwt/v4"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Auth0 struct {
|
||||||
|
domain string
|
||||||
|
audience string
|
||||||
|
client *http.Client
|
||||||
|
cache JwksCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuth0(audience, domain string, strictSsl bool) *Auth0 {
|
||||||
|
customTransport := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
|
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: !strictSsl}
|
||||||
|
client := &http.Client{Transport: customTransport}
|
||||||
|
|
||||||
|
return &Auth0{
|
||||||
|
domain: domain,
|
||||||
|
audience: audience,
|
||||||
|
client: client,
|
||||||
|
cache: JwksCache{
|
||||||
|
RWMutex: &sync.RWMutex{},
|
||||||
|
cache: make(map[string]cacheItem),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Jwks struct {
|
||||||
|
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) {
|
||||||
|
issuer := fmt.Sprintf("https://%s/", a.domain)
|
||||||
|
return func(token *jwt.Token) (interface{}, error) {
|
||||||
|
// Verify 'aud' claim
|
||||||
|
aud := a.audience
|
||||||
|
checkAud := token.Claims.(jwt.MapClaims).VerifyAudience(aud, false)
|
||||||
|
if !checkAud {
|
||||||
|
return token, errors.New("Invalid audience.")
|
||||||
|
}
|
||||||
|
// Verify 'iss' claim
|
||||||
|
iss := issuer
|
||||||
|
checkIss := token.Claims.(jwt.MapClaims).VerifyIssuer(iss, false)
|
||||||
|
if !checkIss {
|
||||||
|
return token, errors.New("Invalid issuer.")
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
jwtMiddleware := mw.New(func(ctx context.Context, token string) (interface{}, error) {
|
||||||
|
jwtToken, err := jwt.Parse(token, a.ValidationKeyGetter())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, ok := jwtToken.Method.(*jwt.SigningMethodRSA); !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected signing method: %v", jwtToken.Header["alg"])
|
||||||
|
}
|
||||||
|
err = jwtToken.Claims.Valid()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return jwtToken, nil
|
||||||
|
},
|
||||||
|
mw.WithTokenExtractor(func(r *http.Request) (string, error) {
|
||||||
|
token := r.Header.Get("Authorization")
|
||||||
|
if strings.HasPrefix(token, "Bearer ") {
|
||||||
|
return token[7:], nil
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}),
|
||||||
|
mw.WithCredentialsOptional(true),
|
||||||
|
)
|
||||||
|
|
||||||
|
return jwtMiddleware
|
||||||
|
}
|
||||||
|
|
||||||
|
func TokenFromContext(ctx context.Context) (*jwt.Token, error) {
|
||||||
|
if value := ctx.Value(mw.ContextKey{}); value != nil {
|
||||||
|
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 {
|
||||||
|
return jwks, err
|
||||||
|
}
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
type ContextKey string
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package rand
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const charset = "abcdefghijklmnopqrstuvwxyz" +
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
|
||||||
|
var seededRand *rand.Rand = rand.New(
|
||||||
|
rand.NewSource(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
func StringWithCharset(length int, charset string) string {
|
||||||
|
b := make([]byte, length)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = charset[seededRand.Intn(len(charset))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func String(length int) string {
|
||||||
|
return StringWithCharset(length, charset)
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- Add Unbound Software Development organization
|
||||||
|
insert into aggregates (id, name)
|
||||||
|
values ('d46ffcb0-19e8-4769-8697-590326ef7b51', 'domain.Organization');
|
||||||
|
|
||||||
|
insert into events (name, aggregate_id, sequence_no, payload, tstamp, aggregate_name)
|
||||||
|
values ('domain.OrganizationAdded', 'd46ffcb0-19e8-4769-8697-590326ef7b51', 1, '{"id":"d46ffcb0-19e8-4769-8697-590326ef7b51","time":"2023-04-26T14:46:04.43462+02:00","name":"Unbound Software Development","initiator":"google-oauth2|101953650269257914934"}', '2023-04-26T14:46:04.43462+02:00', 'domain.Organization');
|
||||||
|
|
||||||
|
-- Add API keys
|
||||||
|
insert into events (name, aggregate_id, sequence_no, payload, tstamp, aggregate_name)
|
||||||
|
values ('domain.APIKeyAdded', 'd46ffcb0-19e8-4769-8697-590326ef7b51', 2,
|
||||||
|
'{"id":"d46ffcb0-19e8-4769-8697-590326ef7b51","time":"2023-04-26T15:46:54.181929+02:00","organizationId":"","name":"CI","key":"dXNfYWtfeUl2R3RRQUJQTmJzVEFrUeOwxEKY/BwUmvv0yJlvuSQnrkHkZJuTTKSVmRt4UrhV","refs":["Shiny@staging","Shiny@prod"],"read":false,"publish":true,"initiator":"google-oauth2|101953650269257914934"}',
|
||||||
|
'2023-04-26 15:46:54.181929 +02:00', 'domain.Organization');
|
||||||
|
|
||||||
|
insert into events (name, aggregate_id, sequence_no, payload, tstamp, aggregate_name)
|
||||||
|
values ('domain.APIKeyAdded', 'd46ffcb0-19e8-4769-8697-590326ef7b51', 3,
|
||||||
|
'{"id":"d46ffcb0-19e8-4769-8697-590326ef7b51","time":"2023-04-26T15:52:55.955203+02:00","organizationId":"","name":"Gateway","key":"dXNfYWtfdnkzSkRseDNlSDNjcnZzOeOwxEKY/BwUmvv0yJlvuSQnrkHkZJuTTKSVmRt4UrhV","refs":["Shiny@staging","Shiny@prod"],"read":true,"publish":false,"initiator":"google-oauth2|101953650269257914934"}',
|
||||||
|
'2023-04-26 15:52:55.955203 +02:00', 'domain.Organization');
|
||||||
|
|
||||||
|
insert into events (name, aggregate_id, sequence_no, payload, tstamp, aggregate_name)
|
||||||
|
values ('domain.APIKeyAdded', 'd46ffcb0-19e8-4769-8697-590326ef7b51', 4,
|
||||||
|
'{"id":"d46ffcb0-19e8-4769-8697-590326ef7b51","time":"2023-04-26T16:30:00.0011+02:00","organizationId":"","name":"Local dev","key":"dXNfYWtfM0kzaGZndmVaQllyQzdjVOOwxEKY/BwUmvv0yJlvuSQnrkHkZJuTTKSVmRt4UrhV","refs":["Shiny@dev"],"read":true,"publish":true,"initiator":"google-oauth2|101953650269257914934"}',
|
||||||
|
'2023-04-26 16:30:00.001100 +02:00', 'domain.Organization');
|
||||||
|
|
||||||
|
insert into events (name, aggregate_id, sequence_no, payload, tstamp, aggregate_name)
|
||||||
|
values ('domain.APIKeyAdded', 'd46ffcb0-19e8-4769-8697-590326ef7b51', 5,
|
||||||
|
'{"id":"d46ffcb0-19e8-4769-8697-590326ef7b51","time":"2023-04-27T07:43:26.599544+02:00","organizationId":"","name":"Acctest","key":"dXNfYWtfdlVqMzdBMXVraklmaGtKSOOwxEKY/BwUmvv0yJlvuSQnrkHkZJuTTKSVmRt4UrhV","refs":["Shiny@test"],"read":true,"publish":true,"initiator":"google-oauth2|101953650269257914934"}',
|
||||||
|
'2023-04-27 07:43:26.599544 +02:00', 'domain.Organization');
|
||||||
|
|
||||||
|
-- Update events since json-tags were added
|
||||||
|
UPDATE events e
|
||||||
|
SET payload = jsonb_build_object(
|
||||||
|
'id', payload::jsonb ->> 'id',
|
||||||
|
'time', payload::jsonb ->> 'time',
|
||||||
|
'ref', payload::jsonb ->> 'Ref',
|
||||||
|
'sdl', payload::jsonb ->> 'Sdl',
|
||||||
|
'url', payload::jsonb ->> 'Url',
|
||||||
|
'wsUrl', payload::jsonb ->> 'WSUrl',
|
||||||
|
'service', payload::jsonb ->> 'Service',
|
||||||
|
'initiator', 'CI'
|
||||||
|
)
|
||||||
|
WHERE e.name = 'domain.SubGraphUpdated';
|
||||||
|
|
||||||
|
-- Add organization id to all existing subgraphs
|
||||||
|
update events e
|
||||||
|
set payload = jsonb_set(payload::jsonb, '{organizationId}', '"d46ffcb0-19e8-4769-8697-590326ef7b51"', true)
|
||||||
|
where e.name = 'domain.SubGraphUpdated';
|
||||||
|
|
||||||
|
DELETE
|
||||||
|
from snapshots;
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"embed"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
"github.com/pressly/goose/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SetupDB(driverName, url string) (*sqlx.DB, error) {
|
func SetupDB(driverName, url string) (*sqlx.DB, error) {
|
||||||
@@ -25,3 +28,13 @@ func SetupDB(driverName, url string) (*sqlx.DB, error) {
|
|||||||
//
|
//
|
||||||
// return goose.Up(db.DB, "migrations")
|
// return goose.Up(db.DB, "migrations")
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
//go:embed event_store_migrations/*.sql
|
||||||
|
var embedEventStoreMigrations embed.FS
|
||||||
|
|
||||||
|
func RunEventStoreMigrations(db *sqlx.DB) error {
|
||||||
|
goose.SetTableName("goose_db_version_event")
|
||||||
|
goose.SetBaseFS(embedEventStoreMigrations)
|
||||||
|
|
||||||
|
return goose.Up(db.DB, "event_store_migrations")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user