Skip to content

Commit 1db86a9

Browse files
authored
chore: tenant data bulk delete (#77)
* chore: tenant data bulk delete * chore: address PR concerns * chore: Update go to latest version (fix CI problems) * commit: address pr comments * chore: change documentation
1 parent 6e7b278 commit 1db86a9

31 files changed

Lines changed: 1510 additions & 178 deletions

.github/workflows/api-contract-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
- name: Set up Go
5454
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
5555
with:
56-
go-version: '1.26.2'
56+
go-version: '1.26.3'
5757

5858
- name: Cache Go modules
5959
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5

.github/workflows/code-quality.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
id: setup-go
4040
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
4141
with:
42-
go-version: '1.26.2'
42+
go-version: '1.26.3'
4343

4444
- name: Cache Go modules
4545
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5

.github/workflows/migrations-validate.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
- name: Set up Go
2626
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
2727
with:
28-
go-version: '1.26.2'
28+
go-version: '1.26.3'
2929

3030
- name: Install goose
3131
run: go install github.com/pressly/goose/v3/cmd/goose@v3.27.1

.github/workflows/tests.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
- name: Set up Go
3232
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
3333
with:
34-
go-version: '1.26.2'
34+
go-version: '1.26.3'
3535

3636
- name: Cache Go modules
3737
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
@@ -85,7 +85,7 @@ jobs:
8585
- name: Set up Go
8686
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
8787
with:
88-
go-version: '1.26.2'
88+
go-version: '1.26.3'
8989

9090
- name: Cache Go modules
9191
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
@@ -148,7 +148,7 @@ jobs:
148148
- name: Set up Go
149149
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
150150
with:
151-
go-version: '1.26.2'
151+
go-version: '1.26.3'
152152

153153
- name: Cache Go modules
154154
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5

.golangci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ run:
99
timeout: 5m
1010
concurrency: 4
1111
modules-download-mode: readonly
12-
go: "1.26.2"
12+
go: "1.26.3"
1313
# Allow linting test files but suppress specific strict rules via exclusion below
1414
tests: true
1515

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Stage 1: Build
33
# =============================================================================
44
# TARGETOS/TARGETARCH are set by Docker Buildx for multi-platform builds (e.g. linux/arm64 on Mac M1).
5-
FROM golang:1.26.2-alpine AS builder
5+
FROM golang:1.26.3-alpine AS builder
66
ARG TARGETOS=linux
77
ARG TARGETARCH
88

Makefile

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ help:
2222
@echo " make mcp-smoke - Run the live MCP package smoke test (requires Hub env vars)"
2323
@echo " make test-all - Run all tests (unit + integration)"
2424
@echo " make tests-coverage - Run tests with coverage report"
25-
@echo " make check-coverage - Run tests and fail if coverage below COVERAGE_THRESHOLD (excludes cmd/api)"
25+
@echo " make check-coverage - Run tests and fail if coverage below COVERAGE_THRESHOLD"
2626
@echo " make init-db - Initialize database schema (run migrations with goose)"
2727
@echo " make migrate-status - Show migration status"
2828
@echo " make migrate-validate - Validate migration files (no DB)"
2929
@echo " make river-migrate - Run River job queue migrations (required for webhook delivery)"
30-
@echo " make fmt - Format code (golangci-lint run --fix)"
30+
@echo " make fmt - Format code"
3131
@echo " make lint - Run linter (includes format checks)"
3232
@echo " make lint-new - Run linter only on new code since base (default origin/main; for CI set LINT_BASE_REV to PR base SHA)"
3333
@echo " make deps - Install Go dependencies"
@@ -60,28 +60,28 @@ mcp-smoke:
6060
# Run unit tests (fast, no database required)
6161
test-unit:
6262
@echo "Running unit tests..."
63-
go test ./internal/... -v
63+
go test ./cmd/api ./internal/... -v
6464

6565
# Run all tests (unit + integration)
6666
test-all: test-unit tests
6767
@echo "All tests passed!"
6868

69-
# Run tests with coverage (unit + integration).
70-
# cmd/api (app.go, main.go) is excluded—coverage is for internal/, pkg/, and tests/.
69+
# Run tests with package-instrumented coverage (unit + integration).
70+
# Integration tests live in tests/, so coverpkg is required for those tests to count against app packages.
7171
tests-coverage:
7272
@echo "Running tests with coverage..."
73-
go test ./internal/... ./pkg/... ./tests/... -v -cover -coverprofile=coverage.out
73+
@(set -a && [ -f .env ] && . ./.env && set +a; go test ./cmd/api ./internal/... ./pkg/... ./tests/... -v -coverpkg=./cmd/api,./internal/...,./pkg/... -coverprofile=coverage.out)
7474
go tool cover -html=coverage.out -o coverage.html
7575
@echo "Coverage report generated: coverage.html"
7676

7777
# Minimum coverage threshold (percent). Fails if coverage falls below this.
7878
COVERAGE_THRESHOLD ?= 15
7979

8080
# Check coverage threshold (fail if below COVERAGE_THRESHOLD).
81-
# Excludes cmd/api (app.go, main.go) from coverage. Includes internal/, tests/, and pkg/.
81+
# Uses package-instrumented coverage so integration tests contribute to app package coverage.
8282
check-coverage:
8383
@echo "Running tests with coverage (threshold: $(COVERAGE_THRESHOLD)%)..."
84-
@(set -a && [ -f .env ] && . ./.env && set +a; go test ./internal/... ./pkg/... ./tests/... -coverprofile=coverage.out)
84+
@(set -a && [ -f .env ] && . ./.env && set +a; go test ./cmd/api ./internal/... ./pkg/... ./tests/... -coverpkg=./cmd/api,./internal/...,./pkg/... -coverprofile=coverage.out)
8585
@COV=$$(go tool cover -func=coverage.out | \tail -1 | awk '{gsub(/%/, ""); print $$3}') && \
8686
if [ -z "$$COV" ] || ! awk -v c="$$COV" -v t="$(COVERAGE_THRESHOLD)" 'BEGIN { exit (c+0 >= t) ? 0 : 1 }'; then \
8787
echo ""; \
@@ -292,11 +292,11 @@ install-tools:
292292
go install github.com/riverqueue/river/cmd/river@$(RIVER_VERSION)
293293
@echo "Tools installed (golangci-lint $(GOLANGCI_LINT_VERSION), govulncheck $(GOVULNCHECK_VERSION), goose $(GOOSE_VERSION), river $(RIVER_VERSION))"
294294

295-
# Format code (golangci-lint applies gofumpt + gci from .golangci.yml formatters)
295+
# Format code (golangci-lint applies gofumpt + gci from .golangci.yml formatters).
296296
fmt:
297297
@echo "Formatting code..."
298298
@test -x $(GOLANGCI_LINT) || { echo "Error: golangci-lint not found. Install with: make install-tools"; exit 1; }
299-
$(GOLANGCI_LINT) run --fix ./...
299+
$(GOLANGCI_LINT) fmt ./...
300300
@echo "Code formatted"
301301

302302
# Lint code

cmd/api/app.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ func NewApp(cfg *config.Config, db *pgxpool.Pool) (*App, error) {
229229

230230
feedbackRecordsRepo := repository.NewFeedbackRecordsRepository(db)
231231
embeddingsRepo := repository.NewEmbeddingsRepository(db)
232+
tenantDataRepo := repository.NewTenantDataRepository(db)
232233
embeddingProviderName, embeddingModel := embeddingProviderAndModel(cfg)
233234
embeddingModelForDB := embeddingModel
234235

@@ -317,6 +318,8 @@ func NewApp(cfg *config.Config, db *pgxpool.Pool) (*App, error) {
317318

318319
webhooksService := service.NewWebhooksService(webhooksRepo, messageManager, cfg.Webhook.MaxCount, cfg.Webhook.URLBlacklist)
319320
webhooksHandler := handlers.NewWebhooksHandler(webhooksService)
321+
tenantDataService := service.NewTenantDataService(tenantDataRepo)
322+
tenantDataHandler := handlers.NewTenantDataHandler(tenantDataService)
320323

321324
feedbackRecordsHandler := handlers.NewFeedbackRecordsHandler(feedbackRecordsService)
322325
healthHandler := handlers.NewHealthHandler()
@@ -329,7 +332,7 @@ func NewApp(cfg *config.Config, db *pgxpool.Pool) (*App, error) {
329332
}
330333

331334
server := newHTTPServer(
332-
cfg, healthHandler, openapiHandler, feedbackRecordsHandler, webhooksHandler, searchHandler,
335+
cfg, healthHandler, openapiHandler, feedbackRecordsHandler, webhooksHandler, tenantDataHandler, searchHandler,
333336
meterProvider, tracerProvider,
334337
)
335338

@@ -353,6 +356,7 @@ func newHTTPServer(
353356
openapi *handlers.OpenAPIHandler,
354357
feedback *handlers.FeedbackRecordsHandler,
355358
webhooks *handlers.WebhooksHandler,
359+
tenantData *handlers.TenantDataHandler,
356360
search *handlers.SearchHandler,
357361
meterProvider *sdkmetric.MeterProvider,
358362
tracerProvider *sdktrace.TracerProvider,
@@ -368,13 +372,14 @@ func newHTTPServer(
368372
protected.HandleFunc("GET /v1/feedback-records/{id}", feedback.Get)
369373
protected.HandleFunc("PATCH /v1/feedback-records/{id}", feedback.Update)
370374
protected.HandleFunc("DELETE /v1/feedback-records/{id}", feedback.Delete)
371-
protected.HandleFunc("DELETE /v1/feedback-records", feedback.BulkDelete)
375+
protected.HandleFunc("DELETE /v1/feedback-records", feedback.DeleteByUser)
372376

373377
protected.HandleFunc("POST /v1/webhooks", webhooks.Create)
374378
protected.HandleFunc("GET /v1/webhooks", webhooks.List)
375379
protected.HandleFunc("GET /v1/webhooks/{id}", webhooks.Get)
376380
protected.HandleFunc("PATCH /v1/webhooks/{id}", webhooks.Update)
377381
protected.HandleFunc("DELETE /v1/webhooks/{id}", webhooks.Delete)
382+
protected.HandleFunc("DELETE /v1/tenants/{tenant_id}/data", tenantData.Delete)
378383

379384
// Search endpoints are always registered; when embeddings are disabled, the handler returns 503.
380385
protected.HandleFunc("POST /v1/feedback-records/search/semantic", search.SemanticSearch)

cmd/api/app_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,24 @@ func TestNewHTTPServerKeepsV1RoutesProtected(t *testing.T) {
240240
}
241241
}
242242

243+
func TestNewHTTPServerKeepsTenantDataRoutesProtected(t *testing.T) {
244+
server := newTestHTTPServer(t)
245+
246+
recorder := httptest.NewRecorder()
247+
request := httptest.NewRequestWithContext(
248+
context.Background(),
249+
http.MethodDelete,
250+
"/v1/tenants/test-tenant-id/data",
251+
http.NoBody,
252+
)
253+
254+
server.Handler.ServeHTTP(recorder, request)
255+
256+
if recorder.Code != http.StatusUnauthorized {
257+
t.Fatalf("DELETE /v1/tenants/{tenant_id}/data status = %d, want %d", recorder.Code, http.StatusUnauthorized)
258+
}
259+
}
260+
243261
func newTestHTTPServer(t *testing.T) *http.Server {
244262
t.Helper()
245263

@@ -262,6 +280,7 @@ func newTestHTTPServerWithPublicBaseURL(t *testing.T, publicBaseURL string) *htt
262280
newTestOpenAPIHandler(t, publicBaseURL),
263281
handlers.NewFeedbackRecordsHandler(nil),
264282
handlers.NewWebhooksHandler(nil),
283+
handlers.NewTenantDataHandler(nil),
265284
handlers.NewSearchHandler(nil),
266285
nil,
267286
nil,

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/formbricks/hub
22

3-
go 1.26.2
3+
go 1.26.3
44

55
require (
66
github.com/go-playground/form/v4 v4.3.0

0 commit comments

Comments
 (0)