From 802bb56ea10fc6f8273db8de6bf23f762078b5d9 Mon Sep 17 00:00:00 2001 From: Fabi <38692350+fgerschwiler@users.noreply.github.com> Date: Mon, 20 Apr 2020 15:16:33 +0200 Subject: [PATCH] feat: project cache (#48) * feat: eventstore repository * fix: remove gorm * version * feat: pkg * feat: add some files for project * feat: eventstore without eventstore-lib * rename files * gnueg * fix: key json * fix: add object * fix: change imports * fix: internal models * fix: some imports * fix: global model * fix: add some functions on repo * feat(eventstore): sdk * fix(eventstore): search query * fix(eventstore): rename app to eventstore * delete empty test * remove unused func * merge master * fix(eventstore): tests * fix(models): delete unused struct * fix: some funcitons * feat(eventstore): implemented push events * fix: move project eventstore to project package * fix: change project eventstore funcs * feat(eventstore): overwrite context data * fix: change project eventstore * fix: add project repo to mgmt server * feat(types): SQL-config * fix: commented code * feat(eventstore): options to overwrite editor * feat: auth interceptor and cockroach migrations * fix: migrations * fix: fix filter * fix: not found on getbyid * fix: add sequence * fix: add some tests * fix(eventstore): nullable sequence * fix: add some tests * merge * fix: add some tests * fix(migrations): correct statements for sequence * fix: add some tests * fix: add some tests * fix: changes from mr * Update internal/eventstore/models/field.go Co-Authored-By: livio-a * fix(eventstore): code quality * fix: add types to aggregate/Event-types * fix(eventstore): rename modifier* to editor* * fix(eventstore): delete editor_org * fix(migrations): remove editor_org field, rename modifier_* to editor_* * fix: generate files * fix(eventstore): tests * fix(eventstore): rename modifier to editor * fix(migrations): add cluster migration, fix(migrations): fix typo of host in clean clsuter * fix(eventstore): move health * fix(eventstore): AggregateTypeFilter aggregateType as param * code quality * feat: add member funcs * feat: add member model * feat: add member events * feat: add member repo model * fix: project member funcs * fix: add tests * fix: add tests * feat: implement member requests * fix: merge master * fix: read existing in project repo * fix: fix tests * feat: add internal cache * feat: add cache mock * fix: return values of cache mock * fix: add cache config * fix: use eventstore sdk * Update internal/project/model/project_member.go Co-Authored-By: Silvan * fix: use get project func * fix: return err not nil * fix: change err types * Update internal/cache/bigcache/cache.go Co-Authored-By: livio-a * Update internal/cache/config/config.go Co-Authored-By: livio-a * Update internal/cache/config/config.go Co-Authored-By: livio-a * fix: config * fix: config * resolve conversations * fix: mr changes * fix: fix decode of bigcache * feat: test caches * fix: remove unnecessary code Co-authored-by: adlerhurst Co-authored-by: livio-a --- cmd/zitadel/startup.yaml | 4 + go.mod | 2 + go.sum | 9 + internal/cache/bigcache/bigcache_test.go | 221 ++++++++++++++++++ internal/cache/bigcache/cache.go | 67 ++++++ internal/cache/bigcache/config.go | 16 ++ internal/cache/cache.go | 7 + internal/cache/config.go | 5 + internal/cache/config/config.go | 58 +++++ internal/cache/fastcache/config.go | 11 + internal/cache/fastcache/fastcache.go | 56 +++++ internal/cache/fastcache/fastcache_test.go | 216 +++++++++++++++++ internal/cache/generate.go | 3 + internal/cache/mock/cache.mock.go | 75 ++++++ internal/eventstore/config.go | 2 + .../repository/eventsourcing/project.go | 11 +- .../repository/eventsourcing/repository.go | 2 +- .../project/repository/eventsourcing/cache.go | 34 +++ .../repository/eventsourcing/eventstore.go | 52 +++-- .../eventsourcing/eventstore_mock_test.go | 22 +- .../eventsourcing/eventstore_test.go | 2 +- 21 files changed, 840 insertions(+), 35 deletions(-) create mode 100644 internal/cache/bigcache/bigcache_test.go create mode 100644 internal/cache/bigcache/cache.go create mode 100644 internal/cache/bigcache/config.go create mode 100644 internal/cache/cache.go create mode 100644 internal/cache/config.go create mode 100644 internal/cache/config/config.go create mode 100644 internal/cache/fastcache/config.go create mode 100644 internal/cache/fastcache/fastcache.go create mode 100644 internal/cache/fastcache/fastcache_test.go create mode 100644 internal/cache/generate.go create mode 100644 internal/cache/mock/cache.mock.go create mode 100644 internal/project/repository/eventsourcing/cache.go diff --git a/cmd/zitadel/startup.yaml b/cmd/zitadel/startup.yaml index 4f96d4e25c..1c4fe07288 100644 --- a/cmd/zitadel/startup.yaml +++ b/cmd/zitadel/startup.yaml @@ -27,6 +27,10 @@ Mgmt: User: 'management' Database: 'management' SSLmode: disable + Cache: + Type: 'fastcache' + Config: + MaxCacheSizeInByte: 10485760 #10mb Auth: API: diff --git a/go.mod b/go.mod index ffc157ae0e..d861654ef7 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,8 @@ require ( github.com/Masterminds/goutils v1.1.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/sprig v2.22.0+incompatible + github.com/VictoriaMetrics/fastcache v1.5.7 + github.com/allegro/bigcache v1.2.1 github.com/aws/aws-sdk-go v1.30.4 // indirect github.com/caos/logging v0.0.1 github.com/cockroachdb/cockroach-go v0.0.0-20200312223839-f565e4789405 diff --git a/go.sum b/go.sum index b41ac92d1d..d5f6a9ed99 100644 --- a/go.sum +++ b/go.sum @@ -41,6 +41,11 @@ github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3Q github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw= +github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= +github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -56,6 +61,8 @@ github.com/caos/logging v0.0.1/go.mod h1:9LKiDE2ChuGv6CHYif/kiugrfEXu9AwDiFWSreX github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -119,6 +126,8 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4 h1:+EOh4OY6tjM6ZueeUKinl1f0U2820HzQOuf1iqMnsks= github.com/golang/protobuf v1.4.0-rc.4/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= diff --git a/internal/cache/bigcache/bigcache_test.go b/internal/cache/bigcache/bigcache_test.go new file mode 100644 index 0000000000..9500ca92ff --- /dev/null +++ b/internal/cache/bigcache/bigcache_test.go @@ -0,0 +1,221 @@ +package bigcache + +import ( + a_cache "github.com/allegro/bigcache" + "github.com/caos/zitadel/internal/errors" + es_models "github.com/caos/zitadel/internal/eventstore/models" + "reflect" + "testing" +) + +type TestStruct struct { + Test string +} + +func getBigCacheMock() *Bigcache { + cache, _ := a_cache.NewBigCache(a_cache.DefaultConfig(2000)) + return &Bigcache{cache: cache} +} + +func TestSet(t *testing.T) { + type args struct { + cache *Bigcache + key string + value *TestStruct + } + type res struct { + result *TestStruct + errFunc func(err error) bool + } + tests := []struct { + name string + args args + res res + }{ + { + name: "set cache no err", + args: args{ + cache: getBigCacheMock(), + key: "KEY", + value: &TestStruct{Test: "Test"}, + }, + res: res{ + result: &TestStruct{}, + }, + }, + { + name: "key empty", + args: args{ + cache: getBigCacheMock(), + key: "", + value: &TestStruct{Test: "Test"}, + }, + res: res{ + errFunc: errors.IsErrorInvalidArgument, + }, + }, + { + name: "set cache nil value", + args: args{ + cache: getBigCacheMock(), + key: "KEY", + }, + res: res{ + errFunc: errors.IsErrorInvalidArgument, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.args.cache.Set(tt.args.key, tt.args.value) + + if tt.res.errFunc == nil && err != nil { + t.Errorf("got wrong result should not get err: %v ", err) + } + + if tt.res.errFunc == nil { + tt.args.cache.Get(tt.args.key, tt.res.result) + if tt.res.result == nil { + t.Errorf("got wrong result should get result: %v ", err) + } + } + if tt.res.errFunc != nil && !tt.res.errFunc(err) { + t.Errorf("got wrong err: %v ", err) + } + }) + } +} + +func TestGet(t *testing.T) { + type args struct { + event []*es_models.Event + cache *Bigcache + key string + setValue *TestStruct + getValue *TestStruct + } + type res struct { + result *TestStruct + errFunc func(err error) bool + } + tests := []struct { + name string + args args + res res + }{ + { + name: "get cache no err", + args: args{ + cache: getBigCacheMock(), + key: "KEY", + setValue: &TestStruct{Test: "Test"}, + getValue: &TestStruct{Test: "Test"}, + }, + res: res{ + result: &TestStruct{Test: "Test"}, + }, + }, + { + name: "get cache no key", + args: args{ + cache: getBigCacheMock(), + setValue: &TestStruct{Test: "Test"}, + getValue: &TestStruct{Test: "Test"}, + }, + res: res{ + errFunc: errors.IsErrorInvalidArgument, + }, + }, + { + name: "get cache no value", + args: args{ + cache: getBigCacheMock(), + key: "KEY", + setValue: &TestStruct{Test: "Test"}, + }, + res: res{ + errFunc: errors.IsErrorInvalidArgument, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.args.cache.Set("KEY", tt.args.setValue) + if err != nil { + t.Errorf("something went wrong") + } + + err = tt.args.cache.Get(tt.args.key, tt.args.getValue) + + if tt.res.errFunc == nil && err != nil { + t.Errorf("got wrong result should not get err: %v ", err) + } + + if tt.res.errFunc == nil && !reflect.DeepEqual(tt.args.getValue, tt.res.result) { + t.Errorf("got wrong result expected: %v actual: %v", tt.res.result, tt.args.getValue) + } + + if tt.res.errFunc != nil && !tt.res.errFunc(err) { + t.Errorf("got wrong err: %v ", err) + } + }) + } +} + +func TestDelete(t *testing.T) { + type args struct { + event []*es_models.Event + cache *Bigcache + key string + setValue *TestStruct + getValue *TestStruct + } + type res struct { + result *TestStruct + errFunc func(err error) bool + } + tests := []struct { + name string + args args + res res + }{ + { + name: "delete cache no err", + args: args{ + cache: getBigCacheMock(), + key: "KEY", + setValue: &TestStruct{Test: "Test"}, + }, + res: res{}, + }, + { + name: "get cache no key", + args: args{ + cache: getBigCacheMock(), + setValue: &TestStruct{Test: "Test"}, + getValue: &TestStruct{Test: "Test"}, + }, + res: res{ + errFunc: errors.IsErrorInvalidArgument, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.args.cache.Set("KEY", tt.args.setValue) + if err != nil { + t.Errorf("something went wrong") + } + + err = tt.args.cache.Delete(tt.args.key) + + if tt.res.errFunc == nil && err != nil { + t.Errorf("got wrong result should not get err: %v ", err) + } + + if tt.res.errFunc != nil && !tt.res.errFunc(err) { + t.Errorf("got wrong err: %v ", err) + } + }) + } +} diff --git a/internal/cache/bigcache/cache.go b/internal/cache/bigcache/cache.go new file mode 100644 index 0000000000..23d695fbe4 --- /dev/null +++ b/internal/cache/bigcache/cache.go @@ -0,0 +1,67 @@ +package bigcache + +import ( + "bytes" + "encoding/gob" + "github.com/caos/logging" + "reflect" + + "github.com/caos/zitadel/internal/errors" + + a_cache "github.com/allegro/bigcache" +) + +type Bigcache struct { + cache *a_cache.BigCache +} + +func NewBigcache(c *Config) (*Bigcache, error) { + cacheConfig := a_cache.DefaultConfig(c.CacheLifetime) + cacheConfig.HardMaxCacheSize = c.MaxCacheSizeInMB + cache, err := a_cache.NewBigCache(cacheConfig) + if err != nil { + return nil, err + } + + return &Bigcache{ + cache: cache, + }, nil +} + +func (c *Bigcache) Set(key string, object interface{}) error { + if key == "" || reflect.ValueOf(object).IsNil() { + return errors.ThrowInvalidArgument(nil, "FASTC-du73s", "key or value should not be empty") + } + var b bytes.Buffer + enc := gob.NewEncoder(&b) + if err := enc.Encode(object); err != nil { + return errors.ThrowInvalidArgument(err, "FASTC-RUyxI", "unable to encode object") + } + return c.cache.Set(key, b.Bytes()) +} + +func (c *Bigcache) Get(key string, ptrToObject interface{}) error { + if key == "" || reflect.ValueOf(ptrToObject).IsNil() { + return errors.ThrowInvalidArgument(nil, "FASTC-dksoe", "key or value should not be empty") + } + value, err := c.cache.Get(key) + if err == a_cache.ErrEntryNotFound { + return errors.ThrowNotFound(err, "BIGCA-we32s", "not in cache") + } + if err != nil { + logging.Log("BIGCA-ftofbc").WithError(err).Info("read from cache failed") + return errors.ThrowInvalidArgument(err, "BIGCA-3idls", "error in reading from cache") + } + + b := bytes.NewBuffer(value) + dec := gob.NewDecoder(b) + + return dec.Decode(ptrToObject) +} + +func (c *Bigcache) Delete(key string) error { + if key == "" { + return errors.ThrowInvalidArgument(nil, "FASTC-clsi2", "key should not be empty") + } + return c.cache.Delete(key) +} diff --git a/internal/cache/bigcache/config.go b/internal/cache/bigcache/config.go new file mode 100644 index 0000000000..4111041462 --- /dev/null +++ b/internal/cache/bigcache/config.go @@ -0,0 +1,16 @@ +package bigcache + +import ( + "github.com/caos/zitadel/internal/cache" + "time" +) + +type Config struct { + MaxCacheSizeInMB int + //CacheLifetime if set, entries older than the lifetime will be deleted on cleanup (every minute) + CacheLifetime time.Duration +} + +func (c *Config) NewCache() (cache.Cache, error) { + return NewBigcache(c) +} diff --git a/internal/cache/cache.go b/internal/cache/cache.go new file mode 100644 index 0000000000..b2eef1cc6f --- /dev/null +++ b/internal/cache/cache.go @@ -0,0 +1,7 @@ +package cache + +type Cache interface { + Set(key string, object interface{}) error + Get(key string, ptrToObject interface{}) error + Delete(key string) error +} diff --git a/internal/cache/config.go b/internal/cache/config.go new file mode 100644 index 0000000000..86c298241d --- /dev/null +++ b/internal/cache/config.go @@ -0,0 +1,5 @@ +package cache + +type Config interface { + NewCache() (Cache, error) +} diff --git a/internal/cache/config/config.go b/internal/cache/config/config.go new file mode 100644 index 0000000000..6527d6107e --- /dev/null +++ b/internal/cache/config/config.go @@ -0,0 +1,58 @@ +package config + +import ( + "encoding/json" + "github.com/caos/zitadel/internal/cache" + "github.com/caos/zitadel/internal/cache/bigcache" + "github.com/caos/zitadel/internal/cache/fastcache" + "github.com/caos/zitadel/internal/errors" +) + +type CacheConfig struct { + Type string + Config cache.Config +} + +var caches = map[string]func() cache.Config{ + "bigcache": func() cache.Config { return &bigcache.Config{} }, + "fastcache": func() cache.Config { return &fastcache.Config{} }, +} + +func (c *CacheConfig) UnmarshalJSON(data []byte) error { + var rc struct { + Type string + Config json.RawMessage + } + + if err := json.Unmarshal(data, &rc); err != nil { + return errors.ThrowInternal(err, "CONFI-98ejs", "unable to unmarshal config") + } + + c.Type = rc.Type + + var err error + c.Config, err = newCacheConfig(c.Type, rc.Config) + if err != nil { + return errors.ThrowInternal(err, "CONFI-do9es", "unable create config") + } + + return nil +} + +func newCacheConfig(cacheType string, configData []byte) (cache.Config, error) { + t, ok := caches[cacheType] + if !ok { + return nil, errors.ThrowInternal(nil, "CONFI-di328s", "no config") + } + + cacheConfig := t() + if len(configData) == 0 { + return cacheConfig, nil + } + + if err := json.Unmarshal(configData, cacheConfig); err != nil { + return nil, errors.ThrowInternal(nil, "CONFI-skei3", "could not read config") + } + + return cacheConfig, nil +} diff --git a/internal/cache/fastcache/config.go b/internal/cache/fastcache/config.go new file mode 100644 index 0000000000..bbd32861d5 --- /dev/null +++ b/internal/cache/fastcache/config.go @@ -0,0 +1,11 @@ +package fastcache + +import "github.com/caos/zitadel/internal/cache" + +type Config struct { + MaxCacheSizeInByte int +} + +func (c *Config) NewCache() (cache.Cache, error) { + return NewFastcache(c) +} diff --git a/internal/cache/fastcache/fastcache.go b/internal/cache/fastcache/fastcache.go new file mode 100644 index 0000000000..7a5b1ef30e --- /dev/null +++ b/internal/cache/fastcache/fastcache.go @@ -0,0 +1,56 @@ +package fastcache + +import ( + "bytes" + "encoding/gob" + "github.com/caos/zitadel/internal/errors" + "reflect" + + "github.com/VictoriaMetrics/fastcache" +) + +type Fastcache struct { + cache *fastcache.Cache +} + +func NewFastcache(config *Config) (*Fastcache, error) { + return &Fastcache{ + cache: fastcache.New(config.MaxCacheSizeInByte), + }, nil +} + +func (fc *Fastcache) Set(key string, object interface{}) error { + if key == "" || reflect.ValueOf(object).IsNil() { + return errors.ThrowInvalidArgument(nil, "FASTC-87dj3", "key or value should not be empty") + } + var b bytes.Buffer + enc := gob.NewEncoder(&b) + if err := enc.Encode(object); err != nil { + return errors.ThrowInvalidArgument(err, "FASTC-RUyxI", "unable to encode object") + } + fc.cache.Set([]byte(key), b.Bytes()) + return nil +} + +func (fc *Fastcache) Get(key string, ptrToObject interface{}) error { + if key == "" || reflect.ValueOf(ptrToObject).IsNil() { + return errors.ThrowInvalidArgument(nil, "FASTC-di8es", "key or value should not be empty") + } + data := fc.cache.Get(nil, []byte(key)) + if len(data) == 0 { + return errors.ThrowNotFound(nil, "FASTC-xYzSm", "key not found") + } + + b := bytes.NewBuffer(data) + dec := gob.NewDecoder(b) + + return dec.Decode(ptrToObject) +} + +func (fc *Fastcache) Delete(key string) error { + if key == "" { + return errors.ThrowInvalidArgument(nil, "FASTC-lod92", "key should not be empty") + } + fc.cache.Del([]byte(key)) + return nil +} diff --git a/internal/cache/fastcache/fastcache_test.go b/internal/cache/fastcache/fastcache_test.go new file mode 100644 index 0000000000..2189e5440c --- /dev/null +++ b/internal/cache/fastcache/fastcache_test.go @@ -0,0 +1,216 @@ +package fastcache + +import ( + "github.com/VictoriaMetrics/fastcache" + "github.com/caos/zitadel/internal/errors" + es_models "github.com/caos/zitadel/internal/eventstore/models" + "reflect" + "testing" +) + +type TestStruct struct { + Test string +} + +func TestSet(t *testing.T) { + type args struct { + cache *Fastcache + key string + value *TestStruct + } + type res struct { + result *TestStruct + errFunc func(err error) bool + } + tests := []struct { + name string + args args + res res + }{ + { + name: "set cache no err", + args: args{ + cache: &Fastcache{cache: fastcache.New(2000)}, + key: "KEY", + value: &TestStruct{Test: "Test"}, + }, + res: res{ + result: &TestStruct{}, + }, + }, + { + name: "key empty", + args: args{ + cache: &Fastcache{cache: fastcache.New(2000)}, + key: "", + value: &TestStruct{Test: "Test"}, + }, + res: res{ + errFunc: errors.IsErrorInvalidArgument, + }, + }, + { + name: "set cache nil value", + args: args{ + cache: &Fastcache{cache: fastcache.New(2000)}, + key: "KEY", + }, + res: res{ + errFunc: errors.IsErrorInvalidArgument, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.args.cache.Set(tt.args.key, tt.args.value) + + if tt.res.errFunc == nil && err != nil { + t.Errorf("got wrong result should not get err: %v ", err) + } + + if tt.res.errFunc == nil { + tt.args.cache.Get(tt.args.key, tt.res.result) + if tt.res.result == nil { + t.Errorf("got wrong result should get result: %v ", err) + } + } + if tt.res.errFunc != nil && !tt.res.errFunc(err) { + t.Errorf("got wrong err: %v ", err) + } + }) + } +} + +func TestGet(t *testing.T) { + type args struct { + event []*es_models.Event + cache *Fastcache + key string + setValue *TestStruct + getValue *TestStruct + } + type res struct { + result *TestStruct + errFunc func(err error) bool + } + tests := []struct { + name string + args args + res res + }{ + { + name: "get cache no err", + args: args{ + cache: &Fastcache{cache: fastcache.New(2000)}, + key: "KEY", + setValue: &TestStruct{Test: "Test"}, + getValue: &TestStruct{Test: "Test"}, + }, + res: res{ + result: &TestStruct{Test: "Test"}, + }, + }, + { + name: "get cache no key", + args: args{ + cache: &Fastcache{cache: fastcache.New(2000)}, + setValue: &TestStruct{Test: "Test"}, + getValue: &TestStruct{Test: "Test"}, + }, + res: res{ + errFunc: errors.IsErrorInvalidArgument, + }, + }, + { + name: "get cache no value", + args: args{ + cache: &Fastcache{cache: fastcache.New(2000)}, + key: "KEY", + setValue: &TestStruct{Test: "Test"}, + }, + res: res{ + errFunc: errors.IsErrorInvalidArgument, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.args.cache.Set("KEY", tt.args.setValue) + if err != nil { + t.Errorf("something went wrong") + } + + err = tt.args.cache.Get(tt.args.key, tt.args.getValue) + + if tt.res.errFunc == nil && err != nil { + t.Errorf("got wrong result should not get err: %v ", err) + } + + if tt.res.errFunc == nil && !reflect.DeepEqual(tt.args.getValue, tt.res.result) { + t.Errorf("got wrong result expected: %v actual: %v", tt.res.result, tt.args.getValue) + } + + if tt.res.errFunc != nil && !tt.res.errFunc(err) { + t.Errorf("got wrong err: %v ", err) + } + }) + } +} + +func TestDelete(t *testing.T) { + type args struct { + event []*es_models.Event + cache *Fastcache + key string + setValue *TestStruct + getValue *TestStruct + } + type res struct { + result *TestStruct + errFunc func(err error) bool + } + tests := []struct { + name string + args args + res res + }{ + { + name: "delete cache no err", + args: args{ + cache: &Fastcache{cache: fastcache.New(2000)}, + key: "KEY", + setValue: &TestStruct{Test: "Test"}, + }, + res: res{}, + }, + { + name: "get cache no key", + args: args{ + cache: &Fastcache{cache: fastcache.New(2000)}, + setValue: &TestStruct{Test: "Test"}, + getValue: &TestStruct{Test: "Test"}, + }, + res: res{ + errFunc: errors.IsErrorInvalidArgument, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.args.cache.Set("KEY", tt.args.setValue) + if err != nil { + t.Errorf("something went wrong") + } + + err = tt.args.cache.Delete(tt.args.key) + + if tt.res.errFunc == nil && err != nil { + t.Errorf("got wrong result should not get err: %v ", err) + } + + if tt.res.errFunc != nil && !tt.res.errFunc(err) { + t.Errorf("got wrong err: %v ", err) + } + }) + } +} diff --git a/internal/cache/generate.go b/internal/cache/generate.go new file mode 100644 index 0000000000..52bda0cbad --- /dev/null +++ b/internal/cache/generate.go @@ -0,0 +1,3 @@ +package cache + +//go:generate mockgen -package mock -destination ./mock/cache.mock.go github.com/caos/zitadel/internal/cache Cache diff --git a/internal/cache/mock/cache.mock.go b/internal/cache/mock/cache.mock.go new file mode 100644 index 0000000000..240d34da50 --- /dev/null +++ b/internal/cache/mock/cache.mock.go @@ -0,0 +1,75 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/caos/zitadel/internal/cache (interfaces: Cache) + +// Package mock is a generated GoMock package. +package mock + +import ( + gomock "github.com/golang/mock/gomock" + reflect "reflect" +) + +// MockCache is a mock of Cache interface +type MockCache struct { + ctrl *gomock.Controller + recorder *MockCacheMockRecorder +} + +// MockCacheMockRecorder is the mock recorder for MockCache +type MockCacheMockRecorder struct { + mock *MockCache +} + +// NewMockCache creates a new mock instance +func NewMockCache(ctrl *gomock.Controller) *MockCache { + mock := &MockCache{ctrl: ctrl} + mock.recorder = &MockCacheMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockCache) EXPECT() *MockCacheMockRecorder { + return m.recorder +} + +// Delete mocks base method +func (m *MockCache) Delete(arg0 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete +func (mr *MockCacheMockRecorder) Delete(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockCache)(nil).Delete), arg0) +} + +// Get mocks base method +func (m *MockCache) Get(arg0 string, arg1 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Get indicates an expected call of Get +func (mr *MockCacheMockRecorder) Get(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCache)(nil).Get), arg0, arg1) +} + +// Set mocks base method +func (m *MockCache) Set(arg0 string, arg1 interface{}) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Set", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Set indicates an expected call of Set +func (mr *MockCacheMockRecorder) Set(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockCache)(nil).Set), arg0, arg1) +} diff --git a/internal/eventstore/config.go b/internal/eventstore/config.go index e8b23e0fd5..ab3fbc2b6f 100644 --- a/internal/eventstore/config.go +++ b/internal/eventstore/config.go @@ -1,6 +1,7 @@ package eventstore import ( + "github.com/caos/zitadel/internal/cache/config" "github.com/caos/zitadel/internal/eventstore/internal/repository/sql" "github.com/caos/zitadel/internal/eventstore/models" ) @@ -8,6 +9,7 @@ import ( type Config struct { Repository sql.Config ServiceName string + Cache *config.CacheConfig } func Start(conf Config) (Eventstore, error) { diff --git a/internal/management/repository/eventsourcing/project.go b/internal/management/repository/eventsourcing/project.go index 1aa81105e7..68dc982078 100644 --- a/internal/management/repository/eventsourcing/project.go +++ b/internal/management/repository/eventsourcing/project.go @@ -13,16 +13,7 @@ type ProjectRepo struct { } func (repo *ProjectRepo) ProjectByID(ctx context.Context, id string) (project *proj_model.Project, err error) { - //viewProject, err := repo.view.ProjectByID(id) - //if err != nil && !caos_errs.IsNotFound(err) { - // return nil, err - //} - //if viewProject != nil { - // project = org_view.ProjectToModel(viewProject) - //} else { - project = proj_model.NewProject(id) - //} - return repo.ProjectEvents.ProjectByID(ctx, project) + return repo.ProjectEvents.ProjectByID(ctx, id) } func (repo *ProjectRepo) CreateProject(ctx context.Context, name string) (*proj_model.Project, error) { diff --git a/internal/management/repository/eventsourcing/repository.go b/internal/management/repository/eventsourcing/repository.go index 331f190941..78cf71a8d4 100644 --- a/internal/management/repository/eventsourcing/repository.go +++ b/internal/management/repository/eventsourcing/repository.go @@ -33,7 +33,7 @@ func Start(conf Config) (*EsRepository, error) { //conf.Spooler.SQL = sql //spool := spooler.StartSpooler(conf.Spooler) - project, err := es_proj.StartProject(es_proj.ProjectConfig{Eventstore: es}) + project, err := es_proj.StartProject(es_proj.ProjectConfig{Eventstore: es, Cache: conf.Eventstore.Cache}) if err != nil { return nil, err } diff --git a/internal/project/repository/eventsourcing/cache.go b/internal/project/repository/eventsourcing/cache.go new file mode 100644 index 0000000000..4cd90ce7a3 --- /dev/null +++ b/internal/project/repository/eventsourcing/cache.go @@ -0,0 +1,34 @@ +package eventsourcing + +import ( + "github.com/caos/logging" + "github.com/caos/zitadel/internal/cache" + "github.com/caos/zitadel/internal/cache/config" + "github.com/caos/zitadel/internal/eventstore/models" +) + +type ProjectCache struct { + projectCache cache.Cache +} + +func StartCache(conf *config.CacheConfig) (*ProjectCache, error) { + projectCache, err := conf.Config.NewCache() + logging.Log("EVENT-vDneN").OnError(err).Panic("unable to create project cache") + + return &ProjectCache{projectCache: projectCache}, nil +} + +func (c *ProjectCache) getProject(ID string) (project *Project) { + project = &Project{ObjectRoot: models.ObjectRoot{ID: ID}} + if err := c.projectCache.Get(ID, project); err != nil { + logging.Log("EVENT-4eTZh").WithError(err).Debug("error in getting cache") + } + return project +} + +func (c *ProjectCache) cacheProject(project *Project) { + err := c.projectCache.Set(project.ID, project) + if err != nil { + logging.Log("EVENT-ThnBb").WithError(err).Debug("error in setting project cache") + } +} diff --git a/internal/project/repository/eventsourcing/eventstore.go b/internal/project/repository/eventsourcing/eventstore.go index cfb9554820..f77bfe709b 100644 --- a/internal/project/repository/eventsourcing/eventstore.go +++ b/internal/project/repository/eventsourcing/eventstore.go @@ -2,8 +2,7 @@ package eventsourcing import ( "context" - "github.com/caos/zitadel/internal/eventstore/models" - + "github.com/caos/zitadel/internal/cache/config" caos_errs "github.com/caos/zitadel/internal/errors" es_int "github.com/caos/zitadel/internal/eventstore" es_sdk "github.com/caos/zitadel/internal/eventstore/sdk" @@ -12,28 +11,38 @@ import ( type ProjectEventstore struct { es_int.Eventstore + projectCache *ProjectCache } type ProjectConfig struct { es_int.Eventstore + Cache *config.CacheConfig } func StartProject(conf ProjectConfig) (*ProjectEventstore, error) { - return &ProjectEventstore{Eventstore: conf.Eventstore}, nil + projectCache, err := StartCache(conf.Cache) + if err != nil { + return nil, err + } + return &ProjectEventstore{ + Eventstore: conf.Eventstore, + projectCache: projectCache, + }, nil } -func (es *ProjectEventstore) ProjectByID(ctx context.Context, project *proj_model.Project) (*proj_model.Project, error) { +func (es *ProjectEventstore) ProjectByID(ctx context.Context, id string) (*proj_model.Project, error) { + project := es.projectCache.getProject(id) + query, err := ProjectByIDQuery(project.ID, project.Sequence) if err != nil { return nil, err } - - p := ProjectFromModel(project) - err = es_sdk.Filter(ctx, es.FilterEvents, p.AppendEvents, query) + err = es_sdk.Filter(ctx, es.FilterEvents, project.AppendEvents, query) if err != nil { return nil, err } - return ProjectToModel(p), nil + es.projectCache.cacheProject(project) + return ProjectToModel(project), nil } func (es *ProjectEventstore) CreateProject(ctx context.Context, project *proj_model.Project) (*proj_model.Project, error) { @@ -49,6 +58,7 @@ func (es *ProjectEventstore) CreateProject(ctx context.Context, project *proj_mo return nil, err } + es.projectCache.cacheProject(repoProject) return ProjectToModel(repoProject), nil } @@ -56,7 +66,7 @@ func (es *ProjectEventstore) UpdateProject(ctx context.Context, project *proj_mo if !project.IsValid() { return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "Name is required") } - existingProject, err := es.ProjectByID(ctx, &proj_model.Project{ObjectRoot: models.ObjectRoot{ID: project.ID, Sequence: 0}}) + existingProject, err := es.ProjectByID(ctx, project.ID) if err != nil { return nil, err } @@ -69,11 +79,12 @@ func (es *ProjectEventstore) UpdateProject(ctx context.Context, project *proj_mo return nil, err } + es.projectCache.cacheProject(repoExisting) return ProjectToModel(repoExisting), nil } func (es *ProjectEventstore) DeactivateProject(ctx context.Context, id string) (*proj_model.Project, error) { - existing, err := es.ProjectByID(ctx, &proj_model.Project{ObjectRoot: models.ObjectRoot{ID: id, Sequence: 0}}) + existing, err := es.ProjectByID(ctx, id) if err != nil { return nil, err } @@ -84,11 +95,13 @@ func (es *ProjectEventstore) DeactivateProject(ctx context.Context, id string) ( repoExisting := ProjectFromModel(existing) aggregate := ProjectDeactivateAggregate(es.AggregateCreator(), repoExisting) es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, aggregate) + + es.projectCache.cacheProject(repoExisting) return ProjectToModel(repoExisting), nil } func (es *ProjectEventstore) ReactivateProject(ctx context.Context, id string) (*proj_model.Project, error) { - existing, err := es.ProjectByID(ctx, &proj_model.Project{ObjectRoot: models.ObjectRoot{ID: id, Sequence: 0}}) + existing, err := es.ProjectByID(ctx, id) if err != nil { return nil, err } @@ -99,6 +112,8 @@ func (es *ProjectEventstore) ReactivateProject(ctx context.Context, id string) ( repoExisting := ProjectFromModel(existing) aggregate := ProjectReactivateAggregate(es.AggregateCreator(), repoExisting) es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, aggregate) + + es.projectCache.cacheProject(repoExisting) return ProjectToModel(repoExisting), nil } @@ -106,7 +121,7 @@ func (es *ProjectEventstore) ProjectMemberByIDs(ctx context.Context, member *pro if member.UserID == "" { return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-ld93d", "userID missing") } - project, err := es.ProjectByID(ctx, &proj_model.Project{ObjectRoot: models.ObjectRoot{ID: member.ID, Sequence: 0}}) + project, err := es.ProjectByID(ctx, member.ID) if err != nil { return nil, err } @@ -122,7 +137,7 @@ func (es *ProjectEventstore) AddProjectMember(ctx context.Context, member *proj_ if !member.IsValid() { return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "UserID and Roles are required") } - existing, err := es.ProjectByID(ctx, &proj_model.Project{ObjectRoot: models.ObjectRoot{ID: member.ID, Sequence: 0}}) + existing, err := es.ProjectByID(ctx, member.ID) if err != nil { return nil, err } @@ -134,6 +149,10 @@ func (es *ProjectEventstore) AddProjectMember(ctx context.Context, member *proj_ addAggregate := ProjectMemberAddedAggregate(es.Eventstore.AggregateCreator(), repoProject, repoMember) err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, addAggregate) + if err != nil { + return nil, err + } + es.projectCache.cacheProject(repoProject) for _, m := range repoProject.Members { if m.UserID == member.UserID { return ProjectMemberToModel(m), nil @@ -146,7 +165,7 @@ func (es *ProjectEventstore) ChangeProjectMember(ctx context.Context, member *pr if !member.IsValid() { return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "UserID and Roles are required") } - existing, err := es.ProjectByID(ctx, &proj_model.Project{ObjectRoot: models.ObjectRoot{ID: member.ID, Sequence: 0}}) + existing, err := es.ProjectByID(ctx, member.ID) if err != nil { return nil, err } @@ -158,7 +177,7 @@ func (es *ProjectEventstore) ChangeProjectMember(ctx context.Context, member *pr projectAggregate := ProjectMemberChangedAggregate(es.Eventstore.AggregateCreator(), repoProject, repoMember) err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, projectAggregate) - + es.projectCache.cacheProject(repoProject) for _, m := range repoProject.Members { if m.UserID == member.UserID { return ProjectMemberToModel(m), nil @@ -171,7 +190,7 @@ func (es *ProjectEventstore) RemoveProjectMember(ctx context.Context, member *pr if member.UserID == "" { return caos_errs.ThrowPreconditionFailed(nil, "EVENT-d43fs", "UserID and Roles are required") } - existing, err := es.ProjectByID(ctx, &proj_model.Project{ObjectRoot: models.ObjectRoot{ID: member.ID, Sequence: 0}}) + existing, err := es.ProjectByID(ctx, member.ID) if err != nil { return err } @@ -183,5 +202,6 @@ func (es *ProjectEventstore) RemoveProjectMember(ctx context.Context, member *pr projectAggregate := ProjectMemberRemovedAggregate(es.Eventstore.AggregateCreator(), repoProject, repoMember) err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, projectAggregate) + es.projectCache.cacheProject(repoProject) return err } diff --git a/internal/project/repository/eventsourcing/eventstore_mock_test.go b/internal/project/repository/eventsourcing/eventstore_mock_test.go index 3be3bc4e70..b72a3df441 100644 --- a/internal/project/repository/eventsourcing/eventstore_mock_test.go +++ b/internal/project/repository/eventsourcing/eventstore_mock_test.go @@ -2,12 +2,20 @@ package eventsourcing import ( "encoding/json" + mock_cache "github.com/caos/zitadel/internal/cache/mock" "github.com/caos/zitadel/internal/eventstore/mock" es_models "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/project/model" "github.com/golang/mock/gomock" ) +func GetMockCache(ctrl *gomock.Controller) *ProjectCache { + mockCache := mock_cache.NewMockCache(ctrl) + mockCache.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + mockCache.EXPECT().Set(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + return &ProjectCache{projectCache: mockCache} +} + func GetMockProjectByIDOK(ctrl *gomock.Controller) *ProjectEventstore { data, _ := json.Marshal(Project{Name: "Name"}) events := []*es_models.Event{ @@ -15,14 +23,14 @@ func GetMockProjectByIDOK(ctrl *gomock.Controller) *ProjectEventstore { } mockEs := mock.NewMockEventstore(ctrl) mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil) - return &ProjectEventstore{Eventstore: mockEs} + return &ProjectEventstore{Eventstore: mockEs, projectCache: GetMockCache(ctrl)} } func GetMockProjectByIDNoEvents(ctrl *gomock.Controller) *ProjectEventstore { events := []*es_models.Event{} mockEs := mock.NewMockEventstore(ctrl) mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil) - return &ProjectEventstore{Eventstore: mockEs} + return &ProjectEventstore{Eventstore: mockEs, projectCache: GetMockCache(ctrl)} } func GetMockManipulateProject(ctrl *gomock.Controller) *ProjectEventstore { @@ -34,7 +42,7 @@ func GetMockManipulateProject(ctrl *gomock.Controller) *ProjectEventstore { mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil) mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST")) mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil) - return &ProjectEventstore{Eventstore: mockEs} + return &ProjectEventstore{Eventstore: mockEs, projectCache: GetMockCache(ctrl)} } func GetMockManipulateInactiveProject(ctrl *gomock.Controller) *ProjectEventstore { @@ -47,7 +55,7 @@ func GetMockManipulateInactiveProject(ctrl *gomock.Controller) *ProjectEventstor mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil) mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST")) mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil) - return &ProjectEventstore{Eventstore: mockEs} + return &ProjectEventstore{Eventstore: mockEs, projectCache: GetMockCache(ctrl)} } func GetMockManipulateProjectWithMember(ctrl *gomock.Controller) *ProjectEventstore { @@ -61,7 +69,7 @@ func GetMockManipulateProjectWithMember(ctrl *gomock.Controller) *ProjectEventst mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil) mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST")) mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil) - return &ProjectEventstore{Eventstore: mockEs} + return &ProjectEventstore{Eventstore: mockEs, projectCache: GetMockCache(ctrl)} } func GetMockManipulateProjectNoEvents(ctrl *gomock.Controller) *ProjectEventstore { @@ -70,7 +78,7 @@ func GetMockManipulateProjectNoEvents(ctrl *gomock.Controller) *ProjectEventstor mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil) mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST")) mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil) - return &ProjectEventstore{Eventstore: mockEs} + return &ProjectEventstore{Eventstore: mockEs, projectCache: GetMockCache(ctrl)} } func GetMockProjectMemberByIDsOK(ctrl *gomock.Controller) *ProjectEventstore { @@ -82,5 +90,5 @@ func GetMockProjectMemberByIDsOK(ctrl *gomock.Controller) *ProjectEventstore { } mockEs := mock.NewMockEventstore(ctrl) mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil) - return &ProjectEventstore{Eventstore: mockEs} + return &ProjectEventstore{Eventstore: mockEs, projectCache: GetMockCache(ctrl)} } diff --git a/internal/project/repository/eventsourcing/eventstore_test.go b/internal/project/repository/eventsourcing/eventstore_test.go index 89c365c8da..89204d8e3a 100644 --- a/internal/project/repository/eventsourcing/eventstore_test.go +++ b/internal/project/repository/eventsourcing/eventstore_test.go @@ -61,7 +61,7 @@ func TestProjectByID(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := tt.args.es.ProjectByID(nil, tt.args.project) + result, err := tt.args.es.ProjectByID(nil, tt.args.project.ID) if !tt.res.wantErr && result.ID != tt.res.project.ID { t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.project.ID, result.ID)