Skip to content

Commit d6e93ef

Browse files
alexluongclaude
andauthored
refactor: extract tenantstore and simplify models package (#666)
* refactor: create tenantstore driver interface, drivertest, and memtenantstore Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: create redistenantstore - Redis driver.TenantStore implementation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: split drivertest into conformance and ListTenant suites Separate RunConformanceTests (CRUD, destination listing, match, misc) from RunListTenantTests (enrichment, excludes deleted, input validation, keyset pagination, pagination suite). ListTenant tests now only run on RediSearch backends (Redis Stack, Dragonfly Stack) instead of skipping at runtime. Pagination suite moved from standalone redistenantstore test into the drivertest framework. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: create tenantstore facade and update all consumers Add tenantstore/tenantstore.go facade following the logstore pattern with type aliases, re-exported error sentinels, and a Config-based constructor. Update all consumers from models.EntityStore to tenantstore.TenantStore across services, apirouter, publishmq, and deliverymq packages. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: clean up models/ and remove all EntityStore references Delete moved code from models/ (entity.go, encryption.go and their tests), remove parseRedisHash/parseTimestamp methods from tenant.go and destination.go. Rename EntityStore to MockStore in destinationmockserver, and rename entityStore to tenantStore across apirouter, publishmq, deliverymq, telemetry, and config. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: simplify internal/models pkg * chore: gofmt --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 50465c5 commit d6e93ef

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+4225
-3825
lines changed

internal/apirouter/destination_handlers.go

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,23 @@ import (
1212
"github.com/hookdeck/outpost/internal/logging"
1313
"github.com/hookdeck/outpost/internal/models"
1414
"github.com/hookdeck/outpost/internal/telemetry"
15+
"github.com/hookdeck/outpost/internal/tenantstore"
1516
"github.com/hookdeck/outpost/internal/util/maputil"
1617
)
1718

1819
type DestinationHandlers struct {
1920
logger *logging.Logger
2021
telemetry telemetry.Telemetry
21-
entityStore models.EntityStore
22+
tenantStore tenantstore.TenantStore
2223
topics []string
2324
registry destregistry.Registry
2425
}
2526

26-
func NewDestinationHandlers(logger *logging.Logger, telemetry telemetry.Telemetry, entityStore models.EntityStore, topics []string, registry destregistry.Registry) *DestinationHandlers {
27+
func NewDestinationHandlers(logger *logging.Logger, telemetry telemetry.Telemetry, tenantStore tenantstore.TenantStore, topics []string, registry destregistry.Registry) *DestinationHandlers {
2728
return &DestinationHandlers{
2829
logger: logger,
2930
telemetry: telemetry,
30-
entityStore: entityStore,
31+
tenantStore: tenantStore,
3132
topics: topics,
3233
registry: registry,
3334
}
@@ -36,9 +37,9 @@ func NewDestinationHandlers(logger *logging.Logger, telemetry telemetry.Telemetr
3637
func (h *DestinationHandlers) List(c *gin.Context) {
3738
typeParams := c.QueryArray("type")
3839
topicsParams := c.QueryArray("topics")
39-
var opts models.ListDestinationByTenantOpts
40+
var opts tenantstore.ListDestinationByTenantOpts
4041
if len(typeParams) > 0 || len(topicsParams) > 0 {
41-
opts = models.WithDestinationFilter(models.DestinationFilter{
42+
opts = tenantstore.WithDestinationFilter(tenantstore.DestinationFilter{
4243
Type: typeParams,
4344
Topics: topicsParams,
4445
})
@@ -49,7 +50,7 @@ func (h *DestinationHandlers) List(c *gin.Context) {
4950
return
5051
}
5152

52-
destinations, err := h.entityStore.ListDestinationByTenant(c.Request.Context(), tenantID, opts)
53+
destinations, err := h.tenantStore.ListDestinationByTenant(c.Request.Context(), tenantID, opts)
5354
if err != nil {
5455
AbortWithError(c, http.StatusInternalServerError, NewErrInternalServer(err))
5556
return
@@ -96,7 +97,7 @@ func (h *DestinationHandlers) Create(c *gin.Context) {
9697
AbortWithValidationError(c, err)
9798
return
9899
}
99-
if err := h.entityStore.CreateDestination(c.Request.Context(), destination); err != nil {
100+
if err := h.tenantStore.CreateDestination(c.Request.Context(), destination); err != nil {
100101
h.handleUpsertDestinationError(c, err)
101102
return
102103
}
@@ -196,7 +197,7 @@ func (h *DestinationHandlers) Update(c *gin.Context) {
196197

197198
// Update destination.
198199
updatedDestination.UpdatedAt = time.Now()
199-
if err := h.entityStore.UpsertDestination(c.Request.Context(), updatedDestination); err != nil {
200+
if err := h.tenantStore.UpsertDestination(c.Request.Context(), updatedDestination); err != nil {
200201
h.handleUpsertDestinationError(c, err)
201202
return
202203
}
@@ -218,7 +219,7 @@ func (h *DestinationHandlers) Delete(c *gin.Context) {
218219
if destination == nil {
219220
return
220221
}
221-
if err := h.entityStore.DeleteDestination(c.Request.Context(), destination.TenantID, destination.ID); err != nil {
222+
if err := h.tenantStore.DeleteDestination(c.Request.Context(), destination.TenantID, destination.ID); err != nil {
222223
AbortWithError(c, http.StatusInternalServerError, NewErrInternalServer(err))
223224
return
224225
}
@@ -274,7 +275,7 @@ func (h *DestinationHandlers) setDisabilityHandler(c *gin.Context, disabled bool
274275
destination.DisabledAt = nil
275276
}
276277
if shouldUpdate {
277-
if err := h.entityStore.UpsertDestination(c.Request.Context(), *destination); err != nil {
278+
if err := h.tenantStore.UpsertDestination(c.Request.Context(), *destination); err != nil {
278279
h.handleUpsertDestinationError(c, err)
279280
return
280281
}
@@ -289,9 +290,9 @@ func (h *DestinationHandlers) setDisabilityHandler(c *gin.Context, disabled bool
289290
}
290291

291292
func (h *DestinationHandlers) mustRetrieveDestination(c *gin.Context, tenantID, destinationID string) *models.Destination {
292-
destination, err := h.entityStore.RetrieveDestination(c.Request.Context(), tenantID, destinationID)
293+
destination, err := h.tenantStore.RetrieveDestination(c.Request.Context(), tenantID, destinationID)
293294
if err != nil {
294-
if errors.Is(err, models.ErrDestinationDeleted) {
295+
if errors.Is(err, tenantstore.ErrDestinationDeleted) {
295296
c.Status(http.StatusNotFound)
296297
return nil
297298
}
@@ -310,7 +311,7 @@ func (h *DestinationHandlers) handleUpsertDestinationError(c *gin.Context, err e
310311
AbortWithValidationError(c, err)
311312
return
312313
}
313-
if errors.Is(err, models.ErrDuplicateDestination) {
314+
if errors.Is(err, tenantstore.ErrDuplicateDestination) {
314315
AbortWithError(c, http.StatusBadRequest, NewErrBadRequest(err))
315316
return
316317
}

internal/apirouter/destination_handlers_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func TestDestinationCreateHandler(t *testing.T) {
1818
t.Parallel()
1919

2020
router, _, redisClient := setupTestRouter(t, "", "")
21-
entityStore := setupTestEntityStore(t, redisClient, nil)
21+
tenantStore := setupTestTenantStore(t, redisClient)
2222

2323
t.Run("should set updated_at equal to created_at on creation", func(t *testing.T) {
2424
t.Parallel()
@@ -30,7 +30,7 @@ func TestDestinationCreateHandler(t *testing.T) {
3030
CreatedAt: time.Now(),
3131
UpdatedAt: time.Now(),
3232
}
33-
err := entityStore.UpsertTenant(context.Background(), tenant)
33+
err := tenantStore.UpsertTenant(context.Background(), tenant)
3434
if err != nil {
3535
t.Fatal(err)
3636
}
@@ -60,8 +60,8 @@ func TestDestinationCreateHandler(t *testing.T) {
6060

6161
// Cleanup
6262
if destID, ok := response["id"].(string); ok {
63-
entityStore.DeleteDestination(context.Background(), tenantID, destID)
63+
tenantStore.DeleteDestination(context.Background(), tenantID, destID)
6464
}
65-
entityStore.DeleteTenant(context.Background(), tenantID)
65+
tenantStore.DeleteTenant(context.Background(), tenantID)
6666
})
6767
}

internal/apirouter/log_handlers.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,13 @@ func parseIncludeOptions(c *gin.Context) IncludeOptions {
8888

8989
// APIAttempt is the API response for an attempt
9090
type APIAttempt struct {
91-
ID string `json:"id"`
92-
Status string `json:"status"`
93-
DeliveredAt time.Time `json:"delivered_at"`
94-
Code string `json:"code,omitempty"`
95-
ResponseData map[string]interface{} `json:"response_data,omitempty"`
96-
AttemptNumber int `json:"attempt_number"`
97-
Manual bool `json:"manual"`
91+
ID string `json:"id"`
92+
Status string `json:"status"`
93+
DeliveredAt time.Time `json:"delivered_at"`
94+
Code string `json:"code,omitempty"`
95+
ResponseData map[string]interface{} `json:"response_data,omitempty"`
96+
AttemptNumber int `json:"attempt_number"`
97+
Manual bool `json:"manual"`
9898

9999
// Expandable fields - string (ID) or object depending on expand
100100
Event interface{} `json:"event"`
@@ -146,8 +146,8 @@ type EventPaginatedResult struct {
146146
func toAPIAttempt(ar *logstore.AttemptRecord, opts IncludeOptions) APIAttempt {
147147
api := APIAttempt{
148148
AttemptNumber: ar.Attempt.AttemptNumber,
149-
Manual: ar.Attempt.Manual,
150-
Destination: ar.Attempt.DestinationID,
149+
Manual: ar.Attempt.Manual,
150+
Destination: ar.Attempt.DestinationID,
151151
}
152152

153153
if ar.Attempt != nil {
@@ -186,7 +186,7 @@ func toAPIAttempt(ar *logstore.AttemptRecord, opts IncludeOptions) APIAttempt {
186186
}
187187

188188
// TODO: Handle destination expansion
189-
// This would require injecting EntityStore into LogHandlers and batch-fetching
189+
// This would require injecting TenantStore into LogHandlers and batch-fetching
190190
// destinations by ID. Consider if this is needed - clients can fetch destination
191191
// details separately via GET /destinations/:id if needed.
192192

internal/apirouter/log_handlers_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ func TestListAttempts(t *testing.T) {
2323
// Create a tenant
2424
tenantID := idgen.String()
2525
destinationID := idgen.Destination()
26-
require.NoError(t, result.entityStore.UpsertTenant(context.Background(), models.Tenant{
26+
require.NoError(t, result.tenantStore.UpsertTenant(context.Background(), models.Tenant{
2727
ID: tenantID,
2828
CreatedAt: time.Now(),
2929
}))
30-
require.NoError(t, result.entityStore.UpsertDestination(context.Background(), models.Destination{
30+
require.NoError(t, result.tenantStore.UpsertDestination(context.Background(), models.Destination{
3131
ID: destinationID,
3232
TenantID: tenantID,
3333
Type: "webhook",
@@ -295,11 +295,11 @@ func TestRetrieveAttempt(t *testing.T) {
295295
// Create a tenant
296296
tenantID := idgen.String()
297297
destinationID := idgen.Destination()
298-
require.NoError(t, result.entityStore.UpsertTenant(context.Background(), models.Tenant{
298+
require.NoError(t, result.tenantStore.UpsertTenant(context.Background(), models.Tenant{
299299
ID: tenantID,
300300
CreatedAt: time.Now(),
301301
}))
302-
require.NoError(t, result.entityStore.UpsertDestination(context.Background(), models.Destination{
302+
require.NoError(t, result.tenantStore.UpsertDestination(context.Background(), models.Destination{
303303
ID: destinationID,
304304
TenantID: tenantID,
305305
Type: "webhook",
@@ -404,11 +404,11 @@ func TestRetrieveEvent(t *testing.T) {
404404
// Create a tenant
405405
tenantID := idgen.String()
406406
destinationID := idgen.Destination()
407-
require.NoError(t, result.entityStore.UpsertTenant(context.Background(), models.Tenant{
407+
require.NoError(t, result.tenantStore.UpsertTenant(context.Background(), models.Tenant{
408408
ID: tenantID,
409409
CreatedAt: time.Now(),
410410
}))
411-
require.NoError(t, result.entityStore.UpsertDestination(context.Background(), models.Destination{
411+
require.NoError(t, result.tenantStore.UpsertDestination(context.Background(), models.Destination{
412412
ID: destinationID,
413413
TenantID: tenantID,
414414
Type: "webhook",
@@ -489,11 +489,11 @@ func TestListEvents(t *testing.T) {
489489
// Create a tenant
490490
tenantID := idgen.String()
491491
destinationID := idgen.Destination()
492-
require.NoError(t, result.entityStore.UpsertTenant(context.Background(), models.Tenant{
492+
require.NoError(t, result.tenantStore.UpsertTenant(context.Background(), models.Tenant{
493493
ID: tenantID,
494494
CreatedAt: time.Now(),
495495
}))
496-
require.NoError(t, result.entityStore.UpsertDestination(context.Background(), models.Destination{
496+
require.NoError(t, result.tenantStore.UpsertDestination(context.Background(), models.Destination{
497497
ID: destinationID,
498498
TenantID: tenantID,
499499
Type: "webhook",

internal/apirouter/requiretenant_middleware.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,20 @@ import (
77

88
"github.com/gin-gonic/gin"
99
"github.com/hookdeck/outpost/internal/models"
10+
"github.com/hookdeck/outpost/internal/tenantstore"
1011
)
1112

12-
func RequireTenantMiddleware(entityStore models.EntityStore) gin.HandlerFunc {
13+
func RequireTenantMiddleware(tenantStore tenantstore.TenantStore) gin.HandlerFunc {
1314
return func(c *gin.Context) {
1415
tenantID, exists := c.Get("tenantID")
1516
if !exists {
1617
c.AbortWithStatus(http.StatusNotFound)
1718
return
1819
}
1920

20-
tenant, err := entityStore.RetrieveTenant(c.Request.Context(), tenantID.(string))
21+
tenant, err := tenantStore.RetrieveTenant(c.Request.Context(), tenantID.(string))
2122
if err != nil {
22-
if err == models.ErrTenantDeleted {
23+
if err == tenantstore.ErrTenantDeleted {
2324
c.AbortWithStatus(http.StatusNotFound)
2425
return
2526
}

internal/apirouter/requiretenant_middleware_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ func TestRequireTenantMiddleware(t *testing.T) {
3232
tenant := models.Tenant{
3333
ID: idgen.String(),
3434
}
35-
entityStore := setupTestEntityStore(t, redisClient, nil)
36-
err := entityStore.UpsertTenant(context.Background(), tenant)
35+
tenantStore := setupTestTenantStore(t, redisClient)
36+
err := tenantStore.UpsertTenant(context.Background(), tenant)
3737
require.Nil(t, err)
3838

3939
w := httptest.NewRecorder()

internal/apirouter/retry_handlers.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,26 @@ import (
88
"github.com/hookdeck/outpost/internal/logging"
99
"github.com/hookdeck/outpost/internal/logstore"
1010
"github.com/hookdeck/outpost/internal/models"
11+
"github.com/hookdeck/outpost/internal/tenantstore"
1112
"go.uber.org/zap"
1213
)
1314

1415
type RetryHandlers struct {
1516
logger *logging.Logger
16-
entityStore models.EntityStore
17+
tenantStore tenantstore.TenantStore
1718
logStore logstore.LogStore
1819
deliveryMQ *deliverymq.DeliveryMQ
1920
}
2021

2122
func NewRetryHandlers(
2223
logger *logging.Logger,
23-
entityStore models.EntityStore,
24+
tenantStore tenantstore.TenantStore,
2425
logStore logstore.LogStore,
2526
deliveryMQ *deliverymq.DeliveryMQ,
2627
) *RetryHandlers {
2728
return &RetryHandlers{
2829
logger: logger,
29-
entityStore: entityStore,
30+
tenantStore: tenantStore,
3031
logStore: logStore,
3132
deliveryMQ: deliveryMQ,
3233
}
@@ -58,7 +59,7 @@ func (h *RetryHandlers) RetryAttempt(c *gin.Context) {
5859
}
5960

6061
// 2. Check destination exists and is enabled
61-
destination, err := h.entityStore.RetrieveDestination(c.Request.Context(), tenant.ID, attemptRecord.Attempt.DestinationID)
62+
destination, err := h.tenantStore.RetrieveDestination(c.Request.Context(), tenant.ID, attemptRecord.Attempt.DestinationID)
6263
if err != nil {
6364
AbortWithError(c, http.StatusInternalServerError, NewErrInternalServer(err))
6465
return

internal/apirouter/retry_handlers_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ func TestRetryAttempt(t *testing.T) {
2323
// Create a tenant and destination
2424
tenantID := idgen.String()
2525
destinationID := idgen.Destination()
26-
require.NoError(t, result.entityStore.UpsertTenant(context.Background(), models.Tenant{
26+
require.NoError(t, result.tenantStore.UpsertTenant(context.Background(), models.Tenant{
2727
ID: tenantID,
2828
CreatedAt: time.Now(),
2929
}))
30-
require.NoError(t, result.entityStore.UpsertDestination(context.Background(), models.Destination{
30+
require.NoError(t, result.tenantStore.UpsertDestination(context.Background(), models.Destination{
3131
ID: destinationID,
3232
TenantID: tenantID,
3333
Type: "webhook",
@@ -117,7 +117,7 @@ func TestRetryAttempt(t *testing.T) {
117117
// Create a new destination that's disabled
118118
disabledDestinationID := idgen.Destination()
119119
disabledAt := time.Now()
120-
require.NoError(t, result.entityStore.UpsertDestination(context.Background(), models.Destination{
120+
require.NoError(t, result.tenantStore.UpsertDestination(context.Background(), models.Destination{
121121
ID: disabledDestinationID,
122122
TenantID: tenantID,
123123
Type: "webhook",

0 commit comments

Comments
 (0)