87 lines
3.6 KiB
Markdown
87 lines
3.6 KiB
Markdown
|
|
# 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.
|