feat(firebase): initial stable release v0.9.0
Firebase App component with launcher lifecycle and health check integration.
What's included:
- Config with ProjectID (FIREBASE_PROJECT_ID env var); credentials via ADC
- Provider interface exposing native *firebase.App directly
- Component interface: launcher.Component + health.Checkable + Provider
- New(logger, cfg) constructor for lifecycle registration via lc.Append
- Health check via GetUser("health-probe-non-existent") + auth.IsUserNotFound at LevelCritical
- No-op OnStop (Firebase Admin SDK has no Close method)
Tested-via: todo-api POC integration
Reviewed-against: docs/adr/
This commit is contained in:
86
CLAUDE.md
Normal file
86
CLAUDE.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# firebase
|
||||
|
||||
Firebase App component with launcher lifecycle and health check integration.
|
||||
|
||||
## Purpose
|
||||
|
||||
Manages the lifecycle of a `firebase.google.com/go/v4` App: validates config at init time,
|
||||
exposes the native `*fb.App` to consumers, and performs health checks by probing the Auth
|
||||
service. Does not wrap or abstract any Firebase service beyond App construction.
|
||||
|
||||
## Tier & Dependencies
|
||||
|
||||
**Tier 3 (infrastructure)** — depends on:
|
||||
- `code.nochebuena.dev/go/health` (Tier 2)
|
||||
- `code.nochebuena.dev/go/launcher` (Tier 2)
|
||||
- `code.nochebuena.dev/go/logz` (Tier 1)
|
||||
- `firebase.google.com/go/v4` (Firebase Admin SDK, wraps gRPC/HTTP internally)
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
- **Health check via known-nonexistent UID**: `HealthCheck` calls `GetUser` with the UID
|
||||
`"health-probe-non-existent"` and uses `auth.IsUserNotFound(err)` to distinguish a
|
||||
healthy "not found" response from a real connectivity failure. Avoids string matching
|
||||
on error messages. See ADR-001.
|
||||
- **SDK lifecycle management**: `OnInit` calls `fb.NewApp`. `OnStop` is a no-op log
|
||||
because the SDK has no `Close` method. See ADR-002.
|
||||
- **App exposed directly**: `App() *fb.App` gives consumers the full SDK entry point.
|
||||
They call `app.Auth(ctx)` or `app.Firestore(ctx)` themselves. The module does not
|
||||
cache or pre-construct service-specific clients.
|
||||
- **Health priority is Critical**: `Priority()` returns `health.LevelCritical`. A Firebase
|
||||
Auth outage is treated as a critical failure.
|
||||
- **Duck-typed Logger**: The internal `logger` field is typed as `logz.Logger`, the shared
|
||||
interface from the `logz` module (ADR-001 global pattern).
|
||||
|
||||
## Patterns
|
||||
|
||||
**Lifecycle registration:**
|
||||
```go
|
||||
fb := firebase.New(logger, cfg)
|
||||
lc.Append(fb) // registers OnInit / OnStart / OnStop
|
||||
```
|
||||
|
||||
**Obtaining service clients from consumers:**
|
||||
```go
|
||||
type authService struct {
|
||||
provider firebase.Provider
|
||||
}
|
||||
|
||||
func (s *authService) VerifyToken(ctx context.Context, token string) (*auth.Token, error) {
|
||||
authClient, err := s.provider.App().Auth(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return authClient.VerifyIDToken(ctx, token)
|
||||
}
|
||||
```
|
||||
|
||||
**Health check registration:**
|
||||
```go
|
||||
health.Register(fbComponent) // satisfies health.Checkable via Name()/Priority()/HealthCheck()
|
||||
```
|
||||
|
||||
## What to Avoid
|
||||
|
||||
- Do not call `App()` before `OnInit` has run — it returns `nil`. Guard against this in
|
||||
tests or when constructing components manually outside a lifecycle manager.
|
||||
- Do not add service-specific methods (e.g. `AuthClient()`, `FirestoreClient()`) to this
|
||||
module. Callers obtain them from `App()` directly, which keeps the module free of service
|
||||
proliferation.
|
||||
- Do not increase the health check polling frequency significantly. Each `HealthCheck` call
|
||||
makes a live API call to Firebase Auth. Poll at most every 30 seconds.
|
||||
- Do not rely on `OnStop` for graceful in-flight request drain. The SDK has no explicit
|
||||
close; in-flight Firebase calls at shutdown must be handled by request context deadlines
|
||||
in the caller.
|
||||
|
||||
## Testing Notes
|
||||
|
||||
- Unit tests do not require a real Firebase project. They test structural invariants:
|
||||
`Name()`, `Priority()`, nil-safety of `OnStop`, and error on empty `ProjectID`.
|
||||
- `TestComponent_OnInit_MissingProjectID` verifies that `OnInit` returns an error before
|
||||
attempting SDK initialisation when `ProjectID` is empty.
|
||||
- `TestComponent_App_ReturnsNilBeforeInit` confirms `App()` is nil before `OnInit` is called.
|
||||
- Integration tests (real token verification, real health check) require Firebase credentials
|
||||
and belong outside this module.
|
||||
- `compliance_test.go` (package `firebase_test`) asserts `New(...)` satisfies `Component`
|
||||
at compile time.
|
||||
Reference in New Issue
Block a user