Skip to content

chore: no tenant_id on similar search#82

Open
xernobyl wants to merge 3 commits into
mainfrom
chore--no-tenand_id-on-similar-search
Open

chore: no tenant_id on similar search#82
xernobyl wants to merge 3 commits into
mainfrom
chore--no-tenand_id-on-similar-search

Conversation

@xernobyl
Copy link
Copy Markdown
Contributor

@xernobyl xernobyl commented Jun 5, 2026

What does this PR do?

Updates the similar feedback endpoint so callers no longer need to pass tenant_id when looking up records similar to a source feedback record.

Hub now derives the tenant boundary from the source feedback record itself, then uses that exact stored tenant ID to scope the nearest-neighbor embedding search. This removes a redundant public parameter while preserving tenant isolation internally.

Changes include:

  • Remove tenant_id from GET /v1/feedback-records/{id}/similar
  • Add repository support to fetch a source embedding together with its feedback record tenant
  • Keep nearest-neighbor search scoped to the derived tenant
  • Preserve the exact stored tenant ID when scoping search
  • Update handler/service tests and OpenAPI docs

Fixes #(issue)

How should this be tested?

  • go test ./internal/api/handlers ./internal/service ./internal/repository ./cmd/api ./cmd/worker
  • /Users/xernobyl/go/bin/golangci-lint run ./...

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Repository Guidelines
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand bits
  • Ran make build
  • Ran make tests (integration tests in tests/)
  • Ran make fmt and make lint; no new warnings
  • Removed debug prints / temporary logging
  • Merged the latest changes from main onto my branch with git pull origin main
  • If database schema changed: added migration in migrations/ with goose annotations and ran make migrate-validate

Appreciated

  • If API changed: added or updated OpenAPI spec and ran contract tests (make tests or API contract workflow)
  • If API behavior changed: added request/response examples or Swagger UI screenshots to this PR
  • Updated docs in docs/ if changes were necessary
  • Ran make tests-coverage for meaningful logic changes

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 5, 2026

✱ Stainless preview builds

This PR will update the hub SDKs with the following commit message.

chore: no tenant_id on similar search

Edit this comment to update it. It will appear in the SDK's changelogs.

hub-openapi studio · code · diff

Your SDK build had at least one "note" diagnostic, but this did not represent a regression.
generate ✅

hub-typescript studio · code · diff

Your SDK build had at least one "note" diagnostic, but this did not represent a regression.
generate ✅build ✅ (prev: build ⏭️) → lint ✅ (prev: lint ⏭️) → test ✅

npm install https://pkg.stainless.com/s/hub-typescript/387a8114a3d07bb32d78e1725c3dfd447f9eff36/dist.tar.gz

This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push.
If you push custom code to the preview branch, re-run this workflow to update the comment.
Last updated: 2026-06-05 14:55:55 UTC

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 5, 2026

Review Change Stack

Walkthrough

This PR refactors the similarity search endpoint to derive tenant context from the source feedback record instead of requiring an explicit tenantID parameter. The repository layer adds a method to fetch both the embedding and tenant in one query; the service layer removes the tenantID parameter and uses a helper to resolve both values from the source record; the HTTP handler updates to match the new service contract and stop validating a tenant_id query parameter; and the OpenAPI specification is updated to reflect the removal of that parameter and clarify tenant derivation.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'chore: no tenant_id on similar search' clearly describes the main change: removing the tenant_id parameter from the similar search endpoint.
Description check ✅ Passed The PR description covers the main objectives, changes made, and testing instructions. However, several required checklist items are unchecked (make build, make tests, database migrations, git pull from main).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
internal/api/handlers/search_handler.go (1)

164-181: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle service.ErrMissingTenantID explicitly.

The service layer can return ErrMissingTenantID when the source feedback record has an empty tenant_id (per search_service.go:223-225). This error is not handled in the current error chain (lines 166-176), so it falls through to the generic error handler at line 178, likely resulting in a 500 response.

This is a data-integrity issue (feedback record exists but has invalid/empty tenant), and should return a more appropriate status:

  • 404 Not Found with a message like "Source feedback record not found or has no tenant"
  • OR 400 Bad Request if you want to surface the data inconsistency to the caller
🛡️ Suggested fix to handle missing tenant explicitly
 	res, err := h.service.SimilarFeedback(r.Context(), id, limit, minScore, cursor)
 	if err != nil {
+		if errors.Is(err, service.ErrMissingTenantID) {
+			response.RespondNotFound(w, r, "Source feedback record has no tenant or was not found")
+
+			return
+		}
+
 		if errors.Is(err, service.ErrEmbeddingNotFound) {
 			response.RespondNotFound(w, r, "Feedback record has no embedding for the current model")

Based on learnings: Treat tenant_id as a security boundary. When a source record exists but has no tenant, explicitly handle it rather than allowing a generic error that could mask tenant-boundary violations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/api/handlers/search_handler.go` around lines 164 - 181, The error
from h.service.SimilarFeedback can be ErrMissingTenantID and must be handled
explicitly; add a branch checking errors.Is(err, service.ErrMissingTenantID)
(alongside the existing ErrEmbeddingNotFound and ErrInvalidCursor checks) and
call response.RespondNotFound(w, r, "Source feedback record not found or has no
tenant") (or RespondInvalidParams if you prefer 400) before falling through to
the generic response.RespondError; reference the service.ErrMissingTenantID
symbol and the handler invocation h.service.SimilarFeedback to locate where to
insert this check.
internal/api/handlers/search_handler_test.go (1)

162-207: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add test coverage for empty tenant scenario.

The tests verify the happy path (tenant derived successfully) and the embedding-not-found case, but don't cover the scenario where the source feedback record exists but has an empty tenant_id. The service can return service.ErrMissingTenantID in this case (per search_service.go:223-225), which should be tested.

🧪 Suggested test case for empty tenant
t.Run("source record with empty tenant returns appropriate error", func(t *testing.T) {
	id := uuid.MustParse("018e1234-5678-9abc-def0-123456789abc")
	mock := &mockSearchService{
		similarFunc: func(_ context.Context, fid uuid.UUID, _ int, _ float64, _ string) (service.SearchResult, error) {
			assert.Equal(t, id, fid)
			return service.SearchResult{}, service.ErrMissingTenantID
		},
	}
	handler := NewSearchHandler(mock)
	req := httptest.NewRequestWithContext(context.Background(), http.MethodGet, similarURL, nil)
	req.SetPathValue("id", id.String())
	
	rec := httptest.NewRecorder()
	
	handler.SimilarFeedback(rec, req)
	
	// Should return 404 or 400 depending on handler implementation
	// Currently falls through to generic error handler (likely 500)
	assert.Contains(t, []int{http.StatusNotFound, http.StatusBadRequest}, rec.Code)
})

Based on learnings: For any tenant-scoping change, include verification at the boundary where the leak could happen. This test would verify proper handling when the tenant boundary is absent from the source record.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/api/handlers/search_handler_test.go` around lines 162 - 207, Add a
test in internal/api/handlers/search_handler_test.go that exercises the
SimilarFeedback handler when the service returns service.ErrMissingTenantID:
create a mockSearchService.similarFunc that asserts the incoming id (use the
same uuid as other tests), returns (service.SearchResult{},
service.ErrMissingTenantID), call NewSearchHandler(mock).SimilarFeedback with a
request having req.SetPathValue("id", id.String()), record the response, and
assert the response code is an appropriate error (e.g., 400 or 404 depending on
handler behavior); this covers the "source record with empty tenant" branch
referenced by service.ErrMissingTenantID.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@internal/api/handlers/search_handler_test.go`:
- Around line 162-207: Add a test in
internal/api/handlers/search_handler_test.go that exercises the SimilarFeedback
handler when the service returns service.ErrMissingTenantID: create a
mockSearchService.similarFunc that asserts the incoming id (use the same uuid as
other tests), returns (service.SearchResult{}, service.ErrMissingTenantID), call
NewSearchHandler(mock).SimilarFeedback with a request having
req.SetPathValue("id", id.String()), record the response, and assert the
response code is an appropriate error (e.g., 400 or 404 depending on handler
behavior); this covers the "source record with empty tenant" branch referenced
by service.ErrMissingTenantID.

In `@internal/api/handlers/search_handler.go`:
- Around line 164-181: The error from h.service.SimilarFeedback can be
ErrMissingTenantID and must be handled explicitly; add a branch checking
errors.Is(err, service.ErrMissingTenantID) (alongside the existing
ErrEmbeddingNotFound and ErrInvalidCursor checks) and call
response.RespondNotFound(w, r, "Source feedback record not found or has no
tenant") (or RespondInvalidParams if you prefer 400) before falling through to
the generic response.RespondError; reference the service.ErrMissingTenantID
symbol and the handler invocation h.service.SimilarFeedback to locate where to
insert this check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5f1a54ec-8669-4b55-b7b0-eb0775c58273

📥 Commits

Reviewing files that changed from the base of the PR and between eb0cae1 and c1d3834.

📒 Files selected for processing (6)
  • internal/api/handlers/search_handler.go
  • internal/api/handlers/search_handler_test.go
  • internal/repository/embeddings_repository.go
  • internal/service/search_service.go
  • internal/service/search_service_test.go
  • openapi.yaml

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant