Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ require (
github.com/joho/godotenv v1.5.1
github.com/kelseyhightower/envconfig v1.4.0
github.com/kroma-labs/sentinel-go v0.3.4
github.com/markbates/goth v1.82.0
github.com/midtrans/midtrans-go v1.3.8
github.com/nats-io/nats.go v1.52.0
github.com/openai/openai-go/v2 v2.7.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,8 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/markbates/goth v1.82.0 h1:8j/c34AjBSTNzO7zTsOyP5IYCQCMBTRBHAbBt/PI0bQ=
github.com/markbates/goth v1.82.0/go.mod h1:/DRlcq0pyqkKToyZjsL2KgiA1zbF1HIjE7u2uC79rUk=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
Expand Down
18 changes: 9 additions & 9 deletions internal/adapters/core/service/store/in_memory_state_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,36 +29,36 @@ func NewInMemoryStateStore() *inMemoryStateStore {
return store
}

func (vss *inMemoryStateStore) Store(ctx context.Context, state string, expiry time.Duration) error {
func (vss *inMemoryStateStore) Store(ctx context.Context, state string, value string, expiry time.Duration) error {
key := vss.constructKey(state)
entry := stateEntry{
value: state,
value: value,
expiresAt: time.Now().Add(expiry),
}
vss.data.Store(key, entry)
return nil
}

func (vss *inMemoryStateStore) VerifyAndDelete(ctx context.Context, state string) error {
func (vss *inMemoryStateStore) VerifyAndDelete(ctx context.Context, state string) (string, error) {
key := vss.constructKey(state)
value, loaded := vss.data.LoadAndDelete(key)
raw, loaded := vss.data.LoadAndDelete(key)
if !loaded {
return ungerr.BadRequestError("invalid state")
return "", ungerr.BadRequestError("invalid state")
}

entry := value.(stateEntry)
entry := raw.(stateEntry)
if time.Now().After(entry.expiresAt) {
return ungerr.BadRequestError("invalid state")
return "", ungerr.BadRequestError("invalid state")
}

return nil
return entry.value, nil
}

func (vss *inMemoryStateStore) startCleanup() {
vss.wg.Add(1)
go func() {
defer vss.wg.Done()
ticker := time.NewTicker(1 * time.Minute) // Adjust interval as needed
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()

for {
Expand Down
16 changes: 9 additions & 7 deletions internal/adapters/core/service/store/nats_kv_state_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"time"

"github.com/itsLeonB/cashback/internal/core/logger"
"github.com/itsLeonB/cashback/internal/core/otel"
"github.com/itsLeonB/ungerr"
"github.com/nats-io/nats.go/jetstream"
Expand All @@ -19,36 +20,37 @@ func NewNATSKVStateStore(kv jetstream.KeyValue) *natsKVStateStore {
return &natsKVStateStore{kv: kv}
}

func (s *natsKVStateStore) Store(ctx context.Context, state string, expiry time.Duration) error {
func (s *natsKVStateStore) Store(ctx context.Context, state string, value string, expiry time.Duration) error {
ctx, span := otel.Tracer.Start(ctx, "natsKVStateStore.Store")
defer span.End()

_, err := s.kv.Create(ctx, s.constructKey(state), []byte(state), jetstream.KeyTTL(expiry))
_, err := s.kv.Create(ctx, s.constructKey(state), []byte(value), jetstream.KeyTTL(expiry))
if err != nil {
return ungerr.Wrap(err, "error storing state in NATS KV")
}

return nil
}

func (s *natsKVStateStore) VerifyAndDelete(ctx context.Context, state string) error {
func (s *natsKVStateStore) VerifyAndDelete(ctx context.Context, state string) (string, error) {
ctx, span := otel.Tracer.Start(ctx, "natsKVStateStore.VerifyAndDelete")
defer span.End()

key := s.constructKey(state)
entry, err := s.kv.Get(ctx, key)
if err != nil {
if errors.Is(err, jetstream.ErrKeyNotFound) {
return ungerr.BadRequestError("invalid state")
return "", ungerr.BadRequestError("invalid state")
}
return ungerr.Wrap(err, "error verifying state in NATS KV")
return "", ungerr.Wrap(err, "error verifying state in NATS KV")
}

if err := s.kv.Delete(ctx, key, jetstream.LastRevision(entry.Revision())); err != nil {
return ungerr.BadRequestError("invalid state")
logger.Warnf("error deleting state from NATS KV: %v", err)
return "", ungerr.BadRequestError("invalid state")
}

return nil
return string(entry.Value()), nil
}

func (s *natsKVStateStore) Shutdown() error {
Expand Down
49 changes: 20 additions & 29 deletions internal/adapters/core/service/store/nats_kv_state_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"github.com/nats-io/nats.go/jetstream"
"github.com/stretchr/testify/assert"
)

type mockKV struct {
Expand All @@ -26,10 +27,11 @@ func (m *mockKV) Create(ctx context.Context, key string, value []byte, opts ...j
}

func (m *mockKV) Get(ctx context.Context, key string) (jetstream.KeyValueEntry, error) {
if _, ok := m.entries[key]; !ok {
v, ok := m.entries[key]
if !ok {
return nil, jetstream.ErrKeyNotFound
}
return &mockEntry{revision: 1}, nil
return &mockEntry{revision: 1, value: v}, nil
}

func (m *mockKV) Delete(ctx context.Context, key string, opts ...jetstream.KVDeleteOpt) error {
Expand All @@ -40,66 +42,55 @@ func (m *mockKV) Delete(ctx context.Context, key string, opts ...jetstream.KVDel
type mockEntry struct {
jetstream.KeyValueEntry
revision uint64
value []byte
}

func (e *mockEntry) Revision() uint64 { return e.revision }
func (e *mockEntry) Value() []byte { return e.value }

func TestNATSKVStateStore_Store(t *testing.T) {
kv := newMockKV()
s := NewNATSKVStateStore(kv)

err := s.Store(context.Background(), "abc123", 5*time.Minute)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err := s.Store(context.Background(), "abc123", "session-data", 5*time.Minute)
assert.NoError(t, err)

if _, ok := kv.entries["state.abc123"]; !ok {
t.Fatal("expected key to be stored")
}
assert.Contains(t, kv.entries, "state.abc123")
}

func TestNATSKVStateStore_Store_Duplicate(t *testing.T) {
kv := newMockKV()
s := NewNATSKVStateStore(kv)

_ = s.Store(context.Background(), "abc123", 5*time.Minute)
err := s.Store(context.Background(), "abc123", 5*time.Minute)
if err == nil {
t.Fatal("expected error on duplicate store")
}
_ = s.Store(context.Background(), "abc123", "session-data", 5*time.Minute)
err := s.Store(context.Background(), "abc123", "session-data", 5*time.Minute)
assert.Error(t, err)
}

func TestNATSKVStateStore_VerifyAndDelete(t *testing.T) {
kv := newMockKV()
s := NewNATSKVStateStore(kv)

_ = s.Store(context.Background(), "abc123", 5*time.Minute)
_ = s.Store(context.Background(), "abc123", "session-data", 5*time.Minute)

err := s.VerifyAndDelete(context.Background(), "abc123")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
value, err := s.VerifyAndDelete(context.Background(), "abc123")
assert.NoError(t, err)
assert.Equal(t, "session-data", value)

if _, ok := kv.entries["state.abc123"]; ok {
t.Fatal("expected key to be deleted")
}
assert.NotContains(t, kv.entries, "state.abc123")
}

func TestNATSKVStateStore_VerifyAndDelete_NotFound(t *testing.T) {
kv := newMockKV()
s := NewNATSKVStateStore(kv)

err := s.VerifyAndDelete(context.Background(), "nonexistent")
if err == nil {
t.Fatal("expected error for nonexistent state")
}
_, err := s.VerifyAndDelete(context.Background(), "nonexistent")
assert.Error(t, err)
}

func TestNATSKVStateStore_Shutdown(t *testing.T) {
kv := newMockKV()
s := NewNATSKVStateStore(kv)

if err := s.Shutdown(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
assert.NoError(t, s.Shutdown())
}
4 changes: 2 additions & 2 deletions internal/core/service/store/state_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
)

type StateStore interface {
Store(ctx context.Context, state string, expiry time.Duration) error
VerifyAndDelete(ctx context.Context, state string) error
Store(ctx context.Context, state string, value string, expiry time.Duration) error
VerifyAndDelete(ctx context.Context, state string) (string, error)
Shutdown() error
}

Expand Down
107 changes: 0 additions & 107 deletions internal/domain/service/oauth/google_provider_service.go

This file was deleted.

Loading
Loading