feat: initial commit
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
.idea
|
||||||
|
.testCoverage.txt
|
||||||
|
.testCoverage.txt.tmp
|
||||||
|
coverage.html
|
||||||
|
/exported
|
||||||
|
/release
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
include:
|
||||||
|
- template: 'Workflows/MergeRequest-Pipelines.gitlab-ci.yml'
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
- deploy-prod
|
||||||
|
- release
|
||||||
|
|
||||||
|
variables:
|
||||||
|
DOCKER_HOST: tcp://docker:2376
|
||||||
|
DOCKER_TLS_CERTDIR: "/certs"
|
||||||
|
DOCKER_TLS_VERIFY: 1
|
||||||
|
DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
|
||||||
|
DOCKER_DRIVER: overlay2
|
||||||
|
|
||||||
|
.buildtools:
|
||||||
|
image: buildtool/build-tools:${BUILDTOOLS_VERSION}
|
||||||
|
services:
|
||||||
|
- docker:dind
|
||||||
|
|
||||||
|
build:
|
||||||
|
extends: .buildtools
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- build
|
||||||
|
- curl -Os https://uploader.codecov.io/latest/linux/codecov
|
||||||
|
- chmod +x codecov
|
||||||
|
- ./codecov -t ${CODECOV_TOKEN} -R $CI_PROJECT_DIR -C $CI_COMMIT_SHA -r $CI_PROJECT_PATH
|
||||||
|
- push
|
||||||
|
|
||||||
|
vulnerabilities:
|
||||||
|
stage: build
|
||||||
|
image: golang:1.19.2
|
||||||
|
script:
|
||||||
|
- go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||||
|
- govulncheck ./...
|
||||||
|
|
||||||
|
deploy-prod:
|
||||||
|
extends: .buildtools
|
||||||
|
stage: deploy-prod
|
||||||
|
before_script:
|
||||||
|
- echo Deploy to prod
|
||||||
|
script:
|
||||||
|
- deploy prod
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_BRANCH == "main"
|
||||||
|
environment:
|
||||||
|
name: prod
|
||||||
|
|
||||||
|
release:
|
||||||
|
stage: release
|
||||||
|
image: docker:stable
|
||||||
|
services:
|
||||||
|
- docker:dind
|
||||||
|
|
||||||
|
variables:
|
||||||
|
GORELEASER_IMAGE: goreleaser/goreleaser:v1.11.5-amd64
|
||||||
|
GITLAB_TOKEN: ${CI_JOB_TOKEN}
|
||||||
|
# Disable shallow cloning so that goreleaser can diff between tags to
|
||||||
|
# generate a changelog.
|
||||||
|
GIT_DEPTH: 0
|
||||||
|
|
||||||
|
# Only run this release job for tags, not every commit (for example).
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_TAG
|
||||||
|
|
||||||
|
script: |
|
||||||
|
docker pull $GORELEASER_IMAGE
|
||||||
|
|
||||||
|
# GITLAB_TOKEN is needed to create GitLab releases.
|
||||||
|
# DOCKER_* are needed to push Docker images.
|
||||||
|
docker run --rm --privileged \
|
||||||
|
-v $PWD:/src \
|
||||||
|
-w /src \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-e GITLAB_TOKEN \
|
||||||
|
$GORELEASER_IMAGE release --rm-dist
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
project_name: unbound-schemas
|
||||||
|
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
|
||||||
|
builds:
|
||||||
|
- id: schemactl
|
||||||
|
main: ./cmd/schemactl/schemactl.go
|
||||||
|
binary: schemactl
|
||||||
|
goos:
|
||||||
|
- linux
|
||||||
|
- darwin
|
||||||
|
- windows
|
||||||
|
goarch:
|
||||||
|
- amd64
|
||||||
|
- arm64
|
||||||
|
|
||||||
|
brews:
|
||||||
|
- name: unbound-schemas
|
||||||
|
tap:
|
||||||
|
owner: unboundsoftware
|
||||||
|
name: homebrew-taps
|
||||||
|
folder: Formula
|
||||||
|
install: |
|
||||||
|
bin.install "schemactl"
|
||||||
|
commit_author:
|
||||||
|
name: "Joakim Olsson"
|
||||||
|
email: joakim@unbound.se
|
||||||
|
homepage: "https://schemas.unbound.se/"
|
||||||
|
|
||||||
|
archives:
|
||||||
|
- id: unbound-schemas
|
||||||
|
replacements:
|
||||||
|
darwin: Darwin
|
||||||
|
linux: Linux
|
||||||
|
windows: Windows
|
||||||
|
386: i386
|
||||||
|
amd64: x86_64
|
||||||
|
|
||||||
|
checksum:
|
||||||
|
name_template: 'checksums.txt'
|
||||||
|
|
||||||
|
snapshot:
|
||||||
|
name_template: "{{ .Tag }}-next"
|
||||||
|
|
||||||
|
changelog:
|
||||||
|
sort: asc
|
||||||
|
filters:
|
||||||
|
exclude:
|
||||||
|
- '^Merge'
|
||||||
|
- '^docs:'
|
||||||
|
- '^test:'
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# See https://pre-commit.com for more information
|
||||||
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v4.3.0
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: check-yaml
|
||||||
|
args:
|
||||||
|
- --allow-multiple-documents
|
||||||
|
- id: check-added-large-files
|
||||||
|
- repo: https://github.com/jumanjihouse/pre-commit-hooks
|
||||||
|
rev: 3.0.0
|
||||||
|
hooks:
|
||||||
|
- id: markdownlint
|
||||||
|
- repo: https://gitlab.com/devopshq/gitlab-ci-linter
|
||||||
|
rev: v1.0.3
|
||||||
|
hooks:
|
||||||
|
- id: gitlab-ci-linter
|
||||||
|
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
|
||||||
|
rev: v9.1.0
|
||||||
|
hooks:
|
||||||
|
- id: commitlint
|
||||||
|
stages: [ commit-msg ]
|
||||||
|
additional_dependencies: [ '@commitlint/config-conventional' ]
|
||||||
|
- repo: https://github.com/lietu/go-pre-commit
|
||||||
|
rev: v0.0.1
|
||||||
|
hooks:
|
||||||
|
- id: errcheck
|
||||||
|
- id: go-fmt-goimports
|
||||||
|
- id: go-test
|
||||||
|
- id: go-vet
|
||||||
|
- id: golangci-lint
|
||||||
|
- id: gofumpt
|
||||||
|
- id: staticcheck
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
FROM golang:1.19.2 as build
|
||||||
|
WORKDIR /build
|
||||||
|
ENV CGO_ENABLED=0
|
||||||
|
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 ["/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 go tool cover -html=coverage.txt -o coverage.html
|
||||||
|
RUN go tool cover -func=coverage.txt
|
||||||
|
RUN rm coverage.txt.tmp
|
||||||
|
|
||||||
|
RUN GOOS=linux GOARCH=amd64 go build \
|
||||||
|
-a -installsuffix cgo \
|
||||||
|
-mod=readonly \
|
||||||
|
-o /release/service \
|
||||||
|
-ldflags '-w -s' \
|
||||||
|
./cmd/service/service.go
|
||||||
|
|
||||||
|
FROM scratch as export
|
||||||
|
COPY --from=build /build/coverage.txt /
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
ENV TZ Europe/Stockholm
|
||||||
|
COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo
|
||||||
|
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
|
COPY --from=build /release/service /
|
||||||
|
CMD ["/service"]
|
||||||
Vendored
+58
@@ -0,0 +1,58 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/apex/log"
|
||||||
|
"github.com/sparetimecoders/goamqp"
|
||||||
|
|
||||||
|
"gitlab.com/unboundsoftware/schemas/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
const subGraphKey = "%s<->%s"
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
services map[string]map[string]struct{}
|
||||||
|
subGraphs map[string]string
|
||||||
|
logger log.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Services(ref string) []string {
|
||||||
|
var services []string
|
||||||
|
for k := range c.services[ref] {
|
||||||
|
services = append(services, k)
|
||||||
|
}
|
||||||
|
return services
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) SubGraphId(ref, service string) string {
|
||||||
|
return c.subGraphs[fmt.Sprintf(subGraphKey, ref, service)]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Update(msg any, _ goamqp.Headers) (any, error) {
|
||||||
|
switch m := msg.(type) {
|
||||||
|
case *domain.SubGraphUpdated:
|
||||||
|
if _, exists := c.services[m.Ref]; !exists {
|
||||||
|
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()
|
||||||
|
case *domain.SubGraph:
|
||||||
|
if _, exists := c.services[m.Ref]; !exists {
|
||||||
|
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:
|
||||||
|
c.logger.Warnf("unexpected message received: %+v", msg)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(logger log.Interface) *Cache {
|
||||||
|
return &Cache{
|
||||||
|
subGraphs: make(map[string]string),
|
||||||
|
services: make(map[string]map[string]struct{}),
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alecthomas/kong"
|
||||||
|
"github.com/apex/log"
|
||||||
|
|
||||||
|
"gitlab.com/unboundsoftware/schemas/ctl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
ApiKey string
|
||||||
|
SchemaRef string
|
||||||
|
SchemasURL url.URL
|
||||||
|
logger log.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
type PublishCmd struct {
|
||||||
|
Service string `name:"service" env:"SERVICE" help:"The service to publish SDL for" required:""`
|
||||||
|
Url *url.URL `name:"url" env:"URL" help:"The URL of the service" optional:""`
|
||||||
|
WSUrl *url.URL `name:"ws-url" env:"WS_URL" help:"The Websocket URL of the service" optional:""`
|
||||||
|
SDL *os.File `name:"sdl" env:"SDL" help:"The file containing the GraphQL SDL" required:""`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PublishCmd) Run(ctx Context) error {
|
||||||
|
buff, err := io.ReadAll(c.SDL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
subGraph, err := ctl.Publish(ctx.ApiKey, ctx.SchemaRef, c.Service, string(buff), c.Url, c.WSUrl, ctx.SchemasURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx.logger.Infof("published %s@%s %s", subGraph.Service, subGraph.ChangedAt.Format(time.RFC3339), subGraph.ChangedBy)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListCmd struct {
|
||||||
|
Service string `name:"service" env:"SERVICE" help:"The service to publish SDL for" optional:""`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ListCmd) Run(ctx Context) error {
|
||||||
|
subGraphs, err := ctl.List(ctx.ApiKey, ctx.SchemaRef, c.Service, ctx.SchemasURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sort.SliceStable(subGraphs, func(i, j int) bool {
|
||||||
|
return subGraphs[i].Service < subGraphs[j].Service
|
||||||
|
})
|
||||||
|
for _, subGraph := range subGraphs {
|
||||||
|
ctx.logger.Infof("%s URL: %s WS-URL: %s Changed: %s %s", subGraph.Service, emptyIfNil(subGraph.URL), emptyIfNil(subGraph.WSUrl), subGraph.ChangedAt.Format(time.RFC3339), subGraph.ChangedBy)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CLI struct {
|
||||||
|
ApiKey string `name:"api-key" env:"API_KEY" help:"The API-key to use when communicating with the server" required:""`
|
||||||
|
SchemaRef string `name:"schema-ref" env:"SCHEMA_REF" help:"The schema reference to work with" required:""`
|
||||||
|
OverrideSchemasUrl *url.URL `name:"override-schemas-url" env:"OVERRIDE_SCHEMAS_URL" help:"Use a specific URL when communicating with the Schemas-API" optional:""`
|
||||||
|
Publish PublishCmd `cmd:""`
|
||||||
|
List ListCmd `cmd:""`
|
||||||
|
LogLevel string `name:"log-level" env:"LOG_LEVEL" help:"The level of logging to use (debug, info, warn, error, fatal)" default:"info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cli := CLI{}
|
||||||
|
ctx := kong.Parse(&cli)
|
||||||
|
log.SetLevelFromString(cli.LogLevel)
|
||||||
|
logger := log.WithField("service", "schemactl")
|
||||||
|
err := ctx.Run(Context{ApiKey: cli.ApiKey, SchemaRef: cli.SchemaRef, SchemasURL: schemasUrl(cli.OverrideSchemasUrl), logger: logger})
|
||||||
|
ctx.FatalIfErrorf(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func emptyIfNil(s *string) string {
|
||||||
|
if s == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return *s
|
||||||
|
}
|
||||||
|
|
||||||
|
func schemasUrl(u *url.URL) url.URL {
|
||||||
|
if u == nil {
|
||||||
|
u, err := url.Parse("https://schemas.unbound.se/query")
|
||||||
|
if err != nil {
|
||||||
|
return url.URL{}
|
||||||
|
}
|
||||||
|
return *u
|
||||||
|
}
|
||||||
|
return *u
|
||||||
|
}
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/99designs/gqlgen/graphql/handler"
|
||||||
|
"github.com/99designs/gqlgen/graphql/playground"
|
||||||
|
"github.com/alecthomas/kong"
|
||||||
|
"github.com/apex/log"
|
||||||
|
"github.com/apex/log/handlers/json"
|
||||||
|
sentryhttp "github.com/getsentry/sentry-go/http"
|
||||||
|
"github.com/rs/cors"
|
||||||
|
"github.com/sparetimecoders/goamqp"
|
||||||
|
"gitlab.com/unboundsoftware/eventsourced/amqp"
|
||||||
|
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
||||||
|
"gitlab.com/unboundsoftware/eventsourced/pg"
|
||||||
|
|
||||||
|
"gitlab.com/unboundsoftware/schemas/cache"
|
||||||
|
"gitlab.com/unboundsoftware/schemas/domain"
|
||||||
|
"gitlab.com/unboundsoftware/schemas/graph"
|
||||||
|
"gitlab.com/unboundsoftware/schemas/graph/generated"
|
||||||
|
"gitlab.com/unboundsoftware/schemas/middleware"
|
||||||
|
"gitlab.com/unboundsoftware/schemas/store"
|
||||||
|
)
|
||||||
|
|
||||||
|
var CLI struct {
|
||||||
|
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"`
|
||||||
|
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"`
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const serviceName = "schemas"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
_ = kong.Parse(&CLI)
|
||||||
|
log.SetHandler(json.New(os.Stdout))
|
||||||
|
log.SetLevelFromString(CLI.LogLevel)
|
||||||
|
logger := log.WithField("service", serviceName)
|
||||||
|
closeEvents := make(chan error)
|
||||||
|
|
||||||
|
if err := start(
|
||||||
|
closeEvents,
|
||||||
|
logger,
|
||||||
|
ConnectAMQP,
|
||||||
|
); err != nil {
|
||||||
|
logger.WithError(err).Error("process error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func start(closeEvents chan error, logger *log.Entry, connectToAmqpFunc func(url string) (Connection, error)) error {
|
||||||
|
rootCtx, rootCancel := context.WithCancel(context.Background())
|
||||||
|
defer rootCancel()
|
||||||
|
|
||||||
|
db, err := store.SetupDB(CLI.DatabaseDriverName, CLI.DatabaseURL)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to setup DB: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
eventStore, err := pg.New(
|
||||||
|
db.DB,
|
||||||
|
pg.WithEventTypes(
|
||||||
|
&domain.SubGraphUpdated{},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create eventstore: %v", err)
|
||||||
|
}
|
||||||
|
eventPublisher, err := goamqp.NewPublisher(
|
||||||
|
goamqp.Route{
|
||||||
|
Type: domain.SubGraphUpdated{},
|
||||||
|
Key: "SubGraph.Updated",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create event publisher: %v", err)
|
||||||
|
}
|
||||||
|
amqp.New(eventPublisher)
|
||||||
|
conn, err := connectToAmqpFunc(CLI.AmqpURL)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to connect to AMQP: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceCache := cache.New(logger)
|
||||||
|
roots, err := eventStore.GetAggregateRoots(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(subGraph, eventStore); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := serviceCache.Update(subGraph, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setups := []goamqp.Setup{
|
||||||
|
goamqp.UseLogger(logger.Errorf),
|
||||||
|
goamqp.CloseListener(closeEvents),
|
||||||
|
goamqp.WithPrefetchLimit(20),
|
||||||
|
goamqp.EventStreamPublisher(eventPublisher),
|
||||||
|
goamqp.TransientEventStreamConsumer("SubGraph.Updated", serviceCache.Update, domain.SubGraphUpdated{}),
|
||||||
|
}
|
||||||
|
if err := conn.Start(setups...); err != nil {
|
||||||
|
return fmt.Errorf("failed to setup AMQP: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() { _ = conn.Close() }()
|
||||||
|
|
||||||
|
logger.Info("Started")
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
httpSrv := &http.Server{Addr: fmt.Sprintf(":%d", CLI.Port), Handler: mux}
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
|
sigint := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigint, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
sig := <-sigint
|
||||||
|
if sig != nil {
|
||||||
|
// In case our shutdown logic is broken/incomplete we reset signal
|
||||||
|
// handlers so next signal goes to go itself. Go is more aggressive when
|
||||||
|
// shutting down goroutines
|
||||||
|
signal.Reset(os.Interrupt, syscall.SIGTERM)
|
||||||
|
logger.Info("Got shutdown signal..")
|
||||||
|
rootCancel()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
err := <-closeEvents
|
||||||
|
if err != nil {
|
||||||
|
logger.WithError(err).Error("received close from AMQP")
|
||||||
|
rootCancel()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
<-rootCtx.Done()
|
||||||
|
|
||||||
|
if err := httpSrv.Close(); err != nil {
|
||||||
|
logger.WithError(err).Error("close http server")
|
||||||
|
}
|
||||||
|
close(sigint)
|
||||||
|
close(closeEvents)
|
||||||
|
}()
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
defer rootCancel()
|
||||||
|
|
||||||
|
resolver := &graph.Resolver{
|
||||||
|
EventStore: eventStore,
|
||||||
|
Publisher: amqp.New(eventPublisher),
|
||||||
|
Logger: logger,
|
||||||
|
Cache: serviceCache,
|
||||||
|
}
|
||||||
|
|
||||||
|
config := generated.Config{
|
||||||
|
Resolvers: resolver,
|
||||||
|
Complexity: generated.ComplexityRoot{},
|
||||||
|
}
|
||||||
|
apiKeyMiddleware := middleware.NewApiKey(CLI.APIKey, logger)
|
||||||
|
config.Directives.HasApiKey = apiKeyMiddleware.Directive
|
||||||
|
srv := handler.NewDefaultServer(generated.NewExecutableSchema(
|
||||||
|
config,
|
||||||
|
))
|
||||||
|
|
||||||
|
sentryHandler := sentryhttp.New(sentryhttp.Options{Repanic: true})
|
||||||
|
mux.Handle("/", sentryHandler.HandleFunc(playground.Handler("GraphQL playground", "/query")))
|
||||||
|
mux.Handle("/health", sentryHandler.HandleFunc(healthFunc))
|
||||||
|
mux.Handle("/query", cors.AllowAll().Handler(sentryHandler.Handle(apiKeyMiddleware.Handler(srv))))
|
||||||
|
|
||||||
|
logger.Infof("connect to http://localhost:%d/ for GraphQL playground", CLI.Port)
|
||||||
|
|
||||||
|
if err := httpSrv.ListenAndServe(); err != nil {
|
||||||
|
logger.WithError(err).Error("listen http")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func healthFunc(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
_, _ = w.Write([]byte("OK"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConnectAMQP(url string) (Connection, error) {
|
||||||
|
return goamqp.NewFromURL(serviceName, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Connection interface {
|
||||||
|
Start(opts ...goamqp.Setup) error
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
query SubGraphs($ref: String!) {
|
||||||
|
subGraphs(ref: $ref) {
|
||||||
|
service
|
||||||
|
url
|
||||||
|
wsUrl
|
||||||
|
changedBy
|
||||||
|
changedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
mutation UpdateSubGraph($input: InputSubGraph!) {
|
||||||
|
updateSubGraph(input: $input) {
|
||||||
|
service
|
||||||
|
url
|
||||||
|
wsUrl
|
||||||
|
changedBy
|
||||||
|
changedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
package ctl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/Khan/genqlient/graphql"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run github.com/Khan/genqlient@main ./genqlient.yaml
|
||||||
|
|
||||||
|
func Publish(apiKey, schemaRef, service, sdl string, url, wsUrl *url.URL, schemasUrl url.URL) (*SubGraph, error) {
|
||||||
|
client := graphql.NewClient(schemasUrl.String(), &http.Client{Transport: NewTransport(http.DefaultTransport, apiKey)})
|
||||||
|
var urlString *string
|
||||||
|
if url != nil {
|
||||||
|
temp := url.String()
|
||||||
|
urlString = &temp
|
||||||
|
}
|
||||||
|
var wsUrlString *string
|
||||||
|
if wsUrl != nil {
|
||||||
|
temp := wsUrl.String()
|
||||||
|
wsUrlString = &temp
|
||||||
|
}
|
||||||
|
response, err := UpdateSubGraph(context.Background(), client, &InputSubGraph{
|
||||||
|
Ref: schemaRef,
|
||||||
|
Service: service,
|
||||||
|
Url: urlString,
|
||||||
|
WsUrl: wsUrlString,
|
||||||
|
Sdl: sdl,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &SubGraph{
|
||||||
|
Service: response.UpdateSubGraph.Service,
|
||||||
|
URL: response.UpdateSubGraph.Url,
|
||||||
|
WSUrl: response.UpdateSubGraph.WsUrl,
|
||||||
|
ChangedBy: response.UpdateSubGraph.ChangedBy,
|
||||||
|
ChangedAt: response.UpdateSubGraph.ChangedAt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func List(apiKey, schemaRef, service string, schemasUrl url.URL) ([]*SubGraph, error) {
|
||||||
|
client := graphql.NewClient(schemasUrl.String(), &http.Client{Transport: NewTransport(http.DefaultTransport, apiKey)})
|
||||||
|
response, err := SubGraphs(context.Background(), client, schemaRef)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
subGraphs := make([]*SubGraph, len(response.SubGraphs))
|
||||||
|
for i, subGraph := range response.SubGraphs {
|
||||||
|
subGraphs[i] = &SubGraph{
|
||||||
|
Service: subGraph.Service,
|
||||||
|
URL: subGraph.Url,
|
||||||
|
WSUrl: subGraph.WsUrl,
|
||||||
|
ChangedBy: subGraph.ChangedBy,
|
||||||
|
ChangedAt: subGraph.ChangedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subGraphs, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
// Code generated by github.com/Khan/genqlient, DO NOT EDIT.
|
||||||
|
|
||||||
|
package ctl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Khan/genqlient/graphql"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InputSubGraph struct {
|
||||||
|
Ref string `json:"ref"`
|
||||||
|
Service string `json:"service"`
|
||||||
|
Url *string `json:"url"`
|
||||||
|
WsUrl *string `json:"wsUrl"`
|
||||||
|
Sdl string `json:"sdl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRef returns InputSubGraph.Ref, and is useful for accessing the field via an interface.
|
||||||
|
func (v *InputSubGraph) GetRef() string { return v.Ref }
|
||||||
|
|
||||||
|
// GetService returns InputSubGraph.Service, and is useful for accessing the field via an interface.
|
||||||
|
func (v *InputSubGraph) GetService() string { return v.Service }
|
||||||
|
|
||||||
|
// GetUrl returns InputSubGraph.Url, and is useful for accessing the field via an interface.
|
||||||
|
func (v *InputSubGraph) GetUrl() *string { return v.Url }
|
||||||
|
|
||||||
|
// GetWsUrl returns InputSubGraph.WsUrl, and is useful for accessing the field via an interface.
|
||||||
|
func (v *InputSubGraph) GetWsUrl() *string { return v.WsUrl }
|
||||||
|
|
||||||
|
// GetSdl returns InputSubGraph.Sdl, and is useful for accessing the field via an interface.
|
||||||
|
func (v *InputSubGraph) GetSdl() string { return v.Sdl }
|
||||||
|
|
||||||
|
// SubGraphsResponse is returned by SubGraphs on success.
|
||||||
|
type SubGraphsResponse struct {
|
||||||
|
SubGraphs []*SubGraphsSubGraphsSubGraph `json:"subGraphs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSubGraphs returns SubGraphsResponse.SubGraphs, and is useful for accessing the field via an interface.
|
||||||
|
func (v *SubGraphsResponse) GetSubGraphs() []*SubGraphsSubGraphsSubGraph { return v.SubGraphs }
|
||||||
|
|
||||||
|
// SubGraphsSubGraphsSubGraph includes the requested fields of the GraphQL type SubGraph.
|
||||||
|
type SubGraphsSubGraphsSubGraph struct {
|
||||||
|
Service string `json:"service"`
|
||||||
|
Url *string `json:"url"`
|
||||||
|
WsUrl *string `json:"wsUrl"`
|
||||||
|
ChangedBy string `json:"changedBy"`
|
||||||
|
ChangedAt time.Time `json:"changedAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetService returns SubGraphsSubGraphsSubGraph.Service, and is useful for accessing the field via an interface.
|
||||||
|
func (v *SubGraphsSubGraphsSubGraph) GetService() string { return v.Service }
|
||||||
|
|
||||||
|
// GetUrl returns SubGraphsSubGraphsSubGraph.Url, and is useful for accessing the field via an interface.
|
||||||
|
func (v *SubGraphsSubGraphsSubGraph) GetUrl() *string { return v.Url }
|
||||||
|
|
||||||
|
// GetWsUrl returns SubGraphsSubGraphsSubGraph.WsUrl, and is useful for accessing the field via an interface.
|
||||||
|
func (v *SubGraphsSubGraphsSubGraph) GetWsUrl() *string { return v.WsUrl }
|
||||||
|
|
||||||
|
// GetChangedBy returns SubGraphsSubGraphsSubGraph.ChangedBy, and is useful for accessing the field via an interface.
|
||||||
|
func (v *SubGraphsSubGraphsSubGraph) GetChangedBy() string { return v.ChangedBy }
|
||||||
|
|
||||||
|
// GetChangedAt returns SubGraphsSubGraphsSubGraph.ChangedAt, and is useful for accessing the field via an interface.
|
||||||
|
func (v *SubGraphsSubGraphsSubGraph) GetChangedAt() time.Time { return v.ChangedAt }
|
||||||
|
|
||||||
|
// UpdateSubGraphResponse is returned by UpdateSubGraph on success.
|
||||||
|
type UpdateSubGraphResponse struct {
|
||||||
|
UpdateSubGraph *UpdateSubGraphUpdateSubGraph `json:"updateSubGraph"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUpdateSubGraph returns UpdateSubGraphResponse.UpdateSubGraph, and is useful for accessing the field via an interface.
|
||||||
|
func (v *UpdateSubGraphResponse) GetUpdateSubGraph() *UpdateSubGraphUpdateSubGraph {
|
||||||
|
return v.UpdateSubGraph
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateSubGraphUpdateSubGraph includes the requested fields of the GraphQL type SubGraph.
|
||||||
|
type UpdateSubGraphUpdateSubGraph struct {
|
||||||
|
Service string `json:"service"`
|
||||||
|
Url *string `json:"url"`
|
||||||
|
WsUrl *string `json:"wsUrl"`
|
||||||
|
ChangedBy string `json:"changedBy"`
|
||||||
|
ChangedAt time.Time `json:"changedAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetService returns UpdateSubGraphUpdateSubGraph.Service, and is useful for accessing the field via an interface.
|
||||||
|
func (v *UpdateSubGraphUpdateSubGraph) GetService() string { return v.Service }
|
||||||
|
|
||||||
|
// GetUrl returns UpdateSubGraphUpdateSubGraph.Url, and is useful for accessing the field via an interface.
|
||||||
|
func (v *UpdateSubGraphUpdateSubGraph) GetUrl() *string { return v.Url }
|
||||||
|
|
||||||
|
// GetWsUrl returns UpdateSubGraphUpdateSubGraph.WsUrl, and is useful for accessing the field via an interface.
|
||||||
|
func (v *UpdateSubGraphUpdateSubGraph) GetWsUrl() *string { return v.WsUrl }
|
||||||
|
|
||||||
|
// GetChangedBy returns UpdateSubGraphUpdateSubGraph.ChangedBy, and is useful for accessing the field via an interface.
|
||||||
|
func (v *UpdateSubGraphUpdateSubGraph) GetChangedBy() string { return v.ChangedBy }
|
||||||
|
|
||||||
|
// GetChangedAt returns UpdateSubGraphUpdateSubGraph.ChangedAt, and is useful for accessing the field via an interface.
|
||||||
|
func (v *UpdateSubGraphUpdateSubGraph) GetChangedAt() time.Time { return v.ChangedAt }
|
||||||
|
|
||||||
|
// __SubGraphsInput is used internally by genqlient
|
||||||
|
type __SubGraphsInput struct {
|
||||||
|
Ref string `json:"ref"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRef returns __SubGraphsInput.Ref, and is useful for accessing the field via an interface.
|
||||||
|
func (v *__SubGraphsInput) GetRef() string { return v.Ref }
|
||||||
|
|
||||||
|
// __UpdateSubGraphInput is used internally by genqlient
|
||||||
|
type __UpdateSubGraphInput struct {
|
||||||
|
Input *InputSubGraph `json:"input,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInput returns __UpdateSubGraphInput.Input, and is useful for accessing the field via an interface.
|
||||||
|
func (v *__UpdateSubGraphInput) GetInput() *InputSubGraph { return v.Input }
|
||||||
|
|
||||||
|
func SubGraphs(
|
||||||
|
ctx context.Context,
|
||||||
|
client graphql.Client,
|
||||||
|
ref string,
|
||||||
|
) (*SubGraphsResponse, error) {
|
||||||
|
req := &graphql.Request{
|
||||||
|
OpName: "SubGraphs",
|
||||||
|
Query: `
|
||||||
|
query SubGraphs ($ref: String!) {
|
||||||
|
subGraphs(ref: $ref) {
|
||||||
|
service
|
||||||
|
url
|
||||||
|
wsUrl
|
||||||
|
changedBy
|
||||||
|
changedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
Variables: &__SubGraphsInput{
|
||||||
|
Ref: ref,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
var data SubGraphsResponse
|
||||||
|
resp := &graphql.Response{Data: &data}
|
||||||
|
|
||||||
|
err = client.MakeRequest(
|
||||||
|
ctx,
|
||||||
|
req,
|
||||||
|
resp,
|
||||||
|
)
|
||||||
|
|
||||||
|
return &data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateSubGraph(
|
||||||
|
ctx context.Context,
|
||||||
|
client graphql.Client,
|
||||||
|
input *InputSubGraph,
|
||||||
|
) (*UpdateSubGraphResponse, error) {
|
||||||
|
req := &graphql.Request{
|
||||||
|
OpName: "UpdateSubGraph",
|
||||||
|
Query: `
|
||||||
|
mutation UpdateSubGraph ($input: InputSubGraph!) {
|
||||||
|
updateSubGraph(input: $input) {
|
||||||
|
service
|
||||||
|
url
|
||||||
|
wsUrl
|
||||||
|
changedBy
|
||||||
|
changedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
Variables: &__UpdateSubGraphInput{
|
||||||
|
Input: input,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
var data UpdateSubGraphResponse
|
||||||
|
resp := &graphql.Response{Data: &data}
|
||||||
|
|
||||||
|
err = client.MakeRequest(
|
||||||
|
ctx,
|
||||||
|
req,
|
||||||
|
resp,
|
||||||
|
)
|
||||||
|
|
||||||
|
return &data, err
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Default genqlient config; for full documentation see:
|
||||||
|
# https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
|
||||||
|
schema: ../graph/schema.graphqls
|
||||||
|
operations:
|
||||||
|
- "*.graphql"
|
||||||
|
generated: generated.go
|
||||||
|
package: ctl
|
||||||
|
use_struct_references: true
|
||||||
|
optional: pointer
|
||||||
|
#- output: pointer
|
||||||
|
bindings:
|
||||||
|
Time:
|
||||||
|
type: time.Time
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package ctl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type headerTransport struct {
|
||||||
|
tripper http.RoundTripper
|
||||||
|
apiKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTransport(tripper http.RoundTripper, apiKey string) *headerTransport {
|
||||||
|
return &headerTransport{
|
||||||
|
tripper: tripper,
|
||||||
|
apiKey: apiKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *headerTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
req.Header.Set("X-Api-Key", t.apiKey)
|
||||||
|
return t.tripper.RoundTrip(req)
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package ctl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_headerTransport_RoundTrip(t1 *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
apiKey string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
handler func(t *testing.T) http.Handler
|
||||||
|
want *http.Response
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "apiKey is set as header",
|
||||||
|
fields: fields{
|
||||||
|
apiKey: "abc123",
|
||||||
|
},
|
||||||
|
handler: func(t *testing.T) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
assert.Equal(t, "abc123", req.Header.Get("x-api-key"))
|
||||||
|
_, err := w.Write([]byte("OK"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
want: nil,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t1.Run(tt.name, func(t1 *testing.T) {
|
||||||
|
srv := httptest.NewServer(tt.handler(t1))
|
||||||
|
defer srv.Close()
|
||||||
|
t := NewTransport(http.DefaultTransport, tt.fields.apiKey)
|
||||||
|
c := http.Client{Transport: t}
|
||||||
|
_, err := c.Get(srv.URL)
|
||||||
|
assert.NoError(t1, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package ctl
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type SubGraph struct {
|
||||||
|
Service string
|
||||||
|
URL *string
|
||||||
|
WSUrl *string
|
||||||
|
ChangedBy string
|
||||||
|
ChangedAt time.Time
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SubGraph struct {
|
||||||
|
eventsourced.BaseAggregate
|
||||||
|
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 {
|
||||||
|
switch e := event.(type) {
|
||||||
|
case *SubGraphUpdated:
|
||||||
|
if s.Ref == "" {
|
||||||
|
s.CreatedBy = e.Initiator
|
||||||
|
s.CreatedAt = e.When()
|
||||||
|
}
|
||||||
|
s.ChangedBy = e.Initiator
|
||||||
|
s.ChangedAt = e.When()
|
||||||
|
s.Ref = e.Ref
|
||||||
|
s.Service = e.Service
|
||||||
|
if e.Url != nil {
|
||||||
|
url := strings.TrimSpace(*e.Url)
|
||||||
|
s.Url = &url
|
||||||
|
}
|
||||||
|
if e.WSUrl != nil {
|
||||||
|
url := strings.TrimSpace(*e.WSUrl)
|
||||||
|
s.WSUrl = &url
|
||||||
|
}
|
||||||
|
s.Sdl = e.Sdl
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unexpected event type: %+v", event)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ eventsourced.Aggregate = &SubGraph{}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpdateSubGraph struct {
|
||||||
|
Ref string
|
||||||
|
Service string
|
||||||
|
Url *string
|
||||||
|
WSUrl *string
|
||||||
|
Sdl string
|
||||||
|
Initiator string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u UpdateSubGraph) Validate(_ context.Context, aggregate eventsourced.Aggregate) error {
|
||||||
|
switch a := aggregate.(type) {
|
||||||
|
case *SubGraph:
|
||||||
|
if strings.TrimSpace(u.Ref) == "" {
|
||||||
|
return fmt.Errorf("ref is missing")
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(u.Service) == "" {
|
||||||
|
return fmt.Errorf("service is missing")
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(u.Sdl) == "" {
|
||||||
|
return fmt.Errorf("SDL is missing")
|
||||||
|
}
|
||||||
|
if (u.Url == nil || strings.TrimSpace(*u.Url) == "") && a.Url == nil {
|
||||||
|
return fmt.Errorf("url is missing")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("aggregate is not a SubGraph")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u UpdateSubGraph) Event(context.Context) eventsourced.Event {
|
||||||
|
return &SubGraphUpdated{
|
||||||
|
Ref: u.Ref,
|
||||||
|
Service: u.Service,
|
||||||
|
Url: u.Url,
|
||||||
|
WSUrl: u.WSUrl,
|
||||||
|
Sdl: u.Sdl,
|
||||||
|
Initiator: u.Initiator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ eventsourced.Command = UpdateSubGraph{}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package domain
|
||||||
|
|
||||||
|
import "gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
||||||
|
|
||||||
|
type SubGraphUpdated struct {
|
||||||
|
eventsourced.EventAggregateId
|
||||||
|
eventsourced.EventTime
|
||||||
|
Ref string
|
||||||
|
Service string
|
||||||
|
Url *string
|
||||||
|
WSUrl *string
|
||||||
|
Sdl string
|
||||||
|
Initiator string
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
module gitlab.com/unboundsoftware/schemas
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/99designs/gqlgen v0.17.20
|
||||||
|
github.com/Khan/genqlient v0.5.0
|
||||||
|
github.com/alecthomas/kong v0.6.1
|
||||||
|
github.com/apex/log v1.9.0
|
||||||
|
github.com/getsentry/sentry-go v0.14.0
|
||||||
|
github.com/jmoiron/sqlx v1.3.5
|
||||||
|
github.com/rs/cors v1.8.2
|
||||||
|
github.com/sparetimecoders/goamqp v0.1.1
|
||||||
|
github.com/stretchr/testify v1.8.0
|
||||||
|
github.com/vektah/gqlparser/v2 v2.5.1
|
||||||
|
github.com/wundergraph/graphql-go-tools v1.57.2-0.20221005155749-a4fdba38990b
|
||||||
|
gitlab.com/unboundsoftware/eventsourced/amqp v1.5.0
|
||||||
|
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.9.3
|
||||||
|
gitlab.com/unboundsoftware/eventsourced/pg v1.9.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/agnivade/levenshtein v1.1.1 // indirect
|
||||||
|
github.com/buger/jsonparser v1.1.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/golang/mock v1.4.4 // indirect
|
||||||
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
|
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||||
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
github.com/lib/pq v1.10.6 // 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/qri-io/jsonpointer v0.1.1 // indirect
|
||||||
|
github.com/qri-io/jsonschema v0.2.1 // indirect
|
||||||
|
github.com/rabbitmq/amqp091-go v1.3.4 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
github.com/tidwall/gjson v1.14.3 // indirect
|
||||||
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
|
github.com/tidwall/sjson v1.2.5 // indirect
|
||||||
|
github.com/urfave/cli/v2 v2.8.1 // 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/sys v0.0.0-20221006211917-84dc82d7e875 // indirect
|
||||||
|
golang.org/x/text v0.3.7 // indirect
|
||||||
|
golang.org/x/tools v0.1.12 // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,276 @@
|
|||||||
|
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.20/go.mod h1:Mja2HI23kWT1VRH09hvWshFgOzKswpO20o4ScpJIES4=
|
||||||
|
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/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/go.mod h1:EpIvDVXYm01GP6AXzjA7dKriPTH6GmtpmvTAwUUqIX8=
|
||||||
|
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||||
|
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/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
||||||
|
github.com/alecthomas/kong v0.6.1 h1:1kNhcFepkR+HmasQpbiKDLylIL8yh5B5y1zPp5bJimA=
|
||||||
|
github.com/alecthomas/kong v0.6.1/go.mod h1:JfHWDzLmbh/puW6I3V7uWenoh56YNVONW+w8eKeUr9I=
|
||||||
|
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 h1:8Uy0oSf5co/NZXje7U1z8Mpep++QJOldL2hs/sBQf48=
|
||||||
|
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||||
|
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/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
|
||||||
|
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||||
|
github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0=
|
||||||
|
github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA=
|
||||||
|
github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo=
|
||||||
|
github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE=
|
||||||
|
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/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
||||||
|
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/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/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/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.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/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/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||||
|
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/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.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I=
|
||||||
|
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-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||||
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
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/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.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/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/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
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/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 v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
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/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||||
|
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/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/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
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.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
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.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.2/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.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.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
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/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.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/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/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
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/jsonschema v0.2.1 h1:NNFoKms+kut6ABPf6xiKNM5214jzxAhDBrPHCJ97Wg0=
|
||||||
|
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.3.4/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM=
|
||||||
|
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.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||||
|
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||||
|
github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y=
|
||||||
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
|
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||||
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
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/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.1/go.mod h1:JIydmIgCqETEHIiGYmN03gNSs2bghWBHEqnR/Lfmzb0=
|
||||||
|
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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
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.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/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/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
|
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||||
|
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
|
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||||
|
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||||
|
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
|
||||||
|
github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=
|
||||||
|
github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk=
|
||||||
|
github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc=
|
||||||
|
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
|
||||||
|
github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao=
|
||||||
|
github.com/tj/go-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.8.1 h1:CGuYNZF9IKZY/rfBe3lJpccSoIY1ytfvmgQT90cNOl4=
|
||||||
|
github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY=
|
||||||
|
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.5.1 h1:ZGu+bquAY23jsxDRcYpWjttRZrUz07LbiY77gUOHcr4=
|
||||||
|
github.com/vektah/gqlparser/v2 v2.5.1/go.mod h1:mPgqFBu/woKTVYWyNk8cO3kh4S/f4aRFZrvOnp3hmCs=
|
||||||
|
github.com/wundergraph/graphql-go-tools v1.57.2-0.20221005155749-a4fdba38990b h1:ACY3sLD/diOLMB6uvtpZPB0EWtrlH498mT0N7T1bnm4=
|
||||||
|
github.com/wundergraph/graphql-go-tools v1.57.2-0.20221005155749-a4fdba38990b/go.mod h1:gacwbFD8atVwDwzY5btN0/1QXvTprQ/gbT+XlZaQg4c=
|
||||||
|
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/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
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.5.0 h1:YJu6oD8vzxuSZZqPv2N3UyQzvWDWW0h/XTHiy0aiWSo=
|
||||||
|
gitlab.com/unboundsoftware/eventsourced/amqp v1.5.0/go.mod h1:dDShzDLym/gM7Mad26Y0K2TTLUy97hO46XC69Zi91qQ=
|
||||||
|
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.9.0/go.mod h1:nm3W8Gr8shALbgBepOocFRqDenG/oZFBON8WLIlOvck=
|
||||||
|
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.9.2/go.mod h1:i1Woh2JHIgAK27nFxS7q6/fAceSqX4VR7PYTizY5EMI=
|
||||||
|
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.9.3 h1:Khls+eq34tovtDBnSPHpC4tKswUlNDCYHH9Vl5A+hTo=
|
||||||
|
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.9.3/go.mod h1:c4rRdyIFW2s49hnLv5c5HsfL3I144mLnvvIOb0JZdpk=
|
||||||
|
gitlab.com/unboundsoftware/eventsourced/pg v1.9.0 h1:Hr8kgACHTFICDMV71waf/+CZVKkKJWzXqPGbomMUs70=
|
||||||
|
gitlab.com/unboundsoftware/eventsourced/pg v1.9.0/go.mod h1:tfwAGkvmnCpJCAIr94hljJzZXm+W1aHAXjuYJXbXYiI=
|
||||||
|
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-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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
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.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
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-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-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-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-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-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/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
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-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.0.0-20220722155257-8c9f86f7a55f/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-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.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.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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
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-20200815165600-90abf76919f3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
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.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
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-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-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618=
|
||||||
|
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 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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
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.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
# Where are all the schema files located? globs are supported eg src/**/*.graphqls
|
||||||
|
schema:
|
||||||
|
- graph/*.graphqls
|
||||||
|
|
||||||
|
# Where should the generated server code go?
|
||||||
|
exec:
|
||||||
|
filename: graph/generated/generated.go
|
||||||
|
package: generated
|
||||||
|
|
||||||
|
# Uncomment to enable federation
|
||||||
|
# federation:
|
||||||
|
# filename: graph/generated/federation.go
|
||||||
|
# package: generated
|
||||||
|
|
||||||
|
# Where should any generated models go?
|
||||||
|
model:
|
||||||
|
filename: graph/model/models_gen.go
|
||||||
|
package: model
|
||||||
|
|
||||||
|
# Where should the resolver implementations go?
|
||||||
|
resolver:
|
||||||
|
layout: follow-schema
|
||||||
|
dir: graph
|
||||||
|
package: graph
|
||||||
|
|
||||||
|
# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models
|
||||||
|
# struct_tag: json
|
||||||
|
|
||||||
|
# Optional: turn on to use []Thing instead of []*Thing
|
||||||
|
# omit_slice_element_pointers: false
|
||||||
|
|
||||||
|
# Optional: turn off to make struct-type struct fields not use pointers
|
||||||
|
# e.g. type Thing struct { FieldA OtherThing } instead of { FieldA *OtherThing }
|
||||||
|
# struct_fields_always_pointers: true
|
||||||
|
|
||||||
|
# Optional: turn off to make resolvers return values instead of pointers for structs
|
||||||
|
# resolvers_always_return_pointers: true
|
||||||
|
|
||||||
|
# Optional: set to speed up generation time by not performing a final validation pass.
|
||||||
|
# skip_validation: true
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
autobind:
|
||||||
|
# - "gitlab.com/unboundsoftware/schema/graph/model"
|
||||||
|
|
||||||
|
# This section declares type mapping between the GraphQL and go type systems
|
||||||
|
#
|
||||||
|
# The first line in each type will be used as defaults for resolver arguments and
|
||||||
|
# modelgen, the others will be allowed when binding to fields. Configure them to
|
||||||
|
# your liking
|
||||||
|
models:
|
||||||
|
ID:
|
||||||
|
model:
|
||||||
|
- github.com/99designs/gqlgen/graphql.ID
|
||||||
|
- github.com/99designs/gqlgen/graphql.Int
|
||||||
|
- github.com/99designs/gqlgen/graphql.Int64
|
||||||
|
- github.com/99designs/gqlgen/graphql.Int32
|
||||||
|
Int:
|
||||||
|
model:
|
||||||
|
- github.com/99designs/gqlgen/graphql.Int
|
||||||
|
- github.com/99designs/gqlgen/graphql.Int64
|
||||||
|
- github.com/99designs/gqlgen/graphql.Int32
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
|
||||||
|
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InputSubGraph struct {
|
||||||
|
Ref string `json:"ref"`
|
||||||
|
Service string `json:"service"`
|
||||||
|
URL *string `json:"url"`
|
||||||
|
WsURL *string `json:"wsUrl"`
|
||||||
|
Sdl string `json:"sdl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubGraph struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Service string `json:"service"`
|
||||||
|
URL *string `json:"url"`
|
||||||
|
WsURL *string `json:"wsUrl"`
|
||||||
|
Sdl string `json:"sdl"`
|
||||||
|
ChangedBy string `json:"changedBy"`
|
||||||
|
ChangedAt time.Time `json:"changedAt"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package graph
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/apex/log"
|
||||||
|
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
||||||
|
|
||||||
|
"gitlab.com/unboundsoftware/schemas/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run github.com/99designs/gqlgen
|
||||||
|
|
||||||
|
// This file will not be regenerated automatically.
|
||||||
|
//
|
||||||
|
// It serves as dependency injection for your app, add any dependencies you require here.
|
||||||
|
|
||||||
|
type Publisher interface {
|
||||||
|
Publish(event eventsourced.Event) error
|
||||||
|
Stop() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Resolver struct {
|
||||||
|
EventStore eventsourced.EventStore
|
||||||
|
Publisher Publisher
|
||||||
|
Logger log.Interface
|
||||||
|
Cache *cache.Cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) handler(aggregate eventsourced.Aggregate) (eventsourced.CommandHandler, error) {
|
||||||
|
return eventsourced.NewHandler(aggregate, r.EventStore, eventsourced.WithEventPublisher(r.Publisher))
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
type Query {
|
||||||
|
subGraphs(ref: String!): [SubGraph!]! @hasApiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
type Mutation {
|
||||||
|
updateSubGraph(input: InputSubGraph!): SubGraph! @hasApiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubGraph {
|
||||||
|
id: ID!
|
||||||
|
service: String!
|
||||||
|
url: String
|
||||||
|
wsUrl: String
|
||||||
|
sdl: String!
|
||||||
|
changedBy: String!
|
||||||
|
changedAt: Time!
|
||||||
|
}
|
||||||
|
|
||||||
|
input InputSubGraph {
|
||||||
|
ref: String!
|
||||||
|
service: String!
|
||||||
|
url: String
|
||||||
|
wsUrl: String
|
||||||
|
sdl: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
scalar Time
|
||||||
|
|
||||||
|
directive @hasApiKey on FIELD_DEFINITION
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package graph
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
||||||
|
|
||||||
|
"gitlab.com/unboundsoftware/schemas/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *Resolver) fetchSubGraph(subGraphId string) (*domain.SubGraph, error) {
|
||||||
|
subGraph := &domain.SubGraph{BaseAggregate: eventsourced.BaseAggregateFromString(subGraphId)}
|
||||||
|
_, err := r.handler(subGraph)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return subGraph, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package graph
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/wundergraph/graphql-go-tools/pkg/federation/sdlmerge"
|
||||||
|
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
||||||
|
|
||||||
|
"gitlab.com/unboundsoftware/schemas/domain"
|
||||||
|
"gitlab.com/unboundsoftware/schemas/graph/generated"
|
||||||
|
"gitlab.com/unboundsoftware/schemas/graph/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpdateSubGraph is the resolver for the updateSubGraph field.
|
||||||
|
func (r *mutationResolver) UpdateSubGraph(ctx context.Context, input model.InputSubGraph) (*model.SubGraph, error) {
|
||||||
|
subGraphId := r.Cache.SubGraphId(input.Ref, input.Service)
|
||||||
|
subGraph := &domain.SubGraph{}
|
||||||
|
if subGraphId != "" {
|
||||||
|
subGraph.BaseAggregate = eventsourced.BaseAggregateFromString(subGraphId)
|
||||||
|
}
|
||||||
|
handler, err := r.handler(subGraph)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
serviceSDLs := []string{input.Sdl}
|
||||||
|
for _, id := range r.Cache.Services(input.Ref) {
|
||||||
|
sg, err := r.fetchSubGraph(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if sg.Service != input.Service {
|
||||||
|
serviceSDLs = append(serviceSDLs, sg.Sdl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = sdlmerge.MergeSDLs(serviceSDLs...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = handler.Handle(domain.UpdateSubGraph{
|
||||||
|
Ref: input.Ref,
|
||||||
|
Service: input.Service,
|
||||||
|
Url: input.URL,
|
||||||
|
WSUrl: input.WsURL,
|
||||||
|
Sdl: input.Sdl,
|
||||||
|
Initiator: "Fetch name from API-key?",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubGraphs is the resolver for the subGraphs field.
|
||||||
|
func (r *queryResolver) SubGraphs(ctx context.Context, ref string) ([]*model.SubGraph, error) {
|
||||||
|
services := r.Cache.Services(ref)
|
||||||
|
subGraphs := make([]*model.SubGraph, len(services))
|
||||||
|
for i, id := range services {
|
||||||
|
sg, err := r.fetchSubGraph(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
subGraphs[i] = &model.SubGraph{
|
||||||
|
ID: sg.ID.String(),
|
||||||
|
Service: sg.Service,
|
||||||
|
URL: sg.Url,
|
||||||
|
WsURL: sg.WSUrl,
|
||||||
|
Sdl: sg.Sdl,
|
||||||
|
ChangedBy: sg.ChangedBy,
|
||||||
|
ChangedAt: sg.ChangedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subGraphs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mutation returns generated.MutationResolver implementation.
|
||||||
|
func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }
|
||||||
|
|
||||||
|
// Query returns generated.QueryResolver implementation.
|
||||||
|
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
|
||||||
|
|
||||||
|
type (
|
||||||
|
mutationResolver struct{ *Resolver }
|
||||||
|
queryResolver struct{ *Resolver }
|
||||||
|
)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
//go:build tools
|
||||||
|
// +build tools
|
||||||
|
|
||||||
|
package graph
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/99designs/gqlgen"
|
||||||
|
_ "github.com/99designs/gqlgen/graphql/introspection"
|
||||||
|
)
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
apiVersion: autoscaling/v2
|
||||||
|
kind: HorizontalPodAutoscaler
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: schemas
|
||||||
|
name: schemas
|
||||||
|
spec:
|
||||||
|
scaleTargetRef:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
name: schemas
|
||||||
|
minReplicas: 2
|
||||||
|
maxReplicas: 4
|
||||||
|
metrics:
|
||||||
|
- type: Resource
|
||||||
|
resource:
|
||||||
|
name: cpu
|
||||||
|
target:
|
||||||
|
type: Utilization
|
||||||
|
averageUtilization: 60
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: schemas
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: schemas
|
||||||
|
name: schemas
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/change-cause: "${TIMESTAMP} Deployed commit id: ${COMMIT}"
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: schemas
|
||||||
|
strategy:
|
||||||
|
rollingUpdate:
|
||||||
|
maxSurge: 1
|
||||||
|
maxUnavailable: 1
|
||||||
|
type: RollingUpdate
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: schemas
|
||||||
|
spec:
|
||||||
|
affinity:
|
||||||
|
podAntiAffinity:
|
||||||
|
preferredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
- weight: 100
|
||||||
|
podAffinityTerm:
|
||||||
|
labelSelector:
|
||||||
|
matchExpressions:
|
||||||
|
- key: "app"
|
||||||
|
operator: In
|
||||||
|
values:
|
||||||
|
- schemas
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
containers:
|
||||||
|
- name: schemas
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: "100Mi"
|
||||||
|
requests:
|
||||||
|
memory: "100Mi"
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 5
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
image: registry.gitlab.com/unboundsoftware/schemas:${COMMIT}
|
||||||
|
ports:
|
||||||
|
- name: api
|
||||||
|
containerPort: 8080
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: schemas
|
||||||
|
- secretRef:
|
||||||
|
name: rabbitmq
|
||||||
|
restartPolicy: Always
|
||||||
|
serviceAccountName: schemas
|
||||||
|
---
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: schemas
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
name: api
|
||||||
|
protocol: TCP
|
||||||
|
targetPort: 8080
|
||||||
|
selector:
|
||||||
|
app: schemas
|
||||||
|
type: NodePort
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: schemas-ingress
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: "nginx"
|
||||||
|
ingress.kubernetes.io/enable-cors: "true"
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: "local-schemas.unbound.se"
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /query
|
||||||
|
pathType: ImplementationSpecific
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: schemas
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: schemas-ingress
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: "alb"
|
||||||
|
alb.ingress.kubernetes.io/group.name: "unbound"
|
||||||
|
alb.ingress.kubernetes.io/scheme: internet-facing
|
||||||
|
alb.ingress.kubernetes.io/target-type: instance
|
||||||
|
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80},{"HTTPS": 443}]'
|
||||||
|
alb.ingress.kubernetes.io/ssl-redirect: "443"
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: "schemas.unbound.se"
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /query
|
||||||
|
pathType: ImplementationSpecific
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: schemas
|
||||||
|
port:
|
||||||
|
name: api
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: schemas
|
||||||
|
stringData:
|
||||||
|
API_KEY: supersecret123!
|
||||||
|
POSTGRES_URL: "postgres://postgres:postgres@postgres:5432/schemas?sslmode=disable"
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
apiVersion: external-secrets.io/v1beta1
|
||||||
|
kind: ExternalSecret
|
||||||
|
metadata:
|
||||||
|
name: schemas
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
refreshInterval: 1h
|
||||||
|
secretStoreRef:
|
||||||
|
name: external-secrets
|
||||||
|
kind: ClusterSecretStore
|
||||||
|
target:
|
||||||
|
creationPolicy: Owner
|
||||||
|
template:
|
||||||
|
data:
|
||||||
|
POSTGRES_URL: "postgres://{{ .DB_USERNAME }}:{{ .DB_PASSWORD }}@{{ .DB_HOST }}:{{ .DB_PORT }}/schemas?sslmode=disable"
|
||||||
|
API_KEY: "{{ .API_KEY }}"
|
||||||
|
dataFrom:
|
||||||
|
- extract:
|
||||||
|
key: services/schemas
|
||||||
|
- extract:
|
||||||
|
key: rds/postgres/prod-psql
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/99designs/gqlgen/graphql"
|
||||||
|
"github.com/apex/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContextKey string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ApiKey = ContextKey("apikey")
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewApiKey(apiKey string, logger log.Interface) *ApiKeyMiddleware {
|
||||||
|
return &ApiKeyMiddleware{
|
||||||
|
apiKey: apiKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApiKeyMiddleware struct {
|
||||||
|
apiKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ApiKeyMiddleware) Handler(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
header := r.Header.Get("x-api-key")
|
||||||
|
if len(header) == 0 {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
} else {
|
||||||
|
ctx := context.WithValue(r.Context(), ApiKey, header)
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ApiKeyMiddleware) Directive(ctx context.Context, obj interface{}, next graphql.Resolver) (res interface{}, err 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 u, ok := value.(string); ok {
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("current API-key is in wrong format")
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("no API-key found")
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupDB(driverName, url string) (*sqlx.DB, error) {
|
||||||
|
db, err := sqlx.Open(driverName, url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//if err := runMigrations(db); err != nil {
|
||||||
|
// return nil, err
|
||||||
|
//}
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// <!--go:embed migrations/*.sql-->
|
||||||
|
//var embedMigrations embed.FS
|
||||||
|
//
|
||||||
|
//func runMigrations(db *sqlx.DB) error {
|
||||||
|
// goose.SetBaseFS(embedMigrations)
|
||||||
|
//
|
||||||
|
// return goose.Up(db.DB, "migrations")
|
||||||
|
//}
|
||||||
Reference in New Issue
Block a user