feat(tenant-manager): add CreateTenant client method#555
Conversation
Add a CreateTenant method to the Tenant Manager client for provisioning tenants via POST /v1/tenants. It mirrors the read methods' transport contract (circuit-breaker gating, size-limited response body, trace-context injection) and is idempotent by tenant identity: a repeat create returns the existing tenant with 200 OK, so both 200 and 201 map to success. A 5xx feeds the circuit breaker; a 4xx is a valid round-trip (409 maps to the typed ErrTenantConflict). The RBAC-gated write carries the service-account bearer token plus X-API-Key. The matcher standalone-SaaS control plane consumes this via the client to provision tenants. Observability is ported to the lib-observability/v2 packages the sibling client files already use (log/tracing/NewTrackingFromContext), replacing the matcher-era unversioned lib-observability imports; no new module dependency is added. X-Lerian-Ref: 0x1 Claude-Session: https://claude.ai/code/session_019CPR8BaUFL4TuPVos9DGds
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughAdds a Tenant Manager tenant-creation client with optional bearer authentication, typed request and response models, tracing, circuit-breaker handling, conflict errors, fixed response-body truncation, and HTTP behavior tests. ChangesTenant creation client
Sequence Diagram(s)sequenceDiagram
participant Caller
participant CreateTenant
participant TokenProvider
participant TenantManagerAPI
Caller->>CreateTenant: CreateTenant(request)
CreateTenant->>TokenProvider: Request bearer token
TokenProvider-->>CreateTenant: Token or error
CreateTenant->>TenantManagerAPI: POST /v1/tenants
TenantManagerAPI-->>CreateTenant: Tenant response or HTTP error
CreateTenant-->>Caller: Response or typed error
✨ Finishing Touches✨ Simplify code
Comment |
🔒 Security Scan Results —
|
| Stage | Status | Blocking? |
|---|---|---|
| Filesystem Scan | ✅ Clean | — |
| Docker Image Scan | ➖ Skipped | — |
| Docker Hub Health Score | ➖ Skipped | — |
| Pre-release Version Check | ✅ Clean | — |
Trivy
Filesystem Scan
✅ No vulnerabilities or secrets found.
Pre-release Version Check
✅ No unstable version pins found.
🔍 PR Validation Summary✅ PR Mergeable — no blocking failures
|
📊 Unit Test Coverage Report:
|
| Metric | Value |
|---|---|
| Overall Coverage | 87.3% ✅ PASS |
| Threshold | 80% |
Coverage by Package
| Package | Coverage |
|---|---|
github.com/LerianStudio/lib-commons/v6/commons/backoff |
91.1% |
github.com/LerianStudio/lib-commons/v6/commons/certificate |
88.8% |
github.com/LerianStudio/lib-commons/v6/commons/circuitbreaker |
86.8% |
github.com/LerianStudio/lib-commons/v6/commons/cron |
94.2% |
github.com/LerianStudio/lib-commons/v6/commons/crypto |
95.6% |
github.com/LerianStudio/lib-commons/v6/commons/dlq |
81.0% |
github.com/LerianStudio/lib-commons/v6/commons/errgroup |
86.1% |
github.com/LerianStudio/lib-commons/v6/commons/events |
100.0% |
github.com/LerianStudio/lib-commons/v6/commons/internal/nilcheck |
100.0% |
github.com/LerianStudio/lib-commons/v6/commons/jwt |
89.4% |
github.com/LerianStudio/lib-commons/v6/commons/license |
96.9% |
github.com/LerianStudio/lib-commons/v6/commons/mongo |
89.0% |
github.com/LerianStudio/lib-commons/v6/commons/net/http/idempotency |
87.9% |
github.com/LerianStudio/lib-commons/v6/commons/net/http/openapi |
98.4% |
github.com/LerianStudio/lib-commons/v6/commons/net/http/problem |
100.0% |
github.com/LerianStudio/lib-commons/v6/commons/net/http/ratelimit |
92.3% |
github.com/LerianStudio/lib-commons/v6/commons/net/http |
96.2% |
github.com/LerianStudio/lib-commons/v6/commons/outbox |
92.2% |
github.com/LerianStudio/lib-commons/v6/commons/pointers |
100.0% |
github.com/LerianStudio/lib-commons/v6/commons/postgres |
87.7% |
github.com/LerianStudio/lib-commons/v6/commons/rabbitmq |
89.2% |
github.com/LerianStudio/lib-commons/v6/commons/redis |
89.6% |
github.com/LerianStudio/lib-commons/v6/commons/safe |
99.6% |
github.com/LerianStudio/lib-commons/v6/commons/secretsmanager |
99.4% |
github.com/LerianStudio/lib-commons/v6/commons/security/ssrf |
95.9% |
github.com/LerianStudio/lib-commons/v6/commons/security |
100.0% |
github.com/LerianStudio/lib-commons/v6/commons/server |
87.3% |
github.com/LerianStudio/lib-commons/v6/commons/tenant-manager/cache |
97.9% |
github.com/LerianStudio/lib-commons/v6/commons/tenant-manager/client |
93.2% |
github.com/LerianStudio/lib-commons/v6/commons/tenant-manager/consumer |
87.9% |
github.com/LerianStudio/lib-commons/v6/commons/tenant-manager/core |
98.5% |
github.com/LerianStudio/lib-commons/v6/commons/tenant-manager/event |
96.5% |
github.com/LerianStudio/lib-commons/v6/commons/tenant-manager/internal/eviction |
100.0% |
github.com/LerianStudio/lib-commons/v6/commons/tenant-manager/log |
100.0% |
github.com/LerianStudio/lib-commons/v6/commons/tenant-manager/middleware |
93.8% |
github.com/LerianStudio/lib-commons/v6/commons/tenant-manager/mongo |
76.3% |
github.com/LerianStudio/lib-commons/v6/commons/tenant-manager/postgres |
84.9% |
github.com/LerianStudio/lib-commons/v6/commons/tenant-manager/rabbitmq |
83.3% |
github.com/LerianStudio/lib-commons/v6/commons/tenant-manager/redis |
95.8% |
github.com/LerianStudio/lib-commons/v6/commons/tenant-manager/s3 |
89.3% |
github.com/LerianStudio/lib-commons/v6/commons/tenant-manager/tenantcache |
98.6% |
github.com/LerianStudio/lib-commons/v6/commons/tenant-manager/valkey |
100.0% |
github.com/LerianStudio/lib-commons/v6/commons/transaction |
95.1% |
github.com/LerianStudio/lib-commons/v6/commons/webhook |
93.2% |
github.com/LerianStudio/lib-commons/v6/commons |
95.2% |
Generated by Go PR Analysis workflow
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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.
Inline comments:
In `@commons/tenant-manager/client/create_tenant.go`:
- Line 77: Update handleCreateTenantStatus to accept the request-scoped logger
created by observability.NewTrackingFromContext(ctx), and replace all three
c.logger.Log calls in its branches with that logger. Pass the tracking logger
from CreateTenant while preserving the existing status-handling behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: c718e737-fd81-4909-8f29-e0fa3af177a9
📒 Files selected for processing (5)
commons/tenant-manager/client/client.gocommons/tenant-manager/client/create_tenant.gocommons/tenant-manager/client/create_tenant_test.gocommons/tenant-manager/client/metadata.gocommons/tenant-manager/core/errors.go
Address PR #555 review + lint gate: - handleCreateTenantStatus now receives the request-scoped logger derived in CreateTenant (trace-correlated) instead of the static client logger (CodeRabbit review). - truncateBody drops the maxLen parameter (always 512, unparam); limit is a local const. X-Lerian-Ref: 0x1 Claude-Session: https://claude.ai/code/session_019CPR8BaUFL4TuPVos9DGds
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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.
Inline comments:
In `@commons/tenant-manager/client/coverage_boost_test.go`:
- Around line 183-185: The truncation tests use weak assertions that do not
verify the exact output. In commons/tenant-manager/client/coverage_boost_test.go
lines 183-185, assert the result equals the first 512 bytes of body followed by
"...(truncated)", removing the space from the suffix literal. Apply the same
exact 512-byte prefix and "...(truncated)" suffix assertion in
commons/tenant-manager/client/extra_test.go lines 217-225.
In `@commons/tenant-manager/client/create_tenant.go`:
- Around line 163-192: Update handleCreateTenantStatus so the business-rejection
handling currently in the default branch applies only to 4xx status codes. Add a
separate branch for unexpected non-4xx, non-5xx statuses such as 202 and 3xx,
preserving appropriate failure/error handling without resetting the circuit
breaker or classifying them as tenant rejections.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: b63e0d40-19c2-4d93-b40a-3a34071953dc
📒 Files selected for processing (5)
commons/tenant-manager/client/client.gocommons/tenant-manager/client/coverage_boost_test.gocommons/tenant-manager/client/create_tenant.gocommons/tenant-manager/client/extra_test.gocommons/tenant-manager/client/metadata.go
CodeRabbit round 2: - handleCreateTenantStatus: business-rejection branch now explicit 4xx; unexpected statuses (202, 3xx) log an error without touching circuit-breaker counters, mirroring handleGetTenantConfigStatus. New test proves a 202 does not reset preloaded breaker failures. - Truncation tests assert exact body[:512] + "...(truncated)" output. X-Lerian-Ref: 0x1 Claude-Session: https://claude.ai/code/session_019CPR8BaUFL4TuPVos9DGds
What
Adds a
CreateTenantmethod to the Tenant Manager client (commons/tenant-manager/client) for provisioning a tenant viaPOST /v1/tenants.200 OKon a repeat create, so the method treats both200and201as success and unmarshals the returned tenant record.maxResponseBodySize), and trace-context injection (InjectHTTPContext).TokenProvider(WithBearerTokenProvider) plus the always-requiredX-API-Key. When no provider is configured, noAuthorizationheader is sent.5xxis a service failure that feeds the circuit breaker; any4xxis a valid round-trip that resets the failure counter.409maps to the typedcore.ErrTenantConflict; other4xxsurface the truncated response body for diagnosis.Public API (
CreateTenant,CreateTenantRequest/CreateTenantResponse,TenantOwner,TokenProvider,WithBearerTokenProvider) is preserved for downstream consumers.Why
The matcher standalone-SaaS control plane provisions tenants through this client, so the write path needs a first-class
CreateTenantalongside the existing read methods (GetTenantConfig,GetActiveTenantsByService,GetTenantMetadata).Observability port note
The method was authored against matcher's module graph, which imports the unversioned
github.com/LerianStudio/lib-observability{,/log,/tracing}. lib-commons/v6instead depends ongithub.com/LerianStudio/lib-observability/v2(go.mod), which is exactly what the sibling client files (client.go,metadata.go) already use. This PR ports the three import paths to/v2— sameobservability.NewTrackingFromContext,libLog.*, andlibOpentelemetry.*surface as the siblings. No new module dependency is added.Tests
go test -tags=unit ./commons/tenant-manager/client/...— 121 pass (10CreateTenantcases: success, idempotent existing-tenant, bearer/no-bearer, bearer-provider error, 5xx breaker trip, malformed body, network error, 409 typed sentinel, generic 4xx body passthrough, 4xx not-breaker).go build ./commons/tenant-manager/...andgo vetclean.https://claude.ai/code/session_019CPR8BaUFL4TuPVos9DGds