diff --git a/.golangci.yaml b/.golangci.yaml index ea8eba1cb..503332907 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -33,7 +33,6 @@ linters: - godox # TODO - nestif # TODO - nlreturn # TODO - - paralleltest # TODO - protogetter # TODO - revive # TODO - testpackage # TODO diff --git a/libs/common/auth/auth_test.go b/libs/common/auth/auth_test.go index 4342838bc..95624cfef 100644 --- a/libs/common/auth/auth_test.go +++ b/libs/common/auth/auth_test.go @@ -7,6 +7,8 @@ import ( ) func TestIDTokenClaims_AsExpected(t *testing.T) { + t.Parallel() + tests := []struct { name string claims IDTokenClaims @@ -95,6 +97,8 @@ func TestIDTokenClaims_AsExpected(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + err := tt.claims.AsExpected() if tt.expectedError { require.Error(t, err) diff --git a/libs/common/go.mod b/libs/common/go.mod index eb7f61e63..027220d03 100644 --- a/libs/common/go.mod +++ b/libs/common/go.mod @@ -3,6 +3,7 @@ module common go 1.23 replace ( + hwdb => ../hwdb hwlocale => ../hwlocale hwutil => ../hwutil telemetry => ../telemetry @@ -18,6 +19,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 github.com/joho/godotenv v1.5.1 github.com/nicksnyder/go-i18n/v2 v2.4.1 + github.com/pashagolub/pgxmock/v4 v4.3.0 github.com/prometheus/client_golang v1.20.5 github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.10.0 @@ -33,6 +35,7 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 google.golang.org/grpc v1.69.2 google.golang.org/protobuf v1.36.1 + hwdb v0.0.0 hwlocale v0.0.0 hwutil v0.0.0 telemetry v0.0.0 @@ -51,6 +54,11 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect + github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.2 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -62,12 +70,14 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect go.opentelemetry.io/otel/metric v1.33.0 // indirect go.opentelemetry.io/proto/otlp v1.4.0 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/net v0.33.0 // indirect + golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect diff --git a/libs/common/go.sum b/libs/common/go.sum index 35c179561..3b698aed0 100644 --- a/libs/common/go.sum +++ b/libs/common/go.sum @@ -50,6 +50,16 @@ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 h1:kQ0NI7W1B3HwiN5gAYtY+X github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0/go.mod h1:zrT2dxOAjNFPRGjTUe2Xmb4q4YdUwVvQFV6xiCSf+z0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= +github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw= +github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= +github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= @@ -74,6 +84,8 @@ github.com/nicksnyder/go-i18n/v2 v2.4.1 h1:zwzjtX4uYyiaU02K5Ia3zSkpJZrByARkRB4V3 github.com/nicksnyder/go-i18n/v2 v2.4.1/go.mod h1:++Pl70FR6Cki7hdzZRnEEqdc2dJt+SAGotyFg/SvZMk= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= +github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s= +github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= @@ -94,9 +106,13 @@ github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0 h1:EhPtK0mgrgaTMXpegE69hvoSOVC1Ahk8+QJ9B8b+OdU= +github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0/go.mod h1:5LtFrNEkgzxHvXPO9eOvcXsSn9/KeKYgx9kjeI2oXQI= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc= @@ -131,6 +147,8 @@ golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/libs/common/grpc.go b/libs/common/grpc.go index 6ae93d02d..a3eb31ae6 100644 --- a/libs/common/grpc.go +++ b/libs/common/grpc.go @@ -2,11 +2,14 @@ package common import ( "context" + "hwdb" "hwutil" "net" "telemetry" - auth "common/auth" + "github.com/prometheus/client_golang/prometheus" + + "common/auth" "common/hwgrpc" "github.com/dapr/dapr/pkg/proto/runtime/v1" @@ -41,7 +44,7 @@ func StartNewGRPCServer(ctx context.Context, addr string, registerServerHook fun } // dapr/grpc service - service, ok := daprd.NewServiceWithListener(listener, DefaultServerOptions()...).(*daprd.Server) + service, ok := daprd.NewServiceWithListener(listener, DefaultServerOptions(ctx)...).(*daprd.Server) if !ok { log.Fatal().Msg("dapr service listener is not a *daprd.Server") } @@ -87,13 +90,13 @@ func StartNewGRPCServer(ctx context.Context, addr string, registerServerHook fun // DefaultUnaryInterceptors returns the slice of default interceptors for unary gRPC calls // // chain := grpc.ChainUnaryInterceptor(common.DefaultUnaryInterceptors()...) -func DefaultUnaryInterceptors(metrics *prometheusGrpcProvider.ServerMetrics) []grpc.UnaryServerInterceptor { +func DefaultUnaryInterceptors(ctx context.Context, panicsRecovered prometheus.Counter) []grpc.UnaryServerInterceptor { return []grpc.UnaryServerInterceptor{ - metrics.UnaryServerInterceptor(), - hwgrpc.UnaryPanicRecoverInterceptor(), + hwgrpc.UnaryPanicRecoverInterceptor(panicsRecovered), hwgrpc.UnaryLoggingInterceptor, hwgrpc.UnaryErrorQualityControlInterceptor, hwgrpc.UnaryLocaleInterceptor, + hwgrpc.UnaryDBInterceptor(hwdb.GetDB(ctx)), defaultUnaryAuthInterceptor, defaultUnaryOrganizationInterceptor, hwgrpc.UnaryValidateInterceptor, @@ -104,13 +107,13 @@ func DefaultUnaryInterceptors(metrics *prometheusGrpcProvider.ServerMetrics) []g // DefaultStreamInterceptors returns the slice of default interceptors for stream gRPC calls // // chain := grpc.ChainStreamInterceptor(common.DefaultStreamInterceptors()...) -func DefaultStreamInterceptors(metrics *prometheusGrpcProvider.ServerMetrics) []grpc.StreamServerInterceptor { +func DefaultStreamInterceptors(ctx context.Context, panicsRecovered prometheus.Counter) []grpc.StreamServerInterceptor { return []grpc.StreamServerInterceptor{ - metrics.StreamServerInterceptor(), - hwgrpc.StreamPanicRecoverInterceptor(), + hwgrpc.StreamPanicRecoverInterceptor(panicsRecovered), hwgrpc.StreamLoggingInterceptor, hwgrpc.StreamErrorQualityControlInterceptor, hwgrpc.StreamLocaleInterceptor, + hwgrpc.StreamDBInterceptor(hwdb.GetDB(ctx)), defaultStreamAuthInterceptor, defaultStreamOrganizationInterceptor, hwgrpc.StreamValidateInterceptor, @@ -118,13 +121,32 @@ func DefaultStreamInterceptors(metrics *prometheusGrpcProvider.ServerMetrics) [] } } -func DefaultServerOptions() []grpc.ServerOption { +// DefaultServerOptions respects telemetry.WithPrometheusRegistry and hwdb.WithDB context values. +// These will then be propagated into request contexts, if provided. +func DefaultServerOptions(ctx context.Context) []grpc.ServerOption { + // counters + panicsRecovered := hwgrpc.NewPanicsRecoveredCounter(ctx) + + // default interceptors + unaryInterceptors := DefaultUnaryInterceptors(ctx, panicsRecovered) + streamInterceptors := DefaultStreamInterceptors(ctx, panicsRecovered) + // register new metrics collector with prometheus - metrics := prometheusGrpcProvider.NewServerMetrics() - telemetry.PrometheusRegistry().MustRegister(metrics) + promRegistry := telemetry.PrometheusRegistry(ctx) + if promRegistry != nil { + metrics := prometheusGrpcProvider.NewServerMetrics() + promRegistry.MustRegister(metrics) + + // prepend metrics interceptor + unaryInterceptors = hwutil.Prepend(metrics.UnaryServerInterceptor(), unaryInterceptors) + streamInterceptors = hwutil.Prepend(metrics.StreamServerInterceptor(), streamInterceptors) + } + + // create chains + unaryInterceptorChain := grpc.ChainUnaryInterceptor(unaryInterceptors...) + streamInterceptorChain := grpc.ChainStreamInterceptor(streamInterceptors...) - unaryInterceptorChain := grpc.ChainUnaryInterceptor(DefaultUnaryInterceptors(metrics)...) - streamInterceptorChain := grpc.ChainStreamInterceptor(DefaultStreamInterceptors(metrics)...) + // otel stats handler statsHandler := grpc.StatsHandler(otelgrpc.NewServerHandler()) return []grpc.ServerOption{unaryInterceptorChain, streamInterceptorChain, statsHandler} diff --git a/libs/common/hwgrpc/db_interceptor.go b/libs/common/hwgrpc/db_interceptor.go new file mode 100644 index 000000000..9e420fa78 --- /dev/null +++ b/libs/common/hwgrpc/db_interceptor.go @@ -0,0 +1,32 @@ +package hwgrpc + +import ( + "context" + "hwdb" + + "google.golang.org/grpc" +) + +func UnaryDBInterceptor(db hwdb.DBTX) grpc.UnaryServerInterceptor { + return func( + ctx context.Context, + req any, + _ *grpc.UnaryServerInfo, + next grpc.UnaryHandler, + ) (any, error) { + return next(hwdb.WithDB(ctx, db), req) + } +} + +func StreamDBInterceptor(db hwdb.DBTX) grpc.StreamServerInterceptor { + return func( + req any, + stream grpc.ServerStream, + _ *grpc.StreamServerInfo, + next grpc.StreamHandler, + ) error { + ctx := hwdb.WithDB(stream.Context(), db) + stream = WrapServerStream(stream, ctx) + return next(req, stream) + } +} diff --git a/libs/common/hwgrpc/db_interceptor_test.go b/libs/common/hwgrpc/db_interceptor_test.go new file mode 100644 index 000000000..cea60743b --- /dev/null +++ b/libs/common/hwgrpc/db_interceptor_test.go @@ -0,0 +1,118 @@ +package hwgrpc_test + +import ( + "context" + "hwdb" + "testing" + + "github.com/google/uuid" + "github.com/pashagolub/pgxmock/v4" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + + "common/hwgrpc" +) + +func TestDBInterceptor(t *testing.T) { + t.Parallel() + + type testCase struct { + db hwdb.DBTX + handler func(t *testing.T, ctx context.Context) + } + + existingDB, err := pgxmock.NewPool() + if err != nil { + panic(err) + } + + testMatrix := map[string]testCase{ + "missing db": { + db: nil, + handler: func(t *testing.T, ctx context.Context) { + t.Helper() + + // GetDB will return nil + require.Nil(t, hwdb.GetDB(ctx)) + + // MustGetDB will not work + require.Panics(t, func() { + hwdb.MustGetDB(ctx) + }) + }, + }, + "existing db": { + db: existingDB, + handler: func(t *testing.T, ctx context.Context) { + t.Helper() + + require.Equal(t, existingDB, hwdb.MustGetDB(ctx)) + }, + }, + } + + for name, test := range testMatrix { + t.Run(name, func(t *testing.T) { + t.Parallel() + ctx := context.Background() + + // + // Stream + // + + streamInterceptor := hwgrpc.StreamDBInterceptor(test.db) + streamHandlerWasCalled := false + + streamNext := func(srv any, stream grpc.ServerStream) error { + streamHandlerWasCalled = true + test.handler(t, stream.Context()) + return nil + } + + stream := &fakeServerStream{ctx: ctx} + err = streamInterceptor(ctx, stream, nil, streamNext) + + // next handler is properly called + require.NoError(t, err) + require.True(t, streamHandlerWasCalled) + + // + // Unary + // + unaryInterceptor := hwgrpc.UnaryDBInterceptor(test.db) + + expValue := uuid.New() + unaryHandlerWasCalled := false + + unaryNext := func(ctx context.Context, req any) (any, error) { + unaryHandlerWasCalled = true + test.handler(t, ctx) + return expValue, nil + } + + value, err := unaryInterceptor(ctx, nil, nil, unaryNext) + + // unaryNext handler is properly called + require.NoError(t, err) + require.True(t, unaryHandlerWasCalled) + require.Equal(t, expValue, value) + }) + } +} + +type fakeServerStream struct { + grpc.ServerStream + ctx context.Context //nolint:containedctx +} + +func (f *fakeServerStream) Context() context.Context { + return f.ctx +} + +func (f *fakeServerStream) SendMsg(_ any) error { + return nil +} + +func (f *fakeServerStream) RecvMsg(_ any) error { + return nil +} diff --git a/libs/common/hwgrpc/hwgrpc_test.go b/libs/common/hwgrpc/hwgrpc_test.go index cbb427540..a07ca8e74 100644 --- a/libs/common/hwgrpc/hwgrpc_test.go +++ b/libs/common/hwgrpc/hwgrpc_test.go @@ -21,6 +21,7 @@ func arrayEq(t *testing.T, expected []language.Tag, parsed []language.Tag) { } func TestParseLocales(t *testing.T) { + t.Parallel() const mdnExample = "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5" expected := []language.Tag{language.MustParse("fr-CH"), language.French, language.English, language.German} parsed, ok := hwgrpc.ParseLocales(mdnExample) @@ -32,6 +33,7 @@ func TestParseLocales(t *testing.T) { } func TestParseLocalesReordered(t *testing.T) { + t.Parallel() const mdnExample = "fr-CH, de;q=0.7, en;q=0.8, fr;q=0.9, *;q=0.5" expected := []language.Tag{language.MustParse("fr-CH"), language.French, language.English, language.German} parsed, ok := hwgrpc.ParseLocales(mdnExample) @@ -43,6 +45,7 @@ func TestParseLocalesReordered(t *testing.T) { } func TestParseLocalesSimple(t *testing.T) { + t.Parallel() const mdnExample = "de" expected := []language.Tag{language.German} parsed, ok := hwgrpc.ParseLocales(mdnExample) diff --git a/libs/common/hwgrpc/locale_interceptor_test.go b/libs/common/hwgrpc/locale_interceptor_test.go index ef4995156..4d3b21b18 100644 --- a/libs/common/hwgrpc/locale_interceptor_test.go +++ b/libs/common/hwgrpc/locale_interceptor_test.go @@ -12,6 +12,8 @@ import ( ) func TestLocaleInterceptor(t *testing.T) { + t.Parallel() + testCases := map[string][]string{ "de": {"de"}, "en": {"en"}, @@ -24,6 +26,8 @@ func TestLocaleInterceptor(t *testing.T) { for acceptLanguageHeader, expectedLocalesStrings := range testCases { t.Run(fmt.Sprintf("Test localeInterceptor with accept-language header of '%s'", acceptLanguageHeader), func(t *testing.T) { + t.Parallel() + ctx := context.Background() md := metadata.New(map[string]string{ diff --git a/libs/common/hwgrpc/panic_interceptor.go b/libs/common/hwgrpc/panic_interceptor.go index 172efd7ac..22fdd5a75 100644 --- a/libs/common/hwgrpc/panic_interceptor.go +++ b/libs/common/hwgrpc/panic_interceptor.go @@ -10,6 +10,8 @@ import ( "common/hwerr" "common/locale" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery" "github.com/prometheus/client_golang/prometheus" zlog "github.com/rs/zerolog/log" @@ -17,12 +19,19 @@ import ( "google.golang.org/grpc/codes" ) -var panicsRecovered = telemetry.NewLazyCounter(prometheus.CounterOpts{ - Name: "services_panics_recovered_total", - Help: "Total number of panics recovered by PanicRecoverInterceptor", -}) +func NewPanicsRecoveredCounter(ctx context.Context) prometheus.Counter { + registry := telemetry.PrometheusRegistry(ctx) + if registry == nil { // prometheus not set up + return nil // TODO: replace with a no-op to prevent nil handling + } + + return promauto.With(registry).NewCounter(prometheus.CounterOpts{ + Name: "services_panics_recovered_total", + Help: "Total number of panics recovered by PanicRecoverInterceptor", + }) +} -func recoveryHandlerFn() recovery.RecoveryHandlerFuncContext { +func recoveryHandlerFn(panicsRecovered prometheus.Counter) recovery.RecoveryHandlerFuncContext { return func(ctx context.Context, recovered any) (err error) { zlog.Ctx(ctx). Error(). @@ -32,24 +41,22 @@ func recoveryHandlerFn() recovery.RecoveryHandlerFuncContext { _, _ = fmt.Fprintln(os.Stderr, string(debug.Stack())) - panicsRecovered.Counter().Inc() + if panicsRecovered != nil { + panicsRecovered.Inc() + } return hwerr.NewStatusError(ctx, codes.Internal, "panic recovered", locale.GenericError(ctx)) } } -func UnaryPanicRecoverInterceptor() grpc.UnaryServerInterceptor { - panicsRecovered.Ensure() - +func UnaryPanicRecoverInterceptor(panicsRecovered prometheus.Counter) grpc.UnaryServerInterceptor { return recovery.UnaryServerInterceptor( - recovery.WithRecoveryHandlerContext(recoveryHandlerFn()), + recovery.WithRecoveryHandlerContext(recoveryHandlerFn(panicsRecovered)), ) } -func StreamPanicRecoverInterceptor() grpc.StreamServerInterceptor { - panicsRecovered.Ensure() - +func StreamPanicRecoverInterceptor(panicsRecovered prometheus.Counter) grpc.StreamServerInterceptor { return recovery.StreamServerInterceptor( - recovery.WithRecoveryHandlerContext(recoveryHandlerFn()), + recovery.WithRecoveryHandlerContext(recoveryHandlerFn(panicsRecovered)), ) } diff --git a/libs/common/hwgrpc/panic_interceptor_test.go b/libs/common/hwgrpc/panic_interceptor_test.go index bcfab10e9..d1d5844b8 100644 --- a/libs/common/hwgrpc/panic_interceptor_test.go +++ b/libs/common/hwgrpc/panic_interceptor_test.go @@ -37,13 +37,15 @@ type RecoverySuite struct { } func TestPanicRecoverInterceptor(t *testing.T) { - telemetry.SetupMetrics(context.Background(), nil) + t.Parallel() + ctx := telemetry.SetupMetrics(context.Background(), nil) + counter := NewPanicsRecoveredCounter(ctx) s := &RecoverySuite{ InterceptorTestSuite: &testpb.InterceptorTestSuite{ TestService: &recoveryAssertService{TestServiceServer: &testpb.TestPingService{}}, ServerOpts: []grpc.ServerOption{ - grpc.StreamInterceptor(StreamPanicRecoverInterceptor()), - grpc.UnaryInterceptor(UnaryPanicRecoverInterceptor()), + grpc.StreamInterceptor(StreamPanicRecoverInterceptor(counter)), + grpc.UnaryInterceptor(UnaryPanicRecoverInterceptor(counter)), }, }, } diff --git a/libs/common/setup.go b/libs/common/setup.go index 8ac5b28b1..84714a6d1 100644 --- a/libs/common/setup.go +++ b/libs/common/setup.go @@ -123,7 +123,7 @@ func Setup(serviceName, version string, opts ...SetupOption) context.Context { } } - telemetry.SetupMetrics(ctx, Shutdown) + ctx = telemetry.SetupMetrics(ctx, Shutdown) if len(version) == 0 && Mode == ProductionMode { log.Warn().Msg("Version is empty in production build! Recompile using ldflag '-X main.Version='") diff --git a/libs/common/test/grpc_test.go b/libs/common/test/grpc_test.go index 8ad783022..3ca3396dd 100644 --- a/libs/common/test/grpc_test.go +++ b/libs/common/test/grpc_test.go @@ -12,5 +12,6 @@ import ( // - AuthenticatedUserMetadata accesses the AuthenticatedUserClaim map incorrectly // - AuthenticatedUserClaim map is not JSON-able func TestAuthenticatedUserMetadataDoesNotCrash(t *testing.T) { + t.Parallel() _ = AuthenticatedUserMetadata(uuid.NewString()) } diff --git a/libs/hwauthz/commonperm/userOrg.go b/libs/hwauthz/commonperm/userOrg.go index b4eeb5422..60a1542b4 100644 --- a/libs/hwauthz/commonperm/userOrg.go +++ b/libs/hwauthz/commonperm/userOrg.go @@ -28,3 +28,9 @@ func OrganizationFromCtx(ctx context.Context) Organization { organizationID := auth.MustGetOrganizationID(ctx) return Organization(organizationID) } + +const ( + OrganizationMember = "member" + OrganizationLeader = "leader" + UserOrganization = "organization" +) diff --git a/libs/hwauthz/go.mod b/libs/hwauthz/go.mod index 1de54c859..e9e57eca0 100644 --- a/libs/hwauthz/go.mod +++ b/libs/hwauthz/go.mod @@ -4,6 +4,7 @@ go 1.23 replace ( common => ../common + hwdb => ../hwdb hwlocale => ../hwlocale hwtesting => ../hwtesting hwutil => ../hwutil @@ -63,6 +64,11 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.2 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/jzelinskie/stringz v0.0.3 // indirect github.com/klauspost/compress v1.17.9 // indirect @@ -102,6 +108,7 @@ require ( github.com/testcontainers/testcontainers-go/modules/redis v0.34.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect @@ -118,6 +125,7 @@ require ( golang.org/x/crypto v0.31.0 // indirect golang.org/x/net v0.33.0 // indirect golang.org/x/oauth2 v0.25.0 // indirect + golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect @@ -125,5 +133,6 @@ require ( google.golang.org/protobuf v1.36.1 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + hwdb v0.0.0 // indirect hwlocale v0.0.0 // indirect ) diff --git a/libs/hwauthz/go.sum b/libs/hwauthz/go.sum index 40000a73a..a0ee73540 100644 --- a/libs/hwauthz/go.sum +++ b/libs/hwauthz/go.sum @@ -127,14 +127,16 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw= +github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= -github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= -github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= +github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jzelinskie/stringz v0.0.3 h1:0GhG3lVMYrYtIvRbxvQI6zqRTT1P1xyQlpa0FhfUXas= @@ -192,6 +194,8 @@ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2sz github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= +github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s= +github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A= 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= @@ -255,6 +259,8 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0 h1:EhPtK0mgrgaTMXpegE69hvoSOVC1Ahk8+QJ9B8b+OdU= +github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0/go.mod h1:5LtFrNEkgzxHvXPO9eOvcXsSn9/KeKYgx9kjeI2oXQI= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= diff --git a/libs/hwauthz/spicedb/spicedb_test.go b/libs/hwauthz/spicedb/spicedb_test.go index 8829a8b0f..0a4643003 100644 --- a/libs/hwauthz/spicedb/spicedb_test.go +++ b/libs/hwauthz/spicedb/spicedb_test.go @@ -52,6 +52,7 @@ func TestMain(m *testing.M) { } func TestBulkCheck(t *testing.T) { + t.Parallel() ctx := context.Background() client := NewSpiceDBAuthZ() @@ -102,6 +103,8 @@ func TestBulkCheck(t *testing.T) { } func TestLookupResources(t *testing.T) { + t.Parallel() + ctx := context.Background() client := NewSpiceDBAuthZ() @@ -134,6 +137,8 @@ func TestLookupResources(t *testing.T) { } func TestDeleteObject(t *testing.T) { + t.Parallel() + // client ctx := context.Background() client := NewSpiceDBAuthZ() diff --git a/libs/hwauthz/spicedb/userOrg.go b/libs/hwauthz/spicedb/userOrg.go new file mode 100644 index 000000000..379dd86ee --- /dev/null +++ b/libs/hwauthz/spicedb/userOrg.go @@ -0,0 +1,31 @@ +package spicedb + +import ( + "context" + + "github.com/google/uuid" + "hwauthz" + "hwauthz/commonperm" +) + +func AddUserToOrganization( + ctx context.Context, + authz hwauthz.AuthZ, + userID, organizationID uuid.UUID, + leader bool, +) error { + permUser := commonperm.User(userID) + permOrg := commonperm.Organization(organizationID) + + var role hwauthz.Relation = commonperm.OrganizationMember + if leader { + role = commonperm.OrganizationLeader + } + + rel := hwauthz.NewRelationship(permUser, role, permOrg) + backRel := hwauthz.NewRelationship(permOrg, commonperm.UserOrganization, permUser) + if _, err := authz.Create(rel).Create(backRel).Commit(ctx); err != nil { + return err + } + return nil +} diff --git a/libs/hwdb/go.mod b/libs/hwdb/go.mod index 45c4c4a3b..ef9680a39 100644 --- a/libs/hwdb/go.mod +++ b/libs/hwdb/go.mod @@ -15,7 +15,9 @@ require ( github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa github.com/jackc/pgx/v5 v5.7.2 github.com/nicksnyder/go-i18n/v2 v2.4.1 + github.com/pashagolub/pgxmock/v4 v4.3.0 github.com/rs/zerolog v1.33.0 + github.com/stretchr/testify v1.10.0 github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0 go.opentelemetry.io/otel v1.33.0 go.opentelemetry.io/otel/trace v1.33.0 @@ -30,6 +32,7 @@ require ( github.com/BurntSushi/toml v1.4.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fatih/structs v1.1.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/go-logr/logr v1.4.2 // indirect @@ -45,6 +48,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect @@ -57,4 +61,5 @@ require ( golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/libs/hwdb/go.sum b/libs/hwdb/go.sum index 641df3d73..dab3c7e25 100644 --- a/libs/hwdb/go.sum +++ b/libs/hwdb/go.sum @@ -44,6 +44,10 @@ github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= @@ -58,6 +62,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/nicksnyder/go-i18n/v2 v2.4.1 h1:zwzjtX4uYyiaU02K5Ia3zSkpJZrByARkRB4V3YPrr0g= github.com/nicksnyder/go-i18n/v2 v2.4.1/go.mod h1:++Pl70FR6Cki7hdzZRnEEqdc2dJt+SAGotyFg/SvZMk= +github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s= +github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= @@ -70,6 +76,8 @@ github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= @@ -108,6 +116,8 @@ google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7 google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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= diff --git a/libs/hwdb/helper_test.go b/libs/hwdb/helper_test.go index 75d37597d..4135b25cf 100644 --- a/libs/hwdb/helper_test.go +++ b/libs/hwdb/helper_test.go @@ -8,13 +8,19 @@ import ( ) func TestPbToTimestamp(t *testing.T) { + t.Parallel() + t.Run("src = nil", func(t *testing.T) { + t.Parallel() + if hwdb.PbToTimestamp(nil).Valid { t.Error() } }) t.Run("src not nil", func(t *testing.T) { + t.Parallel() + src := timestamppb.Timestamp{ Seconds: 0, Nanos: 0, diff --git a/libs/hwdb/setup.go b/libs/hwdb/setup.go index 42137f6ec..a36673a4a 100644 --- a/libs/hwdb/setup.go +++ b/libs/hwdb/setup.go @@ -2,8 +2,10 @@ package hwdb import ( "context" + "errors" "fmt" "hwutil" + "hwutil/errs" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgconn" @@ -24,9 +26,6 @@ type DBTX interface { Begin(ctx context.Context) (pgx.Tx, error) } -// connectionPool is set in OpenDatabase() and allows for concurrent access to the database -var connectionPool DBTX - // SetupDatabaseFromEnv prefers the env POSTGRES_DSN and will be configured the database connection accordingly. // When this env does not exist, a fallback to the following envs with proper default values will take place. // [NAME_OF_THE_ENV] (DEFAULT) @@ -37,7 +36,7 @@ var connectionPool DBTX // POSTGRES_PORT (5432) // // SetupDatabaseFromEnv returns a close function, which has to be called in order to shut down the database cleanly -func SetupDatabaseFromEnv(context context.Context) func() { +func SetupDatabaseFromEnv(ctx context.Context) (context.Context, func()) { log.Info().Msg("connecting to postgres ...") dsn := hwutil.GetEnvOr("POSTGRES_DSN", "") @@ -46,17 +45,16 @@ func SetupDatabaseFromEnv(context context.Context) func() { log.Debug().Msg("POSTGRES_DSN env not found, built dsn from POSTGRES_HOST, ... envs") } - dbpool, err := openDatabasePool(context, dsn) + dbpool, err := openDatabasePool(ctx, dsn) if err != nil { log.Fatal().Err(err).Msg("could not connect to database") } - // set connectionPool - connectionPool = dbpool + ctx = WithDB(ctx, dbpool) log.Info().Msg("connected to postgres") - return func() { + return ctx, func() { log.Info().Msg("closing db pool") dbpool.Close() } @@ -80,7 +78,6 @@ func buildDsnFromEnvs() string { } // openDatabasePool simply opens a new database pool to the dsn provided -// it does not set up connectionPool! func openDatabasePool(ctx context.Context, dsn string) (*pgxpool.Pool, error) { pgxConfig, err := pgxpool.ParseConfig(dsn) if err != nil { @@ -106,16 +103,32 @@ func openDatabasePool(ctx context.Context, dsn string) (*pgxpool.Pool, error) { return dbpool, nil } -func GetDB() DBTX { - if connectionPool == nil { - log.Error(). - Msg("GetDB called without set-up database, you will run into nil-pointers. " + - "Make sure to call SetupDatabaseFromEnv()!") +type dbKey struct{} + +func WithDB(ctx context.Context, pool DBTX) context.Context { + return context.WithValue(ctx, dbKey{}, pool) +} + +// GetDB attempts to retrieve the DBTX stored in the context, and returns nil if its missing. +// Also see MustGetDB. +func GetDB(ctx context.Context) DBTX { + value := ctx.Value(dbKey{}) + db, ok := value.(DBTX) + // we allow nil (which will not be ok), else panic + if db != nil && !ok { + panic(errs.NewCastError("DBTX", value)) } - return connectionPool + return db } -// TestingSetDB should only be called by testing code -func TestingSetDB(pool DBTX) { - connectionPool = pool +var ErrDBMissing = errors.New("MustGetDB() called without set-up database, use hwdb.WithDB()") + +// MustGetDB does the same as GetDB, but panics if no DBTX is found in the context. +func MustGetDB(ctx context.Context) DBTX { + if db := GetDB(ctx); db != nil { + return db + } + + log.Error().Err(ErrDBMissing).Send() + panic(ErrDBMissing) } diff --git a/libs/hwdb/setup_test.go b/libs/hwdb/setup_test.go new file mode 100644 index 000000000..fa987e15a --- /dev/null +++ b/libs/hwdb/setup_test.go @@ -0,0 +1,80 @@ +package hwdb + +import ( + "context" + "testing" + + "github.com/pashagolub/pgxmock/v4" + "github.com/stretchr/testify/require" +) + +func TestMustGetDB(t *testing.T) { + t.Parallel() + + t.Run("Green", func(t *testing.T) { + t.Parallel() + + dbMock, err := pgxmock.NewPool() + if err != nil { + panic(err) + } + + ctx := WithDB(context.Background(), dbMock) + require.Equal(t, dbMock, MustGetDB(ctx)) + }) + + t.Run("Panic", func(t *testing.T) { + t.Parallel() + + ctx := context.Background() + require.Panics(t, func() { + _ = MustGetDB(ctx) + }) + }) +} + +func TestGetDB(t *testing.T) { + t.Parallel() + + defaultContext := context.Background() + + validDB, err := pgxmock.NewPool() + if err != nil { + panic(err) + } + + testCases := []struct { + name string + useDefault bool + db DBTX + }{ + { + name: "not calling WithDB", + useDefault: true, + db: nil, + }, + { + name: "setting nil db", + useDefault: false, + db: nil, + }, + { + name: "setting valid db", + useDefault: false, + db: validDB, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + + ctx := defaultContext + if !testCase.useDefault { + ctx = WithDB(ctx, testCase.db) + } + + require.Equal(t, testCase.db, GetDB(ctx)) + }) + } +} diff --git a/libs/hwes/aggregate_test.go b/libs/hwes/aggregate_test.go index b1408fd12..bf5c7036c 100644 --- a/libs/hwes/aggregate_test.go +++ b/libs/hwes/aggregate_test.go @@ -8,6 +8,8 @@ import ( ) func TestResolveAggregateIDAndTypeFromStreamID(t *testing.T) { + t.Parallel() + testCases := []struct { streamID string expectedError bool diff --git a/libs/hwes/event_test.go b/libs/hwes/event_test.go index 335847777..05e00ab07 100644 --- a/libs/hwes/event_test.go +++ b/libs/hwes/event_test.go @@ -11,6 +11,8 @@ import ( ) func TestEventWithUserID(t *testing.T) { + t.Parallel() + ctx := context.Background() u := uuid.New() e := hwes.Event{} @@ -25,6 +27,8 @@ func TestEventWithUserID(t *testing.T) { } func TestEventWithOrganizationID(t *testing.T) { + t.Parallel() + ctx := context.Background() u := uuid.New() e := hwes.Event{} diff --git a/libs/hwes/go.mod b/libs/hwes/go.mod index 8fa8c634e..66bd21675 100644 --- a/libs/hwes/go.mod +++ b/libs/hwes/go.mod @@ -5,6 +5,7 @@ go 1.23 replace ( common => ../common gen => ../../gen/go + hwdb => ../hwdb hwlocale => ../hwlocale hwutil => ../hwutil telemetry => ../telemetry @@ -41,6 +42,11 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect + github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.2 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/leodido/go-urn v1.4.0 // indirect @@ -55,6 +61,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect go.opentelemetry.io/otel v1.33.0 // indirect @@ -69,6 +76,7 @@ require ( golang.org/x/crypto v0.31.0 // indirect golang.org/x/net v0.33.0 // indirect golang.org/x/oauth2 v0.25.0 // indirect + golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect @@ -76,5 +84,6 @@ require ( google.golang.org/grpc v1.69.2 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + hwdb v0.0.0 // indirect hwlocale v0.0.0 // indirect ) diff --git a/libs/hwes/go.sum b/libs/hwes/go.sum index a623f3b56..862052cda 100644 --- a/libs/hwes/go.sum +++ b/libs/hwes/go.sum @@ -82,6 +82,16 @@ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 h1:kQ0NI7W1B3HwiN5gAYtY+X github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0/go.mod h1:zrT2dxOAjNFPRGjTUe2Xmb4q4YdUwVvQFV6xiCSf+z0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= +github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw= +github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= +github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= @@ -124,6 +134,8 @@ github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= +github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s= +github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A= 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 v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -153,7 +165,9 @@ github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/testcontainers/testcontainers-go v0.30.0 h1:jmn/XS22q4YRrcMwWg0pAwlClzs/abopbsBzrepyc4E= @@ -162,6 +176,8 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0 h1:EhPtK0mgrgaTMXpegE69hvoSOVC1Ahk8+QJ9B8b+OdU= +github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0/go.mod h1:5LtFrNEkgzxHvXPO9eOvcXsSn9/KeKYgx9kjeI2oXQI= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= diff --git a/libs/hwes/integration_test.go b/libs/hwes/integration_test.go index 0419d2ee6..f8ae16f50 100644 --- a/libs/hwes/integration_test.go +++ b/libs/hwes/integration_test.go @@ -103,6 +103,8 @@ func NewUsernameUpdatedEvent(a hwes.Aggregate, previousUsername, username string } func TestIntegration(t *testing.T) { + t.Parallel() + ctx := context.Background() aggregateStore := test.NewAggregateStore() @@ -154,6 +156,8 @@ func TestIntegration(t *testing.T) { var ErrTest = errors.New("test error") func TestAggregateBase_RegisterEventListener_HandleEvent(t *testing.T) { + t.Parallel() + aggregate := NewUserAggregate(uuid.New()) userInvalidEvent, err := NewUserInvalidEvent(aggregate) diff --git a/libs/hwtesting/go.mod b/libs/hwtesting/go.mod index b8564929f..d812cfbd3 100644 --- a/libs/hwtesting/go.mod +++ b/libs/hwtesting/go.mod @@ -5,6 +5,7 @@ go 1.23 replace ( common => ../common hwauthz => ../hwauthz + hwdb => ../hwdb hwlocale => ../hwlocale hwutil => ../hwutil telemetry => ../telemetry @@ -64,6 +65,11 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.2 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/jzelinskie/stringz v0.0.3 // indirect github.com/klauspost/compress v1.17.9 // indirect @@ -101,6 +107,7 @@ require ( github.com/stretchr/testify v1.10.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect @@ -118,6 +125,7 @@ require ( golang.org/x/crypto v0.31.0 // indirect golang.org/x/net v0.33.0 // indirect golang.org/x/oauth2 v0.25.0 // indirect + golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect @@ -125,6 +133,7 @@ require ( google.golang.org/protobuf v1.36.1 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + hwdb v0.0.0 // indirect hwlocale v0.0.0 // indirect telemetry v0.0.0 // indirect ) diff --git a/libs/hwtesting/go.sum b/libs/hwtesting/go.sum index 40000a73a..a0ee73540 100644 --- a/libs/hwtesting/go.sum +++ b/libs/hwtesting/go.sum @@ -127,14 +127,16 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw= +github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= -github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= -github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= +github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jzelinskie/stringz v0.0.3 h1:0GhG3lVMYrYtIvRbxvQI6zqRTT1P1xyQlpa0FhfUXas= @@ -192,6 +194,8 @@ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2sz github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= +github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s= +github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A= 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= @@ -255,6 +259,8 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0 h1:EhPtK0mgrgaTMXpegE69hvoSOVC1Ahk8+QJ9B8b+OdU= +github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0/go.mod h1:5LtFrNEkgzxHvXPO9eOvcXsSn9/KeKYgx9kjeI2oXQI= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= diff --git a/libs/hwtesting/grpc.go b/libs/hwtesting/grpc.go index f4cd7a669..7b1ac1d64 100644 --- a/libs/hwtesting/grpc.go +++ b/libs/hwtesting/grpc.go @@ -39,23 +39,29 @@ const ( FakeTokenOrganization = "3b25c6f5-4705-4074-9fc6-a50c28eba406" //nolint:gosec ) -func GetFakeTokenCredentials(subOverride string) InsecureBearerToken { +func GetFakeTokenCredentials(subOverride, orgOverride string) InsecureBearerToken { + sub := FakeTokenUser + if subOverride != "" { + sub = subOverride + } + + org := FakeTokenOrganization + if orgOverride != "" { + org = orgOverride + } + // README's fake token m := map[string]interface{}{ - "sub": FakeTokenUser, + "sub": sub, "email": "testine.test@helpwave.de", "name": "Testine Test", "preferred_username": "testine.test", "organization": map[string]interface{}{ - "id": FakeTokenOrganization, + "id": org, "name": "helpwave test", }, } - if subOverride != "" { - m["sub"] = subOverride - } - bytes, err := json.Marshal(m) if err != nil { panic(fmt.Errorf("GetFakeTokenCredentials failed: %w", err)) @@ -65,10 +71,10 @@ func GetFakeTokenCredentials(subOverride string) InsecureBearerToken { return InsecureBearerToken(dist) } -func GetGrpcConn(subOverride string) *grpc.ClientConn { +func GetGrpcConn(subOverride, orgOverride string) *grpc.ClientConn { conn, err := grpc.NewClient( common.ResolveAddrFromEnv(), - grpc.WithPerRPCCredentials(GetFakeTokenCredentials(subOverride)), + grpc.WithPerRPCCredentials(GetFakeTokenCredentials(subOverride, orgOverride)), grpc.WithTransportCredentials(insecure.NewCredentials()), ) if err != nil { diff --git a/libs/hwutil/arrays_test.go b/libs/hwutil/arrays_test.go index 95174d92b..9a1859edd 100644 --- a/libs/hwutil/arrays_test.go +++ b/libs/hwutil/arrays_test.go @@ -7,6 +7,8 @@ import ( ) func TestWithout(t *testing.T) { + t.Parallel() + tests := []struct { name string original []int @@ -59,6 +61,8 @@ func TestWithout(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + result := Without(tt.original, tt.itemsToRemove) require.Equal(t, tt.expectedResult, result) }) @@ -66,6 +70,8 @@ func TestWithout(t *testing.T) { } func TestSameItems(t *testing.T) { + t.Parallel() + tests := []struct { name string a, b []int @@ -117,6 +123,8 @@ func TestSameItems(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + result := SameItems(tt.a, tt.b) require.Equal(t, tt.expectedResult, result) }) diff --git a/libs/hwutil/contains_test.go b/libs/hwutil/contains_test.go index 90d79d81a..c1a68867a 100644 --- a/libs/hwutil/contains_test.go +++ b/libs/hwutil/contains_test.go @@ -16,6 +16,8 @@ type testCase[T comparable] struct { func runTestContains[T comparable](t *testing.T, testCase testCase[T]) { t.Helper() t.Run(fmt.Sprintf("contains %v the value %v", testCase.haystack, testCase.needle), func(t *testing.T) { + t.Parallel() + got := hwutil.Contains(testCase.haystack, testCase.needle) if testCase.expected != got { t.Errorf("expected %t, got %t", testCase.expected, got) @@ -24,6 +26,8 @@ func runTestContains[T comparable](t *testing.T, testCase testCase[T]) { } func TestContains(t *testing.T) { + t.Parallel() + runTestContains(t, testCase[string]{ haystack: []string{"ABC", "DEF", "GHI"}, needle: "DEF", diff --git a/libs/hwutil/parse_test.go b/libs/hwutil/parse_test.go index 4d60be741..98046d565 100644 --- a/libs/hwutil/parse_test.go +++ b/libs/hwutil/parse_test.go @@ -13,6 +13,8 @@ import ( func runPtrToTest[T comparable](t *testing.T, v T) { t.Helper() t.Run(fmt.Sprintf("test value %v as %T", v, v), func(t *testing.T) { + t.Parallel() + vPtr := hwutil.PtrTo(v) if v != *vPtr { t.Errorf("expected %v, got %v", v, *vPtr) @@ -21,6 +23,8 @@ func runPtrToTest[T comparable](t *testing.T, v T) { } func TestPtrTo(t *testing.T) { + t.Parallel() + runPtrToTest(t, true) runPtrToTest(t, "Hello world") runPtrToTest(t, 123) @@ -34,7 +38,11 @@ func TestPtrTo(t *testing.T) { } func TestStringsToUUIDs(t *testing.T) { + t.Parallel() + t.Run("valid uuids", func(t *testing.T) { + t.Parallel() + uuidStrings := []string{ "48441b57-a92a-4022-bfd9-9ded5acdb693", "370472cf-0e4f-449f-a6a4-817d7e025552", @@ -62,6 +70,8 @@ func TestStringsToUUIDs(t *testing.T) { }) t.Run("invalid uuids", func(t *testing.T) { + t.Parallel() + uuidStrings := []string{ "48441b57-a92a-4022-bfd9-9ded5acdb693", "asdasdasdsadsadadadsa", @@ -75,6 +85,8 @@ func TestStringsToUUIDs(t *testing.T) { }) t.Run("empty", func(t *testing.T) { + t.Parallel() + uuidStrings := []string{} actual, err := hwutil.StringsToUUIDs(uuidStrings) if err != nil { @@ -87,7 +99,11 @@ func TestStringsToUUIDs(t *testing.T) { } func TestInterfacesToStrings(t *testing.T) { + t.Parallel() + t.Run("ok path", func(t *testing.T) { + t.Parallel() + expected := []string{ "48441b57-a92a-4022-bfd9-9ded5acdb693", "Unit Test", @@ -104,6 +120,8 @@ func TestInterfacesToStrings(t *testing.T) { }) t.Run("not ok path", func(t *testing.T) { + t.Parallel() + interfaces := []interface{}{ "48441b57-a92a-4022-bfd9-9ded5acdb693", 123, diff --git a/libs/telemetry/go.mod b/libs/telemetry/go.mod index bb4dec800..4b04eafef 100644 --- a/libs/telemetry/go.mod +++ b/libs/telemetry/go.mod @@ -8,6 +8,7 @@ require ( github.com/fatih/structs v1.1.0 github.com/prometheus/client_golang v1.20.5 github.com/rs/zerolog v1.33.0 + github.com/stretchr/testify v1.10.0 go.opentelemetry.io/otel v1.33.0 go.opentelemetry.io/otel/trace v1.33.0 hwutil v0.0.0 @@ -16,6 +17,7 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -28,6 +30,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect @@ -38,4 +41,5 @@ require ( golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/libs/telemetry/go.sum b/libs/telemetry/go.sum index f0e646632..613211061 100644 --- a/libs/telemetry/go.sum +++ b/libs/telemetry/go.sum @@ -29,6 +29,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= @@ -51,6 +55,8 @@ github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= @@ -77,5 +83,8 @@ golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/libs/telemetry/setup.go b/libs/telemetry/setup.go index 8ab791b46..8e1563826 100644 --- a/libs/telemetry/setup.go +++ b/libs/telemetry/setup.go @@ -8,16 +8,12 @@ import ( "os" "time" - "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/rs/zerolog" "github.com/rs/zerolog/log" ) -var prometheusRegistry *prometheus.Registry - func SetupLogging(mode, rawLevel, service, version string) { zerolog.TimeFieldFormat = zerolog.TimeFormatUnix @@ -49,12 +45,12 @@ func SetupLogging(mode, rawLevel, service, version string) { log.Info().Msg("Logging is set up") } -func startMetricsServer(ctx context.Context, addr string, shutdown func(error)) { +func startMetricsServer(ctx context.Context, reg *prometheus.Registry, addr string, shutdown func(error)) { server := &http.Server{ Addr: addr, Handler: promhttp.InstrumentMetricHandler( - PrometheusRegistry(), - promhttp.HandlerFor(PrometheusRegistry(), promhttp.HandlerOpts{}), + reg, + promhttp.HandlerFor(reg, promhttp.HandlerOpts{}), ), ReadHeaderTimeout: time.Second * 30, // prevent potential slowloris attack } @@ -86,10 +82,11 @@ func startMetricsServer(ctx context.Context, addr string, shutdown func(error)) } // SetupMetrics will start a new http server for prometheus to scrape from -func SetupMetrics(ctx context.Context, shutdown func(error)) { +func SetupMetrics(ctx context.Context, shutdown func(error)) context.Context { // create new prometheus registry, we do not use the global default one, // as it causes problems with tests - prometheusRegistry = prometheus.NewRegistry() + prometheusRegistry := prometheus.NewRegistry() + ctx = WithPrometheusRegistry(ctx, prometheusRegistry) l := log.Ctx(ctx) @@ -97,40 +94,30 @@ func SetupMetrics(ctx context.Context, shutdown func(error)) { if addr == "" { l.Warn().Msg("METRICS_ADDR not set, will not export metrics") - return + return ctx } l.Info().Str("addr", addr).Msg("starting metrics server") - go startMetricsServer(ctx, addr, shutdown) + go startMetricsServer(ctx, prometheusRegistry, addr, shutdown) + return ctx } -func PrometheusRegistry() *prometheus.Registry { - return prometheusRegistry -} +type promRegKey struct{} -// LazyCounter prevents access to PrometheusRegistry, before it is initialized -// by creating the counter only when it is needed -type LazyCounter struct { - opts prometheus.CounterOpts - counter *prometheus.Counter +func WithPrometheusRegistry(ctx context.Context, registry *prometheus.Registry) context.Context { + return context.WithValue(ctx, promRegKey{}, registry) } -func NewLazyCounter(opts prometheus.CounterOpts) LazyCounter { - return LazyCounter{ - opts: opts, - counter: nil, - } -} +var ErrPrometheusRegistryMissing = errors.New("PrometheusRegistry called, but no (valid) registry in context") -func (lc *LazyCounter) Counter() prometheus.Counter { - if lc.counter != nil { - return *lc.counter - } - lc.counter = hwutil.PtrTo(promauto.With(prometheusRegistry).NewCounter(lc.opts)) - return *lc.counter -} +func PrometheusRegistry(ctx context.Context) *prometheus.Registry { + value := ctx.Value(promRegKey{}) + asReg, ok := value.(*prometheus.Registry) -func (lc *LazyCounter) Ensure() { - lc.Counter() + // we allow nil (which will not be ok), else panic + if value != nil && !ok { + panic(ErrPrometheusRegistryMissing) + } + return asReg } diff --git a/libs/telemetry/setup_test.go b/libs/telemetry/setup_test.go new file mode 100644 index 000000000..c5a96fe84 --- /dev/null +++ b/libs/telemetry/setup_test.go @@ -0,0 +1,50 @@ +package telemetry + +import ( + "context" + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" +) + +func TestPrometheusRegistry(t *testing.T) { + t.Parallel() + + defaultContext := context.Background() + + testCases := []struct { + name string + useDefault bool + registry *prometheus.Registry + }{ + { + name: "not calling WithPrometheusRegistry", + useDefault: true, + registry: nil, + }, + { + name: "setting nil registry", + useDefault: false, + registry: nil, + }, + { + name: "setting valid registry", + useDefault: false, + registry: prometheus.NewRegistry(), + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + + ctx := defaultContext + if !testCase.useDefault { + ctx = WithPrometheusRegistry(ctx, testCase.registry) + } + + require.Equal(t, testCase.registry, PrometheusRegistry(ctx)) + }) + } +} diff --git a/services/property-svc/cmd/service/main.go b/services/property-svc/cmd/service/main.go index 1a10bb52f..a26bdf538 100644 --- a/services/property-svc/cmd/service/main.go +++ b/services/property-svc/cmd/service/main.go @@ -35,7 +35,7 @@ func Main(version string, ready func()) { flag.Parse() log.Debug().Bool("replayMode", *replayMode).Msg("flags") - closeDBPool := hwdb.SetupDatabaseFromEnv(ctx) + ctx, closeDBPool := hwdb.SetupDatabaseFromEnv(ctx) defer closeDBPool() authz := hwspicedb.NewSpiceDBAuthZ() @@ -56,9 +56,9 @@ func Main(version string, ready func()) { common.Shutdown, propertySpiceDBProjection.NewProjection(eventStore, ServiceName, authz), propertySetSpiceDBProjection.NewProjection(eventStore, ServiceName, authz), - propertyPostgresProjection.NewProjection(eventStore, ServiceName, hwdb.GetDB()), - propertyValuePostgresProjection.NewProjection(eventStore, ServiceName, hwdb.GetDB()), - propertyRulesPostgresProjection.NewProjection(eventStore, ServiceName), + propertyPostgresProjection.NewProjection(eventStore, ServiceName, hwdb.MustGetDB(ctx)), + propertyValuePostgresProjection.NewProjection(eventStore, ServiceName, hwdb.MustGetDB(ctx)), + propertyRulesPostgresProjection.NewProjection(ctx, eventStore, ServiceName), ) propertyHandlers := ph.NewPropertyHandlers(aggregateStore, authz) diff --git a/services/property-svc/cmd/service/replay.go b/services/property-svc/cmd/service/replay.go index fac140a0d..864095209 100644 --- a/services/property-svc/cmd/service/replay.go +++ b/services/property-svc/cmd/service/replay.go @@ -24,7 +24,7 @@ func replay(ctx context.Context, eventStore *esdb.Client) error { log.Info().Msg("starting in replay mode") - db := hwdb.GetDB() + db := hwdb.MustGetDB(ctx) tx, rollback, err := hwdb.BeginTx(db, ctx) if err != nil { return fmt.Errorf("cannot begin transaction: %w", err) diff --git a/services/property-svc/internal/property-value/commands/v1/attach_property_value.go b/services/property-svc/internal/property-value/commands/v1/attach_property_value.go index 28323a927..6606ffcae 100644 --- a/services/property-svc/internal/property-value/commands/v1/attach_property_value.go +++ b/services/property-svc/internal/property-value/commands/v1/attach_property_value.go @@ -42,7 +42,7 @@ func NewAttachPropertyValueCommandHandler( return 0, err } - propertyValueRepo := propertyvaluerepo.New(hwdb.GetDB()) + propertyValueRepo := propertyvaluerepo.New(hwdb.MustGetDB(ctx)) var a *aggregate.PropertyValueAggregate query := hwdb.Optional(propertyValueRepo.GetPropertyValueBySubjectIDAndPropertyID) diff --git a/services/property-svc/internal/property-value/queries/v1/get_property_values_by_subject_id.go b/services/property-svc/internal/property-value/queries/v1/get_property_values_by_subject_id.go index 5df2824e9..d766b0d68 100644 --- a/services/property-svc/internal/property-value/queries/v1/get_property_values_by_subject_id.go +++ b/services/property-svc/internal/property-value/queries/v1/get_property_values_by_subject_id.go @@ -33,7 +33,7 @@ func NewGetRelevantPropertyValuesQueryHandler( ) GetRelevantPropertyValuesQueryHandler { return func(ctx context.Context, matcher viewModels.PropertyMatchers) ([]models.PropertyAndValue, error) { viewHandlers := vh.NewPropertyViewHandlers(as, authz) - propertyValueRepo := propertyvaluerepo.New(hwdb.GetDB()) + propertyValueRepo := propertyvaluerepo.New(hwdb.MustGetDB(ctx)) subjectID, err := matcher.GetSubjectID() if err != nil { diff --git a/services/property-svc/internal/property-view/api/grpc_test.go b/services/property-svc/internal/property-view/api/grpc_test.go index e8d3a602d..716b7fed8 100644 --- a/services/property-svc/internal/property-view/api/grpc_test.go +++ b/services/property-svc/internal/property-view/api/grpc_test.go @@ -29,7 +29,13 @@ import ( "google.golang.org/grpc" ) -func server() (context.Context, pb.PropertyViewsServiceClient, *hwes_test.AggregateStore, func()) { +func setup() ( + context.Context, + pb.PropertyViewsServiceClient, + *hwes_test.AggregateStore, + pgxmock.PgxPoolIface, + func(), +) { // Build gRPC service aggregateStore := hwes_test.NewAggregateStore() propertyViewHandlers := handlers.NewPropertyViewHandlers(aggregateStore, test.NewTrueAuthZ()) @@ -37,41 +43,33 @@ func server() (context.Context, pb.PropertyViewsServiceClient, *hwes_test.Aggreg ctx := common.Setup("property-svc", "test", common.WithFakeAuthOnly()) - // Start Server - grpcServer := grpc.NewServer(common.DefaultServerOptions()...) - pb.RegisterPropertyViewsServiceServer(grpcServer, grpcService) - conn, closer := common_test.StartGRPCServer(ctx, grpcServer) - - client := pb.NewPropertyViewsServiceClient(conn) - - return ctx, client, aggregateStore, closer -} - -func setup() ( - ctx context.Context, - client pb.PropertyViewsServiceClient, - as *hwes_test.AggregateStore, - dbMock pgxmock.PgxPoolIface, - teardown func(), -) { - ctx, client, as, closer := server() - ctx = common_test.AuthenticatedUserContext(ctx, uuid.NewString()) - + // database mock dbMock, err := pgxmock.NewPool() if err != nil { panic(err) } - hwdb.TestingSetDB(dbMock) + ctx = hwdb.WithDB(ctx, dbMock) - teardown = func() { + // auth + ctx = common_test.AuthenticatedUserContext(ctx, uuid.NewString()) + + // Start Server + grpcServer := grpc.NewServer(common.DefaultServerOptions(ctx)...) + pb.RegisterPropertyViewsServiceServer(grpcServer, grpcService) + conn, closer := common_test.StartGRPCServer(ctx, grpcServer) + client := pb.NewPropertyViewsServiceClient(conn) + + teardown := func() { closer() dbMock.Close() } - return ctx, client, as, dbMock, teardown + return ctx, client, aggregateStore, dbMock, teardown } func TestPropertyViewGrpcService_UpdatePropertyViewRule_Validation(t *testing.T) { + t.Parallel() + ctx, client, _, dbMock, teardown := setup() defer teardown() @@ -217,6 +215,8 @@ func TestPropertyViewGrpcService_UpdatePropertyViewRule_Validation(t *testing.T) } func TestPropertyViewGrpcService_UpdatePropertyViewRule_AllEmptyNoEffect(t *testing.T) { + t.Parallel() + ctx, client, as, _, teardown := setup() defer teardown() @@ -240,6 +240,8 @@ func TestPropertyViewGrpcService_UpdatePropertyViewRule_AllEmptyNoEffect(t *test } func TestPropertyViewGrpcService_UpdatePropertyViewRule_TaskPropertyMatcher_GreenPath_Created(t *testing.T) { + t.Parallel() + ctx, client, as, dbMock, teardown := setup() defer teardown() @@ -308,6 +310,8 @@ func TestPropertyViewGrpcService_UpdatePropertyViewRule_TaskPropertyMatcher_Gree } func TestPropertyViewGrpcService_UpdatePropertyViewRule_PatientPropertyMatcher_GreenPath_Created(t *testing.T) { + t.Parallel() + ctx, client, as, dbMock, teardown := setup() defer teardown() @@ -376,6 +380,8 @@ func TestPropertyViewGrpcService_UpdatePropertyViewRule_PatientPropertyMatcher_G } func TestPropertyViewGrpcService_UpdatePropertyViewRule_TaskPropertyMatcher_GreenPath_Updated(t *testing.T) { + t.Parallel() + ctx, client, as, dbMock, teardown := setup() defer teardown() @@ -473,6 +479,8 @@ func TestPropertyViewGrpcService_UpdatePropertyViewRule_TaskPropertyMatcher_Gree } func TestPropertyViewGrpcService_UpdatePropertyViewRule_PatientPropertyMatcher_GreenPath_Updated(t *testing.T) { + t.Parallel() + ctx, client, as, dbMock, teardown := setup() defer teardown() diff --git a/services/property-svc/internal/property-view/models/patient_property_matcher.go b/services/property-svc/internal/property-view/models/patient_property_matcher.go index 74c93d088..dfdeff6de 100644 --- a/services/property-svc/internal/property-view/models/patient_property_matcher.go +++ b/services/property-svc/internal/property-view/models/patient_property_matcher.go @@ -23,7 +23,7 @@ type PatientPropertyMatchers struct { } func (m PatientPropertyMatchers) FindExactRuleID(ctx context.Context) (*uuid.UUID, error) { - patientViews := patientviewsrepo.New(hwdb.GetDB()) + patientViews := patientviewsrepo.New(hwdb.MustGetDB(ctx)) return hwdb.Optional(patientViews.GetPatientRuleIdUsingExactMatchers)(ctx, patientviewsrepo.GetPatientRuleIdUsingExactMatchersParams{ WardID: m.WardID, @@ -52,7 +52,7 @@ func (m PatientPropertyMatchers) GetType() string { } func (m PatientPropertyMatchers) QueryProperties(ctx context.Context) ([]PropertiesQueryRow, error) { - patientViews := patientviewsrepo.New(hwdb.GetDB()) + patientViews := patientviewsrepo.New(hwdb.MustGetDB(ctx)) rows, err := patientViews.GetPatientPropertiesUsingMatchers( ctx, @@ -96,7 +96,7 @@ func (m PatientPropertyMatchers) ToMap() map[string]interface{} { } func (m PatientPropertyMatchers) IsPropertyAlwaysIncluded(ctx context.Context, propertyID uuid.UUID) (bool, error) { - repo := patientviewsrepo.New(hwdb.GetDB()) + repo := patientviewsrepo.New(hwdb.MustGetDB(ctx)) query := hwdb.Optional(repo.IsPatientPropertyAlwaysIncluded) alwaysInclude, err := query(ctx, patientviewsrepo.IsPatientPropertyAlwaysIncludedParams{ PropertyID: propertyID, diff --git a/services/property-svc/internal/property-view/models/task_property_matchers.go b/services/property-svc/internal/property-view/models/task_property_matchers.go index ed2e037ed..14b8b137a 100644 --- a/services/property-svc/internal/property-view/models/task_property_matchers.go +++ b/services/property-svc/internal/property-view/models/task_property_matchers.go @@ -24,7 +24,7 @@ type TaskPropertyMatchers struct { } func (m TaskPropertyMatchers) FindExactRuleID(ctx context.Context) (*uuid.UUID, error) { - taskViews := taskviewsrepo.New(hwdb.GetDB()) + taskViews := taskviewsrepo.New(hwdb.MustGetDB(ctx)) return hwdb.Optional(taskViews.GetTaskRuleIdUsingExactMatchers)(ctx, taskviewsrepo.GetTaskRuleIdUsingExactMatchersParams{ WardID: m.WardID, @@ -53,7 +53,7 @@ func (m TaskPropertyMatchers) GetType() string { } func (m TaskPropertyMatchers) QueryProperties(ctx context.Context) ([]PropertiesQueryRow, error) { - taskViews := taskviewsrepo.New(hwdb.GetDB()) + taskViews := taskviewsrepo.New(hwdb.MustGetDB(ctx)) rows, err := taskViews.GetTaskPropertiesUsingMatchers(ctx, taskviewsrepo.GetTaskPropertiesUsingMatchersParams{ WardID: m.WardID, @@ -95,7 +95,7 @@ func (m TaskPropertyMatchers) ToMap() map[string]interface{} { } func (m TaskPropertyMatchers) IsPropertyAlwaysIncluded(ctx context.Context, propertyID uuid.UUID) (bool, error) { - repo := taskviewsrepo.New(hwdb.GetDB()) + repo := taskviewsrepo.New(hwdb.MustGetDB(ctx)) query := hwdb.Optional(repo.IsTaskPropertyAlwaysIncluded) alwaysInclude, err := query(ctx, taskviewsrepo.IsTaskPropertyAlwaysIncludedParams{ PropertyID: propertyID, diff --git a/services/property-svc/internal/property-view/projections/postgres-projection/property_rules_postgres.go b/services/property-svc/internal/property-view/projections/postgres-projection/property_rules_postgres.go index c1238aa29..48d6ff1ce 100644 --- a/services/property-svc/internal/property-view/projections/postgres-projection/property_rules_postgres.go +++ b/services/property-svc/internal/property-view/projections/postgres-projection/property_rules_postgres.go @@ -31,7 +31,7 @@ type Projection struct { viewsRepo *viewsrepo.Queries } -func NewProjection(es custom.EventStoreClient, serviceName string) *Projection { +func NewProjection(ctx context.Context, es custom.EventStoreClient, serviceName string) *Projection { subscriptionGroupName := serviceName + "-property-rules-postgres-projection" p := &Projection{ CustomProjection: custom.NewCustomProjection( @@ -39,9 +39,9 @@ func NewProjection(es custom.EventStoreClient, serviceName string) *Projection { subscriptionGroupName, &[]string{aggregate.PropertyViewRuleAggregateType + "-"}, ), - db: hwdb.GetDB(), - taskViewsRepo: taskviewsrepo.New(hwdb.GetDB()), - viewsRepo: viewsrepo.New(hwdb.GetDB()), + db: hwdb.MustGetDB(ctx), + taskViewsRepo: taskviewsrepo.New(hwdb.MustGetDB(ctx)), + viewsRepo: viewsrepo.New(hwdb.MustGetDB(ctx)), } p.initEventListeners() return p diff --git a/services/property-svc/internal/property-view/projections/postgres-projection/property_rules_postgres_test.go b/services/property-svc/internal/property-view/projections/postgres-projection/property_rules_postgres_test.go index 88b66a85a..53d816d12 100644 --- a/services/property-svc/internal/property-view/projections/postgres-projection/property_rules_postgres_test.go +++ b/services/property-svc/internal/property-view/projections/postgres-projection/property_rules_postgres_test.go @@ -42,10 +42,10 @@ func setup() (ctx context.Context, projection *Projection, dbMock pgxmock.PgxPoo if err != nil { panic(err) } - hwdb.TestingSetDB(dbMock) + ctx = hwdb.WithDB(context.Background(), dbMock) teardown = dbMock.Close - projection = NewProjection(esClientStub{}, "testing") + projection = NewProjection(ctx, esClientStub{}, "testing") ctx = context.Background() @@ -53,6 +53,8 @@ func setup() (ctx context.Context, projection *Projection, dbMock pgxmock.PgxPoo } func TestPropertyViewPropertyRulesProjection_Create_TaskPropertyMatcher_GreenPath(t *testing.T) { + t.Parallel() + ctx, projection, dbMock, teardown := setup() defer teardown() @@ -105,6 +107,8 @@ func TestPropertyViewPropertyRulesProjection_Create_TaskPropertyMatcher_GreenPat } func TestPropertyViewPropertyRulesProjection_Update_GreenPath(t *testing.T) { + t.Parallel() + ctx, projection, dbMock, teardown := setup() defer teardown() @@ -171,6 +175,8 @@ func TestPropertyViewPropertyRulesProjection_Update_GreenPath(t *testing.T) { } func TestPropertyViewPropertyRulesProjection_Create_PatientPropertyMatcher_GreenPath(t *testing.T) { + t.Parallel() + ctx, projection, dbMock, teardown := setup() defer teardown() @@ -223,6 +229,8 @@ func TestPropertyViewPropertyRulesProjection_Create_PatientPropertyMatcher_Green } func TestPropertyViewPropertyRulesProjection_Create_InvalidPropertyMatcher(t *testing.T) { + t.Parallel() + ctx, projection, _, teardown := setup() defer teardown() diff --git a/services/property-svc/internal/property/queries/v1/get_properties_by_subject_type.go b/services/property-svc/internal/property/queries/v1/get_properties_by_subject_type.go index b5925b527..ef62d2067 100644 --- a/services/property-svc/internal/property/queries/v1/get_properties_by_subject_type.go +++ b/services/property-svc/internal/property/queries/v1/get_properties_by_subject_type.go @@ -25,7 +25,7 @@ func NewGetPropertiesQueryHandler(authz hwauthz.AuthZ) GetPropertiesQueryHandler return func(ctx context.Context, subjectType *pb.SubjectType) ([]*models.PropertyWithConsistency, error) { user := commonperm.UserFromCtx(ctx) - propertyRepo := propertyrepo.New(hwdb.GetDB()) + propertyRepo := propertyrepo.New(hwdb.MustGetDB(ctx)) var subjectTypeID *int32 if subjectType != nil { diff --git a/services/property-svc/internal/property/queries/v1/get_property_by_id.go b/services/property-svc/internal/property/queries/v1/get_property_by_id.go index d1644fe29..a7f9d964c 100644 --- a/services/property-svc/internal/property/queries/v1/get_property_by_id.go +++ b/services/property-svc/internal/property/queries/v1/get_property_by_id.go @@ -31,7 +31,7 @@ func NewGetPropertyByIDQueryHandler(authz hwauthz.AuthZ) GetPropertyByIDQueryHan return nil, 0, err } - propertyRepo := propertyrepo.New(hwdb.GetDB()) + propertyRepo := propertyrepo.New(hwdb.MustGetDB(ctx)) rows, err := propertyRepo.GetPropertiesWithSelectDataAndOptionsBySubjectTypeOrID( ctx, diff --git a/services/property-svc/stories/GetPropertieBySubjectType_test.go b/services/property-svc/stories/GetPropertieBySubjectType_test.go index f6e0d4123..acd7e606a 100644 --- a/services/property-svc/stories/GetPropertieBySubjectType_test.go +++ b/services/property-svc/stories/GetPropertieBySubjectType_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" pb "gen/services/property_svc/v1" + "hwauthz/spicedb" "hwtesting" "hwutil" "regexp" @@ -18,10 +19,20 @@ import ( // - Create Properties // - Check GetPropertiesBySubjectType func TestGetProperties(t *testing.T) { - propertyClient := propertyServiceClient() - + t.Parallel() ctx := context.Background() + // new user and org for this test, to prevent interference with other tests + userID := uuid.New() + orgID := uuid.New() + + // give user appropriate permissions + authz := spicedb.NewSpiceDBAuthZ() + err := spicedb.AddUserToOrganization(ctx, authz, userID, orgID, false) + require.NoError(t, err) + + propertyClient := propertyServiceClient(userID.String(), orgID.String()) + // // Create new Properties // diff --git a/services/property-svc/stories/GetProperty_test.go b/services/property-svc/stories/GetProperty_test.go index a5bcf5906..ec5a280e3 100644 --- a/services/property-svc/stories/GetProperty_test.go +++ b/services/property-svc/stories/GetProperty_test.go @@ -24,12 +24,14 @@ import ( // - Add to always include list for ward and subjectid (matcher too precise) // After each step: GetProperty and check AlwaysIncludedForViewSource for wardid func TestTaskGetPropertyAlwaysIncluded(t *testing.T) { + t.Parallel() + ctx := context.Background() wardID := uuid.New() patientID := uuid.New() taskID := uuid.New() - // give new user appropriate permissions + // give appropriate permissions authz := spicedb.NewSpiceDBAuthZ() patient := commonperm.GenericObject{ID_: patientID.String(), Typ: "patient"} task := commonperm.GenericObject{ID_: taskID.String(), Typ: "task"} @@ -42,7 +44,7 @@ func TestTaskGetPropertyAlwaysIncluded(t *testing.T) { Commit(ctx) require.NoError(t, err) - propertyClient := propertyServiceClient() + propertyClient := propertyServiceClient("", "") propertyViewClient := propertyViewServiceClient() // @@ -189,7 +191,9 @@ func TestTaskGetPropertyAlwaysIncluded(t *testing.T) { // - Update name // - TODO: conflict detection func TestTaskGetPropertyConsistency(t *testing.T) { - propertyClient := propertyServiceClient() + t.Parallel() + + propertyClient := propertyServiceClient("", "") ctx := context.Background() diff --git a/services/property-svc/stories/PropertyValueCRUD_test.go b/services/property-svc/stories/PropertyValueCRUD_test.go index d3dd8a6e5..810500a11 100644 --- a/services/property-svc/stories/PropertyValueCRUD_test.go +++ b/services/property-svc/stories/PropertyValueCRUD_test.go @@ -17,7 +17,9 @@ import ( // - Attach a Value // - Update said value func TestCreateAttachUpdateTextProperty(t *testing.T) { - propertyClient := propertyServiceClient() + t.Parallel() + + propertyClient := propertyServiceClient("", "") ctx := context.Background() // @@ -165,7 +167,9 @@ func TestCreateAttachUpdateTextProperty(t *testing.T) { // - Attach a Value // - Update said value func TestCreateAttachUpdateSelectProperty(t *testing.T) { - propertyClient := propertyServiceClient() + t.Parallel() + + propertyClient := propertyServiceClient("", "") ctx := context.Background() // @@ -333,7 +337,9 @@ func TestCreateAttachUpdateSelectProperty(t *testing.T) { // - Attach a Value // - Update said value func TestCreateAttachUpdateMultiSelectProperty(t *testing.T) { - propertyClient := propertyServiceClient() + t.Parallel() + + propertyClient := propertyServiceClient("", "") ctx := context.Background() // @@ -521,7 +527,9 @@ func TestCreateAttachUpdateMultiSelectProperty(t *testing.T) { // - Upsert an Option // - Attach another Value func TestCreateAttachAddOptionAttachSelectProperty(t *testing.T) { - propertyClient := propertyServiceClient() + t.Parallel() + + propertyClient := propertyServiceClient("", "") ctx := context.Background() // @@ -679,7 +687,9 @@ func TestCreateAttachAddOptionAttachSelectProperty(t *testing.T) { // - Upsert an Option // - Attach another Value func TestCreateAttachAddOptionAttachMultiSelectProperty(t *testing.T) { - propertyClient := propertyServiceClient() + t.Parallel() + + propertyClient := propertyServiceClient("", "") ctx := context.Background() // diff --git a/services/property-svc/stories/setup_test.go b/services/property-svc/stories/setup_test.go index 91a0c372e..d11808b9d 100644 --- a/services/property-svc/stories/setup_test.go +++ b/services/property-svc/stories/setup_test.go @@ -65,16 +65,16 @@ func TestMain(m *testing.M) { os.Exit(exitCode) } -func propertyServiceClient() pb.PropertyServiceClient { - return pb.NewPropertyServiceClient(hwtesting.GetGrpcConn("")) +func propertyServiceClient(subOverride, orgOverride string) pb.PropertyServiceClient { + return pb.NewPropertyServiceClient(hwtesting.GetGrpcConn(subOverride, orgOverride)) } func propertyViewServiceClient() pb.PropertyViewsServiceClient { - return pb.NewPropertyViewsServiceClient(hwtesting.GetGrpcConn("")) + return pb.NewPropertyViewsServiceClient(hwtesting.GetGrpcConn("", "")) } func propertyValueServiceClient() pb.PropertyValueServiceClient { - return pb.NewPropertyValueServiceClient(hwtesting.GetGrpcConn("")) + return pb.NewPropertyValueServiceClient(hwtesting.GetGrpcConn("", "")) } func NamesOf(arr []*pb.SelectValueOption) []string { diff --git a/services/tasks-svc/cmd/service/main.go b/services/tasks-svc/cmd/service/main.go index c181c1827..e121cf725 100644 --- a/services/tasks-svc/cmd/service/main.go +++ b/services/tasks-svc/cmd/service/main.go @@ -2,7 +2,6 @@ package service import ( "common" - "context" pb "gen/services/tasks_svc/v1" hwspicedb "hwauthz/spicedb" "hwdb" @@ -33,7 +32,7 @@ const ServiceName = "tasks-svc" func Main(version string, ready func()) { ctx := common.Setup(ServiceName, version, common.WithAuth()) - closeDBPool := hwdb.SetupDatabaseFromEnv(context.Background()) + ctx, closeDBPool := hwdb.SetupDatabaseFromEnv(ctx) defer closeDBPool() tracking.SetupTracking(ctx, ServiceName, 10, 24*time.Hour, 20) @@ -49,8 +48,8 @@ func Main(version string, ready func()) { ctx, common.Shutdown, taskSpiceDBProjection.NewSpiceDBProjection(eventStore, authz, ServiceName), - taskPostgresProjection.NewProjection(eventStore, ServiceName), - patientPostgresProjection.NewProjection(eventStore, ServiceName), + taskPostgresProjection.NewProjection(ctx, eventStore, ServiceName), + patientPostgresProjection.NewProjection(ctx, eventStore, ServiceName), patientSpiceDBProjection.NewProjection(eventStore, authz, ServiceName), ) diff --git a/services/tasks-svc/go.mod b/services/tasks-svc/go.mod index a51e370d7..f4445cc77 100644 --- a/services/tasks-svc/go.mod +++ b/services/tasks-svc/go.mod @@ -26,6 +26,7 @@ require ( github.com/google/uuid v1.6.0 github.com/jackc/pgx/v5 v5.7.2 github.com/nicksnyder/go-i18n/v2 v2.4.1 + github.com/pashagolub/pgxmock/v4 v4.3.0 github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.10.0 golang.org/x/net v0.33.0 diff --git a/services/tasks-svc/go.sum b/services/tasks-svc/go.sum index f3a3d1518..a314a4c85 100644 --- a/services/tasks-svc/go.sum +++ b/services/tasks-svc/go.sum @@ -212,6 +212,8 @@ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2sz github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= +github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s= +github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A= 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= diff --git a/services/tasks-svc/internal/bed/bed.go b/services/tasks-svc/internal/bed/bed.go index 8b6217f82..c9087fe85 100644 --- a/services/tasks-svc/internal/bed/bed.go +++ b/services/tasks-svc/internal/bed/bed.go @@ -62,7 +62,7 @@ func NewServiceServer(authz hwauthz.AuthZ, es *esdb.Client) *ServiceServer { func (s ServiceServer) CreateBed(ctx context.Context, req *pb.CreateBedRequest) (*pb.CreateBedResponse, error) { log := zlog.Ctx(ctx) - bedRepo := bedrepo.New(hwdb.GetDB()) + bedRepo := bedrepo.New(hwdb.MustGetDB(ctx)) // parse inputs roomID, err := uuid.Parse(req.GetRoomId()) @@ -137,7 +137,7 @@ func (s ServiceServer) CreateBed(ctx context.Context, req *pb.CreateBedRequest) } func (s ServiceServer) GetBed(ctx context.Context, req *pb.GetBedRequest) (*pb.GetBedResponse, error) { - bedRepo := bedrepo.New(hwdb.GetDB()) + bedRepo := bedrepo.New(hwdb.MustGetDB(ctx)) // parse inputs id, err := uuid.Parse(req.GetId()) @@ -175,7 +175,7 @@ func (s ServiceServer) GetBedByPatient( ctx context.Context, req *pb.GetBedByPatientRequest, ) (*pb.GetBedByPatientResponse, error) { - bedRepo := bedrepo.New(hwdb.GetDB()) + bedRepo := bedrepo.New(hwdb.MustGetDB(ctx)) patientID, err := uuid.Parse(req.GetPatientId()) if err != nil { @@ -229,7 +229,7 @@ func (s ServiceServer) GetBeds(ctx context.Context, req *pb.GetBedsRequest) (*pb return nil, status.Error(codes.InvalidArgument, err.Error()) } - bedRepo := bedrepo.New(hwdb.GetDB()) + bedRepo := bedrepo.New(hwdb.MustGetDB(ctx)) beds, err := bedRepo.GetBeds(ctx, roomID) if err != nil { @@ -270,7 +270,7 @@ func (s ServiceServer) GetBedsByRoom( return nil, status.Error(codes.InvalidArgument, err.Error()) } - bedRepo := bedrepo.New(hwdb.GetDB()) + bedRepo := bedrepo.New(hwdb.MustGetDB(ctx)) beds, err := bedRepo.GetBeds(ctx, uuid.NullUUID{ @@ -312,7 +312,7 @@ func (s ServiceServer) GetBedsByRoom( } func (s ServiceServer) UpdateBed(ctx context.Context, req *pb.UpdateBedRequest) (*pb.UpdateBedResponse, error) { - bedRepo := bedrepo.New(hwdb.GetDB()) + bedRepo := bedrepo.New(hwdb.MustGetDB(ctx)) // parse inputs bedID, err := uuid.Parse(req.GetId()) @@ -363,7 +363,7 @@ func (s ServiceServer) UpdateBed(ctx context.Context, req *pb.UpdateBedRequest) func (s ServiceServer) DeleteBed(ctx context.Context, req *pb.DeleteBedRequest) (*pb.DeleteBedResponse, error) { log := zlog.Ctx(ctx) - bedRepo := bedrepo.New(hwdb.GetDB()) + bedRepo := bedrepo.New(hwdb.MustGetDB(ctx)) // parse inputs bedID, err := uuid.Parse(req.GetId()) diff --git a/services/tasks-svc/internal/patient/aggregate/aggregate_test.go b/services/tasks-svc/internal/patient/aggregate/aggregate_test.go index 728eabe3e..dfde3e4b3 100644 --- a/services/tasks-svc/internal/patient/aggregate/aggregate_test.go +++ b/services/tasks-svc/internal/patient/aggregate/aggregate_test.go @@ -26,6 +26,8 @@ func MustApplyEvent(t *testing.T, aggregate hwes.Aggregate, newEvent func() (hwe } func TestPatientAggregate_CreatePatient(t *testing.T) { + t.Parallel() + ctx := context.Background() ctx = auth.ContextWithUserID(ctx, uuid.New()) ctx = auth.ContextWithOrganizationID(ctx, uuid.New()) @@ -58,6 +60,8 @@ func TestPatientAggregate_CreatePatient(t *testing.T) { } func TestPatientAggregate_UpdateNotes(t *testing.T) { + t.Parallel() + ctx := context.Background() ctx = auth.ContextWithUserID(ctx, uuid.New()) ctx = auth.ContextWithOrganizationID(ctx, uuid.New()) @@ -94,6 +98,8 @@ func TestPatientAggregate_UpdateNotes(t *testing.T) { } func TestPatientAggregate_UpdateHumanReadableIdentifier(t *testing.T) { + t.Parallel() + ctx := context.Background() ctx = auth.ContextWithUserID(ctx, uuid.New()) ctx = auth.ContextWithOrganizationID(ctx, uuid.New()) @@ -135,6 +141,8 @@ func TestPatientAggregate_UpdateHumanReadableIdentifier(t *testing.T) { } func TestPatientAggregate_DischargeReadmitPatient(t *testing.T) { + t.Parallel() + ctx := context.Background() ctx = auth.ContextWithUserID(ctx, uuid.New()) ctx = auth.ContextWithOrganizationID(ctx, uuid.New()) @@ -176,6 +184,8 @@ func TestPatientAggregate_DischargeReadmitPatient(t *testing.T) { } func TestPatientAggregate_AssignUnassignBed(t *testing.T) { + t.Parallel() + ctx := context.Background() ctx = auth.ContextWithUserID(ctx, uuid.New()) ctx = auth.ContextWithOrganizationID(ctx, uuid.New()) @@ -221,6 +231,8 @@ func TestPatientAggregate_AssignUnassignBed(t *testing.T) { } func TestPatientAggregate_DeletePatient(t *testing.T) { + t.Parallel() + ctx := auth.ContextWithUserID(context.Background(), uuid.New()) patientID := uuid.New() diff --git a/services/tasks-svc/internal/patient/api/grpc.go b/services/tasks-svc/internal/patient/api/grpc.go index 8de04d8be..c18a8cc79 100644 --- a/services/tasks-svc/internal/patient/api/grpc.go +++ b/services/tasks-svc/internal/patient/api/grpc.go @@ -79,7 +79,7 @@ func (s *PatientGrpcService) GetPatient( return nil, err } - bedRepo := bedrepo.New(hwdb.GetDB()) + bedRepo := bedrepo.New(hwdb.MustGetDB(ctx)) // check permissions user := commonperm.UserFromCtx(ctx) @@ -360,7 +360,7 @@ func (s *PatientGrpcService) GetRecentPatients( _ *pb.GetRecentPatientsRequest, ) (*pb.GetRecentPatientsResponse, error) { log := zlog.Ctx(ctx) - bedRepo := bedrepo.New(hwdb.GetDB()) + bedRepo := bedrepo.New(hwdb.MustGetDB(ctx)) var recentPatientIdsStrs []string recentPatientIdsStrs, err := tracking.GetRecentPatientsForUser(ctx) diff --git a/services/tasks-svc/internal/patient/api/grpc_test.go b/services/tasks-svc/internal/patient/api/grpc_test.go index 021b1a1c8..436377e47 100644 --- a/services/tasks-svc/internal/patient/api/grpc_test.go +++ b/services/tasks-svc/internal/patient/api/grpc_test.go @@ -7,10 +7,13 @@ import ( "decayinglru" pb "gen/services/tasks_svc/v1" "hwauthz/test" + "hwdb" hwes_test "hwes/test" "testing" "time" + "github.com/pashagolub/pgxmock/v4" + "github.com/go-redis/redismock/v9" "github.com/google/uuid" "github.com/stretchr/testify/assert" @@ -33,14 +36,25 @@ func server() (context.Context, pb.PatientServiceClient, func()) { ctx := common.Setup("tasks-svc", "test", common.WithFakeAuthOnly()) + dbMock, err := pgxmock.NewPool() + if err != nil { + panic(err) + } + ctx = hwdb.WithDB(ctx, dbMock) + // Start Server - grpcServer := grpc.NewServer(common.DefaultServerOptions()...) + grpcServer := grpc.NewServer(common.DefaultServerOptions(ctx)...) pb.RegisterPatientServiceServer(grpcServer, patientGrpcService) conn, closer := common_test.StartGRPCServer(ctx, grpcServer) client := pb.NewPatientServiceClient(conn) - return ctx, client, closer + teardown := func() { + dbMock.Close() + closer() + } + + return ctx, client, teardown } func setup(t *testing.T) ( @@ -69,6 +83,8 @@ func setup(t *testing.T) ( } func TestPatientGrpcService_GetPatientValidation(t *testing.T) { + t.Parallel() + ctx, client, _, teardown := setup(t) defer teardown() @@ -86,6 +102,8 @@ func TestPatientGrpcService_GetPatientValidation(t *testing.T) { } func TestPatientGrpcService_CreatePatient(t *testing.T) { + t.Parallel() + // Setup ctx, client, _, teardown := setup(t) defer teardown() @@ -112,6 +130,8 @@ func TestPatientGrpcService_CreatePatient(t *testing.T) { } func TestPatientGrpcService_UpdatePatient(t *testing.T) { + t.Parallel() + ctx, client, _, teardown := setup(t) defer teardown() @@ -145,6 +165,8 @@ func TestPatientGrpcService_UpdatePatient(t *testing.T) { } func TestPatientGrpcService_AssignBed_Validation(t *testing.T) { + t.Parallel() + ctx, client, _, teardown := setup(t) defer teardown() @@ -162,6 +184,8 @@ func TestPatientGrpcService_AssignBed_Validation(t *testing.T) { } func TestPatientGrpcService_UnassignBed_Validation(t *testing.T) { + t.Parallel() + ctx, client, _, teardown := setup(t) defer teardown() @@ -179,6 +203,8 @@ func TestPatientGrpcService_UnassignBed_Validation(t *testing.T) { } func TestPatientGrpcService_DischargePatient_Validation(t *testing.T) { + t.Parallel() + ctx, client, _, teardown := setup(t) defer teardown() @@ -196,6 +222,8 @@ func TestPatientGrpcService_DischargePatient_Validation(t *testing.T) { } func TestPatientGrpcService_ReadmitPatient_Validation(t *testing.T) { + t.Parallel() + ctx, client, _, teardown := setup(t) defer teardown() @@ -213,6 +241,8 @@ func TestPatientGrpcService_ReadmitPatient_Validation(t *testing.T) { } func TestPatientGrpcService_DeletePatient(t *testing.T) { + t.Parallel() + ctx, client, _, teardown := setup(t) defer teardown() diff --git a/services/tasks-svc/internal/patient/projections/postgres-projection/patient_postgres_projection.go b/services/tasks-svc/internal/patient/projections/postgres-projection/patient_postgres_projection.go index d0f710498..f02d074af 100644 --- a/services/tasks-svc/internal/patient/projections/postgres-projection/patient_postgres_projection.go +++ b/services/tasks-svc/internal/patient/projections/postgres-projection/patient_postgres_projection.go @@ -22,7 +22,7 @@ type Projection struct { patientRepo *patientrepo.Queries } -func NewProjection(es *esdb.Client, serviceName string) *Projection { +func NewProjection(ctx context.Context, es *esdb.Client, serviceName string) *Projection { subscriptionGroupName := serviceName + "-patient-postgres-projection" p := &Projection{ CustomProjection: custom.NewCustomProjection( @@ -30,7 +30,7 @@ func NewProjection(es *esdb.Client, serviceName string) *Projection { subscriptionGroupName, &[]string{aggregate.PatientAggregateType + "-"}, ), - patientRepo: patientrepo.New(hwdb.GetDB()), + patientRepo: patientrepo.New(hwdb.MustGetDB(ctx)), } p.initEventListeners() return p diff --git a/services/tasks-svc/internal/patient/queries/v1/get_all_patients_with_details.go b/services/tasks-svc/internal/patient/queries/v1/get_all_patients_with_details.go index 009225493..1681eb168 100644 --- a/services/tasks-svc/internal/patient/queries/v1/get_all_patients_with_details.go +++ b/services/tasks-svc/internal/patient/queries/v1/get_all_patients_with_details.go @@ -23,7 +23,7 @@ type GetAllPatientsWithDetailsQueryHandler func(ctx context.Context) ([]*models. func NewGetAllPatientsWithDetailsQueryHandler(authz hwauthz.AuthZ) GetAllPatientsWithDetailsQueryHandler { return func(ctx context.Context) ([]*models.PatientDetails, error) { - patientRepo := patientrepo.New(hwdb.GetDB()) + patientRepo := patientrepo.New(hwdb.MustGetDB(ctx)) // gather inputs organizationID := auth.MustGetOrganizationID(ctx) diff --git a/services/tasks-svc/internal/patient/queries/v1/get_patient_assignment_by_ward.go b/services/tasks-svc/internal/patient/queries/v1/get_patient_assignment_by_ward.go index 8f98c8cbb..479f50aad 100644 --- a/services/tasks-svc/internal/patient/queries/v1/get_patient_assignment_by_ward.go +++ b/services/tasks-svc/internal/patient/queries/v1/get_patient_assignment_by_ward.go @@ -23,7 +23,7 @@ type GetPatientAssignmentByWardQueryHandler func( func NewGetPatientAssignmentByWardQueryHandler(authz hwauthz.AuthZ) GetPatientAssignmentByWardQueryHandler { return func(ctx context.Context, wardID uuid.UUID) ([]*models.RoomWithBedsWithPatient, error) { - roomRepo := roomrepo.New(hwdb.GetDB()) + roomRepo := roomrepo.New(hwdb.MustGetDB(ctx)) // check permissions // diff --git a/services/tasks-svc/internal/patient/queries/v1/get_patient_by_bed.go b/services/tasks-svc/internal/patient/queries/v1/get_patient_by_bed.go index e3156148d..e2439a52d 100644 --- a/services/tasks-svc/internal/patient/queries/v1/get_patient_by_bed.go +++ b/services/tasks-svc/internal/patient/queries/v1/get_patient_by_bed.go @@ -20,7 +20,7 @@ type GetPatientByBedQueryHandler func(ctx context.Context, bedID uuid.UUID) (*mo func NewGetPatientByBedQueryHandler(authz hwauthz.AuthZ) GetPatientByBedQueryHandler { return func(ctx context.Context, bedID uuid.UUID) (*models.PatientWithConsistency, error) { - patientRepo := patientrepo.New(hwdb.GetDB()) + patientRepo := patientrepo.New(hwdb.MustGetDB(ctx)) // check bed permissions user := commonperm.UserFromCtx(ctx) diff --git a/services/tasks-svc/internal/patient/queries/v1/get_patient_with_details_by_id.go b/services/tasks-svc/internal/patient/queries/v1/get_patient_with_details_by_id.go index b3b1cc45f..5b7c101ab 100644 --- a/services/tasks-svc/internal/patient/queries/v1/get_patient_with_details_by_id.go +++ b/services/tasks-svc/internal/patient/queries/v1/get_patient_with_details_by_id.go @@ -23,7 +23,7 @@ func NewGetPatientWithDetailsByIDQueryHandler( as hwes.AggregateStore, authz hwauthz.AuthZ, ) GetPatientDetailsByIDQueryHandler { return func(ctx context.Context, patientID uuid.UUID) (*models.PatientDetails, error) { - patientRepo := patientrepo.New(hwdb.GetDB()) + patientRepo := patientrepo.New(hwdb.MustGetDB(ctx)) taskHandlers := th.NewTaskHandlers(as, authz) // check permissions diff --git a/services/tasks-svc/internal/patient/queries/v1/get_patients_by_ward.go b/services/tasks-svc/internal/patient/queries/v1/get_patients_by_ward.go index 3f82f2163..ba8fb311e 100644 --- a/services/tasks-svc/internal/patient/queries/v1/get_patients_by_ward.go +++ b/services/tasks-svc/internal/patient/queries/v1/get_patients_by_ward.go @@ -21,7 +21,7 @@ type GetPatientsByWardQueryHandler func(ctx context.Context, wardID uuid.UUID) ( func NewGetPatientsByWardQueryHandler(authz hwauthz.AuthZ) GetPatientsByWardQueryHandler { return func(ctx context.Context, wardID uuid.UUID) ([]*models.PatientWithConsistency, error) { - patientRepo := patientrepo.New(hwdb.GetDB()) + patientRepo := patientrepo.New(hwdb.MustGetDB(ctx)) // ensure get-access to ward user := commonperm.UserFromCtx(ctx) diff --git a/services/tasks-svc/internal/room/room.go b/services/tasks-svc/internal/room/room.go index ee95492b1..d30dff8b2 100644 --- a/services/tasks-svc/internal/room/room.go +++ b/services/tasks-svc/internal/room/room.go @@ -56,7 +56,7 @@ func NewServiceServer(authz hwauthz.AuthZ, es *esdb.Client) *ServiceServer { func (s ServiceServer) CreateRoom(ctx context.Context, req *pb.CreateRoomRequest) (*pb.CreateRoomResponse, error) { log := zlog.Ctx(ctx) - roomRepo := roomrepo.New(hwdb.GetDB()) + roomRepo := roomrepo.New(hwdb.MustGetDB(ctx)) // parse input wardID, err := uuid.Parse(req.GetWardId()) @@ -118,7 +118,7 @@ func (s ServiceServer) CreateRoom(ctx context.Context, req *pb.CreateRoomRequest } func (s ServiceServer) GetRoom(ctx context.Context, req *pb.GetRoomRequest) (*pb.GetRoomResponse, error) { - roomRepo := roomrepo.New(hwdb.GetDB()) + roomRepo := roomrepo.New(hwdb.MustGetDB(ctx)) // parse inputs id, err := uuid.Parse(req.GetId()) @@ -169,7 +169,7 @@ func (s ServiceServer) GetRoom(ctx context.Context, req *pb.GetRoomRequest) (*pb } func (s ServiceServer) UpdateRoom(ctx context.Context, req *pb.UpdateRoomRequest) (*pb.UpdateRoomResponse, error) { - roomRepo := roomrepo.New(hwdb.GetDB()) + roomRepo := roomrepo.New(hwdb.MustGetDB(ctx)) // parse inputs roomID, err := uuid.Parse(req.GetId()) @@ -212,7 +212,7 @@ func (s ServiceServer) UpdateRoom(ctx context.Context, req *pb.UpdateRoomRequest } func (s ServiceServer) GetRooms(ctx context.Context, req *pb.GetRoomsRequest) (*pb.GetRoomsResponse, error) { - roomRepo := roomrepo.New(hwdb.GetDB()) + roomRepo := roomrepo.New(hwdb.MustGetDB(ctx)) // parse inputs wardID, err := hwutil.ParseNullUUID(req.WardId) @@ -277,7 +277,7 @@ func (s ServiceServer) GetRooms(ctx context.Context, req *pb.GetRoomsRequest) (* } func (s ServiceServer) DeleteRoom(ctx context.Context, req *pb.DeleteRoomRequest) (*pb.DeleteRoomResponse, error) { - roomRepo := roomrepo.New(hwdb.GetDB()) + roomRepo := roomrepo.New(hwdb.MustGetDB(ctx)) // parse inputs roomID, err := uuid.Parse(req.GetId()) @@ -321,7 +321,7 @@ func (s ServiceServer) GetRoomOverviewsByWard( ctx context.Context, req *pb.GetRoomOverviewsByWardRequest, ) (*pb.GetRoomOverviewsByWardResponse, error) { - roomRepo := roomrepo.New(hwdb.GetDB()) + roomRepo := roomrepo.New(hwdb.MustGetDB(ctx)) wardID, err := uuid.Parse(req.GetId()) if err != nil { diff --git a/services/tasks-svc/internal/task-template/task_template.go b/services/tasks-svc/internal/task-template/task_template.go index b5acb6263..aecebaa60 100644 --- a/services/tasks-svc/internal/task-template/task_template.go +++ b/services/tasks-svc/internal/task-template/task_template.go @@ -59,7 +59,7 @@ func (s ServiceServer) CreateTaskTemplate( req *pb.CreateTaskTemplateRequest, ) (*pb.CreateTaskTemplateResponse, error) { log := zlog.Ctx(ctx) - db := hwdb.GetDB() + db := hwdb.MustGetDB(ctx) user := commonperm.UserFromCtx(ctx) @@ -163,7 +163,7 @@ func (s ServiceServer) DeleteTaskTemplate( req *pb.DeleteTaskTemplateRequest, ) (*pb.DeleteTaskTemplateResponse, error) { log := zlog.Ctx(ctx) - templateRepo := tasktemplaterepo.New(hwdb.GetDB()) + templateRepo := tasktemplaterepo.New(hwdb.MustGetDB(ctx)) id, err := uuid.Parse(req.GetId()) if err != nil { @@ -210,7 +210,7 @@ func (s ServiceServer) DeleteTaskTemplateSubTask( ) (*pb.DeleteTaskTemplateSubTaskResponse, error) { log := zlog.Ctx(ctx) - tx, rollback, err := hwdb.BeginTx(hwdb.GetDB(), ctx) + tx, rollback, err := hwdb.BeginTx(hwdb.MustGetDB(ctx), ctx) if err != nil { return nil, err } @@ -273,7 +273,7 @@ func (s ServiceServer) UpdateTaskTemplate( ctx context.Context, req *pb.UpdateTaskTemplateRequest, ) (*pb.UpdateTaskTemplateResponse, error) { - templateRepo := tasktemplaterepo.New(hwdb.GetDB()) + templateRepo := tasktemplaterepo.New(hwdb.MustGetDB(ctx)) id, err := uuid.Parse(req.GetId()) if err != nil { @@ -319,7 +319,7 @@ func (s ServiceServer) UpdateTaskTemplateSubTask( req *pb.UpdateTaskTemplateSubTaskRequest, ) (*pb.UpdateTaskTemplateSubTaskResponse, error) { // TX - tx, rollback, err := hwdb.BeginTx(hwdb.GetDB(), ctx) + tx, rollback, err := hwdb.BeginTx(hwdb.MustGetDB(ctx), ctx) if err != nil { return nil, err } @@ -383,7 +383,7 @@ func (s ServiceServer) CreateTaskTemplateSubTask( req *pb.CreateTaskTemplateSubTaskRequest, ) (*pb.CreateTaskTemplateSubTaskResponse, error) { log := zlog.Ctx(ctx) - templateRepo := tasktemplaterepo.New(hwdb.GetDB()) + templateRepo := tasktemplaterepo.New(hwdb.MustGetDB(ctx)) taskTemplateID, err := uuid.Parse(req.GetTaskTemplateId()) if err != nil { @@ -436,7 +436,7 @@ func (s ServiceServer) GetAllTaskTemplates( ctx context.Context, req *pb.GetAllTaskTemplatesRequest, ) (*pb.GetAllTaskTemplatesResponse, error) { - templateRepo := tasktemplaterepo.New(hwdb.GetDB()) + templateRepo := tasktemplaterepo.New(hwdb.MustGetDB(ctx)) user := commonperm.UserFromCtx(ctx) @@ -512,7 +512,7 @@ func (s ServiceServer) GetTaskTemplate( ctx context.Context, req *pb.GetTaskTemplateRequest, ) (*pb.GetTaskTemplateResponse, error) { - templateRepo := tasktemplaterepo.New(hwdb.GetDB()) + templateRepo := tasktemplaterepo.New(hwdb.MustGetDB(ctx)) taskTemplateID, err := uuid.Parse(req.Id) if err != nil { diff --git a/services/tasks-svc/internal/task/aggregate/aggregate_test.go b/services/tasks-svc/internal/task/aggregate/aggregate_test.go index f270e1fcd..a6fa0b2f6 100644 --- a/services/tasks-svc/internal/task/aggregate/aggregate_test.go +++ b/services/tasks-svc/internal/task/aggregate/aggregate_test.go @@ -27,6 +27,8 @@ func MustApplyEvent(t *testing.T, aggregate hwes.Aggregate, newEvent func() (hwe } func TestTaskAggregate_UpdateName(t *testing.T) { + t.Parallel() + ctx := context.Background() ctx = auth.ContextWithUserID(ctx, uuid.New()) ctx = auth.ContextWithOrganizationID(ctx, uuid.New()) @@ -64,6 +66,8 @@ func TestTaskAggregate_UpdateName(t *testing.T) { } func TestTaskAggregate_UpdateDescription(t *testing.T) { + t.Parallel() + ctx := context.Background() ctx = auth.ContextWithUserID(ctx, uuid.New()) ctx = auth.ContextWithOrganizationID(ctx, uuid.New()) @@ -103,6 +107,8 @@ func TestTaskAggregate_UpdateDescription(t *testing.T) { } func TestTaskAggregate_UpdateSubtaskName(t *testing.T) { + t.Parallel() + ctx := context.Background() ctx = auth.ContextWithUserID(ctx, uuid.New()) ctx = auth.ContextWithOrganizationID(ctx, uuid.New()) @@ -147,6 +153,8 @@ func TestTaskAggregate_UpdateSubtaskName(t *testing.T) { } func TestTaskAggregate_CompleteSubtask(t *testing.T) { + t.Parallel() + ctx := context.Background() ctx = auth.ContextWithUserID(ctx, uuid.New()) ctx = auth.ContextWithOrganizationID(ctx, uuid.New()) @@ -199,6 +207,8 @@ func TestTaskAggregate_CompleteSubtask(t *testing.T) { } func TestTaskAggregate_AssignTask(t *testing.T) { + t.Parallel() + ctx := context.Background() ctx = auth.ContextWithUserID(ctx, uuid.New()) ctx = auth.ContextWithOrganizationID(ctx, uuid.New()) @@ -234,6 +244,8 @@ func TestTaskAggregate_AssignTask(t *testing.T) { } func TestTaskAggregate_DeleteTask(t *testing.T) { + t.Parallel() + ctx := auth.ContextWithUserID(context.Background(), uuid.New()) taskID := uuid.New() diff --git a/services/tasks-svc/internal/task/api/grpc_test.go b/services/tasks-svc/internal/task/api/grpc_test.go index b078f8e22..ac16b377d 100644 --- a/services/tasks-svc/internal/task/api/grpc_test.go +++ b/services/tasks-svc/internal/task/api/grpc_test.go @@ -27,7 +27,7 @@ func server() (context.Context, pb.TaskServiceClient, func()) { ctx := common.Setup("tasks-svc", "test", common.WithFakeAuthOnly()) // Start Server - grpcServer := grpc.NewServer(common.DefaultServerOptions()...) + grpcServer := grpc.NewServer(common.DefaultServerOptions(ctx)...) pb.RegisterTaskServiceServer(grpcServer, taskGrpcService) conn, closer := common_test.StartGRPCServer(ctx, grpcServer) @@ -43,6 +43,8 @@ func setup() (ctx context.Context, client pb.TaskServiceClient, teardown func()) } func TestTaskGrpcService_CreateTask_Validation(t *testing.T) { + t.Parallel() + ctx, client, teardown := setup() defer teardown() @@ -120,6 +122,8 @@ func TestTaskGrpcService_CreateTask_Validation(t *testing.T) { } func TestTaskGrpcService_UpdateTask_Validation(t *testing.T) { + t.Parallel() + ctx, client, teardown := setup() defer teardown() @@ -143,6 +147,8 @@ func TestTaskGrpcService_UpdateTask_Validation(t *testing.T) { } func TestTaskGrpcService_AssignTask_Validation(t *testing.T) { + t.Parallel() + ctx, client, teardown := setup() defer teardown() @@ -169,6 +175,8 @@ func TestTaskGrpcService_AssignTask_Validation(t *testing.T) { } func TestTaskGrpcService_UnassignTask_Validation(t *testing.T) { + t.Parallel() + ctx, client, teardown := setup() defer teardown() @@ -195,6 +203,8 @@ func TestTaskGrpcService_UnassignTask_Validation(t *testing.T) { } func TestTaskGrpcService_CreateSubtask_Validation(t *testing.T) { + t.Parallel() + ctx, client, teardown := setup() defer teardown() @@ -243,6 +253,7 @@ func TestTaskGrpcService_CreateSubtask_Validation(t *testing.T) { } func TestTaskGrpcService_UpdateSubtask_Validation(t *testing.T) { + t.Parallel() ctx, client, teardown := setup() defer teardown() @@ -275,6 +286,8 @@ func TestTaskGrpcService_UpdateSubtask_Validation(t *testing.T) { } func TestTaskGrpcService_DeleteSubtask_Validation(t *testing.T) { + t.Parallel() + ctx, client, teardown := setup() defer teardown() @@ -301,6 +314,8 @@ func TestTaskGrpcService_DeleteSubtask_Validation(t *testing.T) { } func TestTaskGrpcService_RemoveTaskDueDate_Validation(t *testing.T) { + t.Parallel() + ctx, client, teardown := setup() defer teardown() @@ -324,6 +339,8 @@ func TestTaskGrpcService_RemoveTaskDueDate_Validation(t *testing.T) { } func TestTaskGrpcService_DeleteTask_Validation(t *testing.T) { + t.Parallel() + ctx, client, teardown := setup() defer teardown() diff --git a/services/tasks-svc/internal/task/projections/postgres-projection/task_postgres_projection.go b/services/tasks-svc/internal/task/projections/postgres-projection/task_postgres_projection.go index dc8a6f24e..310e95a9f 100644 --- a/services/tasks-svc/internal/task/projections/postgres-projection/task_postgres_projection.go +++ b/services/tasks-svc/internal/task/projections/postgres-projection/task_postgres_projection.go @@ -24,7 +24,7 @@ type Projection struct { taskRepo *taskrepo.Queries } -func NewProjection(es *esdb.Client, serviceName string) *Projection { +func NewProjection(ctx context.Context, es *esdb.Client, serviceName string) *Projection { subscriptionGroupName := serviceName + "-task-postgres-projection" p := &Projection{ CustomProjection: custom.NewCustomProjection( @@ -32,7 +32,7 @@ func NewProjection(es *esdb.Client, serviceName string) *Projection { subscriptionGroupName, &[]string{aggregate.TaskAggregateType + "-"}, ), - taskRepo: taskrepo.New(hwdb.GetDB()), + taskRepo: taskrepo.New(hwdb.MustGetDB(ctx)), } p.initEventListeners() return p diff --git a/services/tasks-svc/internal/task/queries/v1/get_task_with_patient_by_id.go b/services/tasks-svc/internal/task/queries/v1/get_task_with_patient_by_id.go index 2adac01a2..12fe30615 100644 --- a/services/tasks-svc/internal/task/queries/v1/get_task_with_patient_by_id.go +++ b/services/tasks-svc/internal/task/queries/v1/get_task_with_patient_by_id.go @@ -28,7 +28,7 @@ func NewGetTaskWithPatientByIDQueryHandler(authz hwauthz.AuthZ) GetTaskWithPatie return nil, err } - taskRepo := taskrepo.New(hwdb.GetDB()) + taskRepo := taskrepo.New(hwdb.MustGetDB(ctx)) rows, err := taskRepo.GetTaskWithPatientById(ctx, taskID) if err := hwdb.Error(ctx, err); err != nil { diff --git a/services/tasks-svc/internal/task/queries/v1/get_tasks_by_patient.go b/services/tasks-svc/internal/task/queries/v1/get_tasks_by_patient.go index dd6fcd711..852d570b2 100644 --- a/services/tasks-svc/internal/task/queries/v1/get_tasks_by_patient.go +++ b/services/tasks-svc/internal/task/queries/v1/get_tasks_by_patient.go @@ -32,7 +32,7 @@ func NewGetTasksByPatientIDQueryHandler(authz hwauthz.AuthZ) GetTasksByPatientID return nil, err } - taskRepo := taskrepo.New(hwdb.GetDB()) + taskRepo := taskrepo.New(hwdb.MustGetDB(ctx)) tasksWithSubtasks, err := taskRepo.GetTasksWithSubtasksByPatient(ctx, patientID) if err := hwdb.Error(ctx, err); err != nil { diff --git a/services/tasks-svc/internal/task/queries/v1/get_tasks_with_patients_by_asignee.go b/services/tasks-svc/internal/task/queries/v1/get_tasks_with_patients_by_asignee.go index 009b5f46a..1e5579a60 100644 --- a/services/tasks-svc/internal/task/queries/v1/get_tasks_with_patients_by_asignee.go +++ b/services/tasks-svc/internal/task/queries/v1/get_tasks_with_patients_by_asignee.go @@ -25,7 +25,7 @@ type GetTasksWithPatientsByAssigneeQueryHandler func( func NewGetTasksWithPatientsByAssigneeQueryHandler(authz hwauthz.AuthZ) GetTasksWithPatientsByAssigneeQueryHandler { return func(ctx context.Context, assigneeID uuid.UUID) ([]*models.TaskWithPatient, error) { - taskRepo := taskrepo.New(hwdb.GetDB()) + taskRepo := taskrepo.New(hwdb.MustGetDB(ctx)) user := commonperm.UserFromCtx(ctx) diff --git a/services/tasks-svc/internal/ward/ward.go b/services/tasks-svc/internal/ward/ward.go index 5810ec4a1..5e46164d6 100644 --- a/services/tasks-svc/internal/ward/ward.go +++ b/services/tasks-svc/internal/ward/ward.go @@ -57,7 +57,7 @@ func NewServiceServer(authz hwauthz.AuthZ, es *esdb.Client) *ServiceServer { func (s *ServiceServer) CreateWard(ctx context.Context, req *pb.CreateWardRequest) (*pb.CreateWardResponse, error) { log := zlog.Ctx(ctx) - wardRepo := wardrepo.New(hwdb.GetDB()) + wardRepo := wardrepo.New(hwdb.MustGetDB(ctx)) // check permissions user := commonperm.UserFromCtx(ctx) @@ -116,7 +116,7 @@ func (s *ServiceServer) CreateWard(ctx context.Context, req *pb.CreateWardReques } func (s *ServiceServer) GetWard(ctx context.Context, req *pb.GetWardRequest) (*pb.GetWardResponse, error) { - wardRepo := wardrepo.New(hwdb.GetDB()) + wardRepo := wardrepo.New(hwdb.MustGetDB(ctx)) // parse input id, err := uuid.Parse(req.GetId()) @@ -150,7 +150,7 @@ func (s *ServiceServer) GetWard(ctx context.Context, req *pb.GetWardRequest) (*p } func (s *ServiceServer) GetWards(ctx context.Context, req *pb.GetWardsRequest) (*pb.GetWardsResponse, error) { - wardRepo := wardrepo.New(hwdb.GetDB()) + wardRepo := wardrepo.New(hwdb.MustGetDB(ctx)) wards, err := wardRepo.GetWards(ctx) err = hwdb.Error(ctx, err) @@ -186,7 +186,7 @@ func (s *ServiceServer) GetRecentWards( ctx context.Context, _ *pb.GetRecentWardsRequest, ) (*pb.GetRecentWardsResponse, error) { - wardRepo := wardrepo.New(hwdb.GetDB()) + wardRepo := wardrepo.New(hwdb.MustGetDB(ctx)) log := zlog.Ctx(ctx) recentWardIDsStr, err := tracking.GetRecentWardsForUser(ctx) @@ -247,7 +247,7 @@ func (s *ServiceServer) GetRecentWards( } func (s *ServiceServer) UpdateWard(ctx context.Context, req *pb.UpdateWardRequest) (*pb.UpdateWardResponse, error) { - wardRepo := wardrepo.New(hwdb.GetDB()) + wardRepo := wardrepo.New(hwdb.MustGetDB(ctx)) // parse input id, err := uuid.Parse(req.GetId()) @@ -293,7 +293,7 @@ func (s *ServiceServer) UpdateWard(ctx context.Context, req *pb.UpdateWardReques } func (s *ServiceServer) DeleteWard(ctx context.Context, req *pb.DeleteWardRequest) (*pb.DeleteWardResponse, error) { - wardRepo := wardrepo.New(hwdb.GetDB()) + wardRepo := wardrepo.New(hwdb.MustGetDB(ctx)) // parse input wardID, err := uuid.Parse(req.GetId()) @@ -350,7 +350,7 @@ func (s *ServiceServer) GetWardOverviews( ctx context.Context, _ *pb.GetWardOverviewsRequest, ) (*pb.GetWardOverviewsResponse, error) { - wardRepo := wardrepo.New(hwdb.GetDB()) + wardRepo := wardrepo.New(hwdb.MustGetDB(ctx)) rows, err := wardRepo.GetWardsWithCounts(ctx, wardrepo.GetWardsWithCountsParams{ StatusTodo: int32(pb.TaskStatus_TASK_STATUS_TODO), @@ -395,7 +395,7 @@ func (s *ServiceServer) GetWardDetails( ctx context.Context, req *pb.GetWardDetailsRequest, ) (*pb.GetWardDetailsResponse, error) { - wardRepo := wardrepo.New(hwdb.GetDB()) + wardRepo := wardrepo.New(hwdb.MustGetDB(ctx)) wardID, err := uuid.Parse(req.GetId()) if err != nil { diff --git a/services/tasks-svc/stories/BedCRUD_test.go b/services/tasks-svc/stories/BedCRUD_test.go index c966660f7..8b4a2492f 100644 --- a/services/tasks-svc/stories/BedCRUD_test.go +++ b/services/tasks-svc/stories/BedCRUD_test.go @@ -16,6 +16,8 @@ import ( // - Create a new bed // - Update it func TestCreateUpdateGetBed(t *testing.T) { + t.Parallel() + ctx := context.Background() bedClient := bedServiceClient() @@ -78,6 +80,8 @@ func TestCreateUpdateGetBed(t *testing.T) { } func TestGetBedByPatient(t *testing.T) { + t.Parallel() + ctx := context.Background() // first, prepare room @@ -118,6 +122,8 @@ func TestGetBedByPatient(t *testing.T) { } func TestGetBeds(t *testing.T) { + t.Parallel() + bedClient := bedServiceClient() ctx := context.Background() diff --git a/services/tasks-svc/stories/PatientCRUD_test.go b/services/tasks-svc/stories/PatientCRUD_test.go index 0dc970b1f..326eb01c7 100644 --- a/services/tasks-svc/stories/PatientCRUD_test.go +++ b/services/tasks-svc/stories/PatientCRUD_test.go @@ -3,8 +3,6 @@ package stories import ( "context" pb "gen/services/tasks_svc/v1" - "hwauthz" - "hwauthz/commonperm" "hwauthz/spicedb" "hwtesting" "hwutil" @@ -19,6 +17,8 @@ import ( ) func TestCreateUpdateGetPatient(t *testing.T) { + t.Parallel() + ctx := context.Background() patientClient := patientServiceClient() @@ -172,6 +172,8 @@ func TestCreateUpdateGetPatient(t *testing.T) { } func TestGetPatientByBed(t *testing.T) { + t.Parallel() + ctx := context.Background() patientClient := patientServiceClient() @@ -190,6 +192,7 @@ func TestGetPatientByBed(t *testing.T) { } createRes, err := patientClient.CreatePatient(ctx, createReq) require.NoError(t, err, "could not create patient") + hwtesting.WaitForProjectionsToSettle() patientID := createRes.GetId() @@ -217,6 +220,8 @@ func TestGetPatientByBed(t *testing.T) { } func TestGetPatientsByWard(t *testing.T) { + t.Parallel() + ctx := context.Background() patientClient := patientServiceClient() @@ -299,6 +304,8 @@ func TestGetPatientsByWard(t *testing.T) { } func TestGetPatientAssignmentByWard(t *testing.T) { + t.Parallel() + ctx := context.Background() patientClient := patientServiceClient() @@ -367,6 +374,8 @@ func TestGetPatientAssignmentByWard(t *testing.T) { } func TestGetPatientList(t *testing.T) { + t.Parallel() + ctx := context.Background() patientClient := patientServiceClient() taskClient := taskServiceClient() @@ -535,6 +544,8 @@ func TestGetPatientList(t *testing.T) { } func TestGetPatientDetails(t *testing.T) { + t.Parallel() + ctx := context.Background() patientClient := patientServiceClient() @@ -641,18 +652,18 @@ func TestGetPatientDetails(t *testing.T) { } func TestGetRecentPatients(t *testing.T) { + t.Parallel() + ctx := context.Background() userID := uuid.New() // new user for this test, to prevent interference with other tests // give new user appropriate permissions authz := spicedb.NewSpiceDBAuthZ() - user := commonperm.User(userID) - org := commonperm.Organization(uuid.MustParse(hwtesting.FakeTokenOrganization)) - _, err := authz.Create(hwauthz.NewRelationship(user, "member", org)).Commit(ctx) + err := spicedb.AddUserToOrganization(ctx, authz, userID, uuid.MustParse(hwtesting.FakeTokenOrganization), false) require.NoError(t, err) - patientClient := pb.NewPatientServiceClient(hwtesting.GetGrpcConn(userID.String())) + patientClient := pb.NewPatientServiceClient(hwtesting.GetGrpcConn(userID.String(), "")) wardID, _ := prepareWard(t, ctx, "") roomID, roomConsistency := prepareRoom(t, ctx, wardID, "") diff --git a/services/tasks-svc/stories/RoomCRUD_test.go b/services/tasks-svc/stories/RoomCRUD_test.go index 04a20ca9e..e74c52384 100644 --- a/services/tasks-svc/stories/RoomCRUD_test.go +++ b/services/tasks-svc/stories/RoomCRUD_test.go @@ -17,6 +17,8 @@ import ( // - Create a new room // - Update it func TestCreateUpdateGetRoom(t *testing.T) { + t.Parallel() + ctx := context.Background() roomClient := roomServiceClient() @@ -75,6 +77,8 @@ func TestCreateUpdateGetRoom(t *testing.T) { } func TestGetRooms(t *testing.T) { + t.Parallel() + roomClient := roomServiceClient() ctx := context.Background() @@ -134,6 +138,8 @@ func TestGetRooms(t *testing.T) { } func TestGetRoomOverviewsByWard(t *testing.T) { + t.Parallel() + patientClient := patientServiceClient() taskClient := taskServiceClient() ctx := context.Background() diff --git a/services/tasks-svc/stories/TaskCRUD_test.go b/services/tasks-svc/stories/TaskCRUD_test.go index a5b647284..51384becd 100644 --- a/services/tasks-svc/stories/TaskCRUD_test.go +++ b/services/tasks-svc/stories/TaskCRUD_test.go @@ -3,8 +3,6 @@ package stories import ( "context" pb "gen/services/tasks_svc/v1" - "hwauthz" - "hwauthz/commonperm" "hwauthz/spicedb" "hwtesting" "hwutil" @@ -19,6 +17,8 @@ import ( ) func TestCreateUpdateGetTask(t *testing.T) { + t.Parallel() + ctx := context.Background() taskClient := taskServiceClient() @@ -249,6 +249,8 @@ func TestCreateUpdateGetTask(t *testing.T) { } func TestGetTasksByPatient(t *testing.T) { + t.Parallel() + taskClient := taskServiceClient() ctx := context.Background() @@ -334,6 +336,8 @@ func TestGetTasksByPatient(t *testing.T) { } func TestGetAssignedTasks(t *testing.T) { + t.Parallel() + taskClient := taskServiceClient() ctx := context.Background() @@ -349,9 +353,7 @@ func TestGetAssignedTasks(t *testing.T) { // give new user appropriate permissions authz := spicedb.NewSpiceDBAuthZ() - user := commonperm.User(userID) - org := commonperm.Organization(uuid.MustParse(hwtesting.FakeTokenOrganization)) - _, err := authz.Create(hwauthz.NewRelationship(user, "member", org)).Commit(ctx) + err := spicedb.AddUserToOrganization(ctx, authz, userID, uuid.MustParse(hwtesting.FakeTokenOrganization), false) require.NoError(t, err) taskIds := make([]string, 0, len(suffixMap)) @@ -387,7 +389,7 @@ func TestGetAssignedTasks(t *testing.T) { hwtesting.WaitForProjectionsToSettle() // client for userid - customTaskClient := pb.NewTaskServiceClient(hwtesting.GetGrpcConn(userID.String())) + customTaskClient := pb.NewTaskServiceClient(hwtesting.GetGrpcConn(userID.String(), "")) res, err := customTaskClient.GetAssignedTasks(ctx, &pb.GetAssignedTasksRequest{}) require.NoError(t, err) diff --git a/services/tasks-svc/stories/TaskTemplateCRUD_test.go b/services/tasks-svc/stories/TaskTemplateCRUD_test.go index 208951859..a4e25feca 100644 --- a/services/tasks-svc/stories/TaskTemplateCRUD_test.go +++ b/services/tasks-svc/stories/TaskTemplateCRUD_test.go @@ -23,6 +23,8 @@ func getTaskTemplate(t *testing.T, ctx context.Context, id string) *pb.GetTaskTe } func TestCreateUpdateGetTaskTemplate(t *testing.T) { + t.Parallel() + ctx := context.Background() taskTemplateClient := taskTemplateServiceClient() wardServiceClient := wardServiceClient() diff --git a/services/tasks-svc/stories/WardCRUD_test.go b/services/tasks-svc/stories/WardCRUD_test.go index 380b4c4ab..d4c5725f7 100644 --- a/services/tasks-svc/stories/WardCRUD_test.go +++ b/services/tasks-svc/stories/WardCRUD_test.go @@ -3,8 +3,6 @@ package stories import ( "context" pb "gen/services/tasks_svc/v1" - "hwauthz" - "hwauthz/commonperm" "hwauthz/spicedb" "hwtesting" "hwutil" @@ -21,6 +19,8 @@ import ( // - Create a new ward // - Update it func TestCreateUpdateGetWard(t *testing.T) { + t.Parallel() + ctx := context.Background() wardClient := wardServiceClient() @@ -91,23 +91,19 @@ func prepareWards(t *testing.T, ctx context.Context, client pb.WardServiceClient } func TestGetRecentWards(t *testing.T) { + t.Parallel() + ctx := context.Background() userID := uuid.New() // new user for this test, to prevent interference with other tests // give user appropriate permissions authz := spicedb.NewSpiceDBAuthZ() - _, err := authz.Create( - hwauthz.NewRelationship( - commonperm.User(userID), - "member", - commonperm.Organization(uuid.MustParse(hwtesting.FakeTokenOrganization)), - ), - ).Commit(ctx) + err := spicedb.AddUserToOrganization(ctx, authz, userID, uuid.MustParse(hwtesting.FakeTokenOrganization), false) require.NoError(t, err) - wardClient := pb.NewWardServiceClient(hwtesting.GetGrpcConn(userID.String())) - taskClient := pb.NewTaskServiceClient(hwtesting.GetGrpcConn(userID.String())) - patientClient := pb.NewPatientServiceClient(hwtesting.GetGrpcConn(userID.String())) + wardClient := pb.NewWardServiceClient(hwtesting.GetGrpcConn(userID.String(), "")) + taskClient := pb.NewTaskServiceClient(hwtesting.GetGrpcConn(userID.String(), "")) + patientClient := pb.NewPatientServiceClient(hwtesting.GetGrpcConn(userID.String(), "")) wardIds := prepareWards(t, ctx, wardClient, 11) consistencies := make(map[string]string) @@ -175,6 +171,8 @@ func TestGetRecentWards(t *testing.T) { } func TestGetWards(t *testing.T) { + t.Parallel() + wardClient := wardServiceClient() ctx := context.Background() @@ -214,6 +212,8 @@ func TestGetWards(t *testing.T) { } func TestGetWardOverviews(t *testing.T) { + t.Parallel() + wardClient := wardServiceClient() patientClient := patientServiceClient() taskClient := taskServiceClient() @@ -285,6 +285,8 @@ func TestGetWardOverviews(t *testing.T) { } func TestGetWardDetails(t *testing.T) { + t.Parallel() + wardClient := wardServiceClient() taskTemplateClient := taskTemplateServiceClient() ctx := context.Background() diff --git a/services/tasks-svc/stories/setup_test.go b/services/tasks-svc/stories/setup_test.go index 639a86cb5..602864505 100644 --- a/services/tasks-svc/stories/setup_test.go +++ b/services/tasks-svc/stories/setup_test.go @@ -71,27 +71,27 @@ func TestMain(m *testing.M) { } func bedServiceClient() pb.BedServiceClient { - return pb.NewBedServiceClient(hwtesting.GetGrpcConn("")) + return pb.NewBedServiceClient(hwtesting.GetGrpcConn("", "")) } func roomServiceClient() pb.RoomServiceClient { - return pb.NewRoomServiceClient(hwtesting.GetGrpcConn("")) + return pb.NewRoomServiceClient(hwtesting.GetGrpcConn("", "")) } func patientServiceClient() pb.PatientServiceClient { - return pb.NewPatientServiceClient(hwtesting.GetGrpcConn("")) + return pb.NewPatientServiceClient(hwtesting.GetGrpcConn("", "")) } func taskServiceClient() pb.TaskServiceClient { - return pb.NewTaskServiceClient(hwtesting.GetGrpcConn("")) + return pb.NewTaskServiceClient(hwtesting.GetGrpcConn("", "")) } func taskTemplateServiceClient() pb.TaskTemplateServiceClient { - return pb.NewTaskTemplateServiceClient(hwtesting.GetGrpcConn("")) + return pb.NewTaskTemplateServiceClient(hwtesting.GetGrpcConn("", "")) } func wardServiceClient() pb.WardServiceClient { - return pb.NewWardServiceClient(hwtesting.GetGrpcConn("")) + return pb.NewWardServiceClient(hwtesting.GetGrpcConn("", "")) } func prepareWard(t *testing.T, ctx context.Context, suffix string) (wardID, wardConsistency string) { diff --git a/services/updates-svc/go.mod b/services/updates-svc/go.mod index a8a932f25..25ecd52a0 100644 --- a/services/updates-svc/go.mod +++ b/services/updates-svc/go.mod @@ -69,6 +69,11 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.2 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/jzelinskie/stringz v0.0.3 // indirect github.com/klauspost/compress v1.17.9 // indirect @@ -108,6 +113,7 @@ require ( github.com/testcontainers/testcontainers-go/modules/redis v0.34.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect @@ -125,6 +131,7 @@ require ( golang.org/x/crypto v0.31.0 // indirect golang.org/x/net v0.33.0 // indirect golang.org/x/oauth2 v0.25.0 // indirect + golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect @@ -134,5 +141,6 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect hwauthz v0.0.0 // indirect + hwdb v0.0.0 // indirect hwlocale v0.0.0 // indirect ) diff --git a/services/updates-svc/go.sum b/services/updates-svc/go.sum index 5be5cd3dc..bf86804be 100644 --- a/services/updates-svc/go.sum +++ b/services/updates-svc/go.sum @@ -131,14 +131,16 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw= +github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= -github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= -github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= +github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jzelinskie/stringz v0.0.3 h1:0GhG3lVMYrYtIvRbxvQI6zqRTT1P1xyQlpa0FhfUXas= @@ -196,6 +198,8 @@ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2sz github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= +github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s= +github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A= 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= @@ -259,6 +263,8 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0 h1:EhPtK0mgrgaTMXpegE69hvoSOVC1Ahk8+QJ9B8b+OdU= +github.com/vgarvardt/pgx-google-uuid/v5 v5.6.0/go.mod h1:5LtFrNEkgzxHvXPO9eOvcXsSn9/KeKYgx9kjeI2oXQI= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= diff --git a/services/updates-svc/stories/setup_test.go b/services/updates-svc/stories/setup_test.go index 66c14499e..5bfad4c9a 100644 --- a/services/updates-svc/stories/setup_test.go +++ b/services/updates-svc/stories/setup_test.go @@ -54,5 +54,5 @@ func TestMain(m *testing.M) { } func updatesServiceClient() pb.UpdatesServiceClient { - return pb.NewUpdatesServiceClient(hwtesting.GetGrpcConn("")) + return pb.NewUpdatesServiceClient(hwtesting.GetGrpcConn("", "")) } diff --git a/services/updates-svc/stories/updates_test.go b/services/updates-svc/stories/updates_test.go index 7917373d3..781442580 100644 --- a/services/updates-svc/stories/updates_test.go +++ b/services/updates-svc/stories/updates_test.go @@ -30,6 +30,8 @@ func requireTrue(t *testing.T, b bool) { } func TestOpenAndClosingReceiveUpdatesStream(t *testing.T) { + t.Parallel() + ctx := context.Background() updatesClient := updatesServiceClient() @@ -40,6 +42,8 @@ func TestOpenAndClosingReceiveUpdatesStream(t *testing.T) { } func TestReceivingEvents(t *testing.T) { + t.Parallel() + ctx := context.Background() es := eventstoredb.SetupEventStoreByEnv() @@ -101,6 +105,8 @@ func TestReceivingEvents(t *testing.T) { } func TestAutoClosingWhenTokenExpiresReceiveUpdateStream(t *testing.T) { + t.Parallel() + ctx := context.Background() updatesClient := updatesServiceClient() diff --git a/services/user-svc/cmd/service/main.go b/services/user-svc/cmd/service/main.go index 2bf8cdd88..fd85d5425 100644 --- a/services/user-svc/cmd/service/main.go +++ b/services/user-svc/cmd/service/main.go @@ -21,7 +21,7 @@ func Main(version string, ready func()) { common.WithNonOrganizationMethod(pb.OrganizationService_CreatePersonalOrganization_FullMethodName), ) - closeDBPool := hwdb.SetupDatabaseFromEnv(ctx) + ctx, closeDBPool := hwdb.SetupDatabaseFromEnv(ctx) defer closeDBPool() kc, err := hwkc.BuildClient(ctx) diff --git a/services/user-svc/go.sum b/services/user-svc/go.sum index 32b17981c..603412e43 100644 --- a/services/user-svc/go.sum +++ b/services/user-svc/go.sum @@ -194,6 +194,8 @@ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2sz github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= +github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s= +github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A= 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= diff --git a/services/user-svc/internal/organization/organization.go b/services/user-svc/internal/organization/organization.go index 916c7a834..965c04970 100644 --- a/services/user-svc/internal/organization/organization.go +++ b/services/user-svc/internal/organization/organization.go @@ -9,14 +9,13 @@ import ( pb "gen/services/user_svc/v1" "hwauthz" "hwauthz/commonperm" + "hwauthz/spicedb" "hwdb" "hwlocale" "hwutil" - "user-svc/internal/organization/perm" - userPerm "user-svc/internal/user/perm" - "user-svc/internal/hwkc" + "user-svc/internal/organization/perm" "user-svc/locale" "user-svc/repos/organization-repo" "user-svc/repos/user-repo" @@ -79,7 +78,7 @@ func (s ServiceServer) GetOrganization( ctx context.Context, req *pb.GetOrganizationRequest, ) (*pb.GetOrganizationResponse, error) { - organizationRepo := organizationrepo.New(hwdb.GetDB()) + organizationRepo := organizationrepo.New(hwdb.MustGetDB(ctx)) id, err := uuid.Parse(req.GetId()) if err != nil { @@ -171,7 +170,7 @@ type OrganizationWithMembers struct { func GetOrganizationsByUserID( ctx context.Context, userID uuid.UUID, authz hwauthz.AuthZ, ) ([]OrganizationWithMembers, error) { - organizationRepo := organizationrepo.New(hwdb.GetDB()) + organizationRepo := organizationrepo.New(hwdb.MustGetDB(ctx)) rows, err := organizationRepo.GetOrganizationsWithMembersByUser(ctx, userID) err = hwdb.Error(ctx, err) @@ -280,7 +279,7 @@ func (s ServiceServer) UpdateOrganization( return nil, err } - organizationRepo := organizationrepo.New(hwdb.GetDB()) + organizationRepo := organizationrepo.New(hwdb.MustGetDB(ctx)) organizationID, err := uuid.Parse(req.GetId()) if err != nil { @@ -314,7 +313,7 @@ func (s ServiceServer) DeleteOrganization( return nil, err } - organizationRepo := organizationrepo.New(hwdb.GetDB()) + organizationRepo := organizationrepo.New(hwdb.MustGetDB(ctx)) organizationID, err := uuid.Parse(req.GetId()) if err != nil { @@ -353,7 +352,7 @@ func (s ServiceServer) RemoveMember( } log := zlog.Ctx(ctx) - organizationRepo := organizationrepo.New(hwdb.GetDB()) + organizationRepo := organizationrepo.New(hwdb.MustGetDB(ctx)) userID, err := uuid.Parse(req.UserId) if err != nil { @@ -380,9 +379,9 @@ func (s ServiceServer) RemoveMember( deletedUser := commonperm.User(userID) if _, err := s.authz. - Delete(hwauthz.NewRelationship(deletedUser, perm.OrganizationMember, permOrg)). - Delete(hwauthz.NewRelationship(deletedUser, perm.OrganizationLeader, permOrg)). - Delete(hwauthz.NewRelationship(permOrg, userPerm.UserOrganization, deletedUser)). + Delete(hwauthz.NewRelationship(deletedUser, commonperm.OrganizationMember, permOrg)). + Delete(hwauthz.NewRelationship(deletedUser, commonperm.OrganizationLeader, permOrg)). + Delete(hwauthz.NewRelationship(permOrg, commonperm.UserOrganization, deletedUser)). Commit(ctx); err != nil { return nil, err } @@ -400,7 +399,7 @@ func (s ServiceServer) InviteMember( req *pb.InviteMemberRequest, ) (*pb.InviteMemberResponse, error) { log := zlog.Ctx(ctx) - organizationRepo := organizationrepo.New(hwdb.GetDB()) + organizationRepo := organizationrepo.New(hwdb.MustGetDB(ctx)) // check permissions permUser := commonperm.UserFromCtx(ctx) @@ -480,7 +479,7 @@ func (s ServiceServer) GetInvitationsByOrganization( ctx context.Context, req *pb.GetInvitationsByOrganizationRequest, ) (*pb.GetInvitationsByOrganizationResponse, error) { - organizationRepo := organizationrepo.New(hwdb.GetDB()) + organizationRepo := organizationrepo.New(hwdb.MustGetDB(ctx)) organizationID, err := uuid.Parse(req.OrganizationId) if err != nil { @@ -549,7 +548,7 @@ func (s ServiceServer) GetInvitationsByUser( ctx context.Context, req *pb.GetInvitationsByUserRequest, ) (*pb.GetInvitationsByUserResponse, error) { - organizationRepo := organizationrepo.New(hwdb.GetDB()) + organizationRepo := organizationrepo.New(hwdb.MustGetDB(ctx)) claims, err := auth.GetAuthClaims(ctx) if err != nil { @@ -613,7 +612,7 @@ func (s ServiceServer) GetMembersByOrganization( ctx context.Context, req *pb.GetMembersByOrganizationRequest, ) (*pb.GetMembersByOrganizationResponse, error) { - organizationRepo := organizationrepo.New(hwdb.GetDB()) + organizationRepo := organizationrepo.New(hwdb.MustGetDB(ctx)) organizationID, err := uuid.Parse(req.GetId()) if err != nil { @@ -653,7 +652,7 @@ func (s ServiceServer) AcceptInvitation( ctx context.Context, req *pb.AcceptInvitationRequest, ) (*pb.AcceptInvitationResponse, error) { - organizationRepo := organizationrepo.New(hwdb.GetDB()) + organizationRepo := organizationrepo.New(hwdb.MustGetDB(ctx)) invitationID, err := uuid.Parse(req.InvitationId) if err != nil { @@ -716,11 +715,12 @@ func (s ServiceServer) AcceptInvitation( // Add user to organization if err := AddUserToOrganization( ctx, - hwdb.GetDB(), + hwdb.MustGetDB(ctx), s.authz, s.kc, userID, currentInvitation.OrganizationID, + false, ); err != nil { return nil, status.Error(codes.Internal, err.Error()) } @@ -732,7 +732,7 @@ func (s ServiceServer) DeclineInvitation( ctx context.Context, req *pb.DeclineInvitationRequest, ) (*pb.DeclineInvitationResponse, error) { - organizationRepo := organizationrepo.New(hwdb.GetDB()) + organizationRepo := organizationrepo.New(hwdb.MustGetDB(ctx)) invitationID, err := uuid.Parse(req.InvitationId) if err != nil { @@ -797,7 +797,7 @@ func (s ServiceServer) RevokeInvitation( ctx context.Context, req *pb.RevokeInvitationRequest, ) (*pb.RevokeInvitationResponse, error) { - organizationRepo := organizationrepo.New(hwdb.GetDB()) + organizationRepo := organizationrepo.New(hwdb.MustGetDB(ctx)) log := zlog.Ctx(ctx) @@ -860,7 +860,7 @@ func CreateOrganizationAndAddUser( authz hwauthz.AuthZ, ) (*organizationrepo.Organization, error) { // open tx - tx, rollback, err := hwdb.BeginTx(hwdb.GetDB(), ctx) + tx, rollback, err := hwdb.BeginTx(hwdb.MustGetDB(ctx), ctx) if err != nil { return nil, err } @@ -901,6 +901,7 @@ func CreateOrganizationAndAddUser( kc, userID, organizationID, + true, ); err != nil { return nil, err } @@ -920,6 +921,7 @@ func AddUserToOrganization( kc hwkc.IClient, userID uuid.UUID, organizationID uuid.UUID, + leader bool, ) error { log := zlog.Ctx(ctx) organizationRepo := organizationrepo.New(tx) @@ -939,11 +941,7 @@ func AddUserToOrganization( } // add user to org in spice - permUser := commonperm.User(userID) - permOrg := commonperm.Organization(organizationID) - rel := hwauthz.NewRelationship(permUser, perm.OrganizationLeader, permOrg) - backRel := hwauthz.NewRelationship(permOrg, userPerm.UserOrganization, permUser) - if _, err := authz.Create(rel).Create(backRel).Commit(ctx); err != nil { + if err := spicedb.AddUserToOrganization(ctx, authz, userID, organizationID, leader); err != nil { return err } @@ -998,7 +996,7 @@ func (s ServiceServer) CreatePersonalOrganization( personalOrganizationLocale := hwlocale.Localize(ctx, locale.PersonalOrganizationName(ctx)) organizationName := fmt.Sprintf("%s %s", personalOrganizationLocale, userClaims.Name) - userRepo := userrepo.New(hwdb.GetDB()) + userRepo := userrepo.New(hwdb.MustGetDB(ctx)) // create user, if it does not exist yet userResult, err := hwdb.Optional(userRepo.GetUserById)(ctx, userID) diff --git a/services/user-svc/internal/organization/perm/perm.go b/services/user-svc/internal/organization/perm/perm.go index 1e0f11402..f19989b22 100644 --- a/services/user-svc/internal/organization/perm/perm.go +++ b/services/user-svc/internal/organization/perm/perm.go @@ -30,11 +30,6 @@ const ( InviteOrganization = "organization" ) -const ( - OrganizationMember = "member" - OrganizationLeader = "leader" -) - // Permissions const ( diff --git a/services/user-svc/internal/user/perm/perm.go b/services/user-svc/internal/user/perm/perm.go index 85efb4f9e..d27c867bc 100644 --- a/services/user-svc/internal/user/perm/perm.go +++ b/services/user-svc/internal/user/perm/perm.go @@ -1,9 +1,5 @@ package perm -// Direct Relations - -const UserOrganization = "organization" - // Permissions const ( diff --git a/services/user-svc/internal/user/user.go b/services/user-svc/internal/user/user.go index e53d2dcc8..df8e2bc6f 100644 --- a/services/user-svc/internal/user/user.go +++ b/services/user-svc/internal/user/user.go @@ -41,7 +41,7 @@ func (s ServiceServer) ReadPublicProfile( ctx context.Context, req *pb.ReadPublicProfileRequest, ) (*pb.ReadPublicProfileResponse, error) { - userRepo := userrepo.New(hwdb.GetDB()) + userRepo := userrepo.New(hwdb.MustGetDB(ctx)) userID, err := uuid.Parse(req.GetId()) if err != nil { @@ -74,7 +74,7 @@ func (s ServiceServer) ReadPublicProfile( func HandleUserUpdatedEvent(ctx context.Context, evt *daprcmn.TopicEvent) (retry bool, err error) { log := zlog.Ctx(ctx) - userRepo := userrepo.New(hwdb.GetDB()) + userRepo := userrepo.New(hwdb.MustGetDB(ctx)) var payload events.UserUpdatedEvent if err := proto.Unmarshal(evt.RawData, &payload); err != nil { diff --git a/services/user-svc/stories/OrganizationCRUD_test.go b/services/user-svc/stories/OrganizationCRUD_test.go index 1181bb0ca..c5cad6fb6 100644 --- a/services/user-svc/stories/OrganizationCRUD_test.go +++ b/services/user-svc/stories/OrganizationCRUD_test.go @@ -14,9 +14,11 @@ import ( ) func TestCreateUpdateGetOrganization(t *testing.T) { + t.Parallel() + ctx := context.Background() - client := pb.NewOrganizationServiceClient(hwtesting.GetGrpcConn("")) + client := pb.NewOrganizationServiceClient(hwtesting.GetGrpcConn("", "")) // // create new org