Files
member-console/internal/integration/registration.go
T
cgalo5758 b0072d8971 Consolidate domain tables into core schema
Squash the pre-production migration history into fresh core, fedwiki,
and stripe baselines and reduce the canonical source list to those
three streams.

Update sqlc configs, generated queries, raw SQL, tests, and docs while
keeping provider tables schema-qualified.

BREAKING: existing local database volumes must be wiped because goose
version history restarts from the new baselines.
2026-07-05 20:10:23 -05:00

235 lines
7.7 KiB
Go

package integration
import (
"context"
"database/sql"
"fmt"
"regexp"
"strings"
)
var slugPattern = regexp.MustCompile(`^[a-z0-9]+$`)
// statePattern is a hygiene format for lifecycle state names. It is deliberately
// permissive (allows underscores, e.g. a future `quota_locked`) because the state
// vocabulary is per-provider, not a closed global enum.
var statePattern = regexp.MustCompile(`^[a-z][a-z0-9_]*$`)
var validProviderKinds = map[ProviderKind]bool{
KindPayment: true,
KindProvisioning: true,
KindNotification: true,
KindTax: true,
}
var validOperations = map[Operation]bool{
OpCreate: true,
OpSetStatus: true,
OpDelete: true,
OpList: true,
OpDescribe: true,
}
// validateManifest enforces the parts of the provider contract that are
// checkable from the manifest alone: slug hygiene, a known kind, known verbs,
// and slug-prefix ownership of every declared resource key.
func validateManifest(m Manifest) error {
if !slugPattern.MatchString(m.Slug) {
return fmt.Errorf("provider slug %q must match ^[a-z0-9]+$ (no underscores)", m.Slug)
}
if !validProviderKinds[m.Kind] {
return fmt.Errorf("provider %q: unknown provider_kind %q", m.Slug, m.Kind)
}
declaresSetStatus := false
for _, op := range m.Operations {
if !validOperations[op] {
return fmt.Errorf("provider %q: unknown operation %q", m.Slug, op)
}
if op == OpSetStatus {
declaresSetStatus = true
}
}
// State-set agreement: a provider declaring set_status must declare the
// implicit `active` baseline plus at least one non-active state; a provider
// without set_status must declare no states. (See provider-registry spec.)
hasActive, hasNonActive := false, false
for _, s := range m.States {
if !statePattern.MatchString(string(s)) {
return fmt.Errorf("provider %q: state %q must match ^[a-z][a-z0-9_]*$", m.Slug, s)
}
if s == StateActive {
hasActive = true
} else {
hasNonActive = true
}
}
if declaresSetStatus {
if !hasActive {
return fmt.Errorf("provider %q: declares set_status but does not declare the `active` state", m.Slug)
}
if !hasNonActive {
return fmt.Errorf("provider %q: declares set_status but declares no non-active state", m.Slug)
}
} else if len(m.States) > 0 {
return fmt.Errorf("provider %q: declares lifecycle states but does not declare the set_status operation", m.Slug)
}
prefix := m.Slug + "_"
for _, key := range m.ResourceKeys {
if !strings.HasPrefix(key, prefix) {
return fmt.Errorf("provider %q: owned resource key %q must be prefixed %q", m.Slug, key, prefix)
}
}
return nil
}
// leadingToken returns the substring before the first underscore (the whole
// string if there is none).
func leadingToken(key string) string {
if i := strings.IndexByte(key, '_'); i >= 0 {
return key[:i]
}
return key
}
// checkSlugNesting enforces that the provider-slug namespace and the platform
// (unprefixed) resource-key namespace do not collide: no provider slug may be
// the leading underscore-delimited token of a platform key, which would let one
// provider's "<slug>_" prefix swallow a platform-owned key. (Slug-vs-slug
// nesting is already impossible because slugs carry no underscore.)
func checkSlugNesting(slugs map[string]bool, platformKeys []string) error {
for _, key := range platformKeys {
tok := leadingToken(key)
if slugs[tok] {
return fmt.Errorf("provider slug %q nests under platform resource key %q", tok, key)
}
}
return nil
}
// RegisterProviders reconciles the provider registry from code at boot,
// parallel to how migrations are assembled from MigrationSources. It validates
// every manifest, enforces slug hygiene against existing platform resource
// keys, then upserts provider + operation rows and stamps
// core.resource_keys.provider for each owned key — all in one
// transaction. It is idempotent: re-running with the same sources only
// refreshes capability fields and reconciles the declared operation set.
// Operational state (provider status) is never clobbered.
//
// NOTE: the contract's "every declared operation resolves to an implemented
// activity" check is only partially realizable here. The manifest is the
// declaration and verb validity is enforced by validateManifest; binding each
// declared verb to its registered Temporal activity lands with the
// outbox→activity dispatch layer (M8c and later).
func RegisterProviders(ctx context.Context, db *sql.DB, sources []ProviderSource) error {
manifests := make([]Manifest, 0, len(sources))
slugs := make(map[string]bool, len(sources))
for _, src := range sources {
m := src.ProviderManifest()
if err := validateManifest(m); err != nil {
return err
}
if slugs[m.Slug] {
return fmt.Errorf("duplicate provider slug %q", m.Slug)
}
slugs[m.Slug] = true
manifests = append(manifests, m)
}
owned := make(map[string]bool)
for _, m := range manifests {
for _, k := range m.ResourceKeys {
owned[k] = true
}
}
q := New(db)
platformKeys, err := q.ListPlatformResourceKeys(ctx)
if err != nil {
return fmt.Errorf("listing platform resource keys: %w", err)
}
// A provider's own keys may still be NULL-provider at this point (freshly
// renamed into place, or about to be stamped below), so they surface as
// platform-owned. Exclude declared-owned keys: only genuinely platform-owned
// keys participate in the slug-nesting check.
platform := platformKeys[:0]
for _, k := range platformKeys {
if !owned[k] {
platform = append(platform, k)
}
}
if err := checkSlugNesting(slugs, platform); err != nil {
return err
}
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer func() { _ = tx.Rollback() }()
qtx := q.WithTx(tx)
for _, m := range manifests {
if _, err := qtx.UpsertProvider(ctx, UpsertProviderParams{
Slug: m.Slug,
ProviderKind: string(m.Kind),
DisplayName: m.DisplayName,
OperatorSurfacePath: sql.NullString{String: m.OperatorSurfacePath, Valid: m.OperatorSurfacePath != ""},
}); err != nil {
return fmt.Errorf("upsert provider %q: %w", m.Slug, err)
}
ops := make([]string, 0, len(m.Operations))
for _, op := range m.Operations {
ops = append(ops, string(op))
if err := qtx.AddProviderOperation(ctx, AddProviderOperationParams{
Provider: m.Slug,
Operation: string(op),
}); err != nil {
return fmt.Errorf("add operation %q for %q: %w", op, m.Slug, err)
}
}
// Reconcile: drop any operation this provider no longer declares.
if err := qtx.DeleteProviderOperationsNotIn(ctx, DeleteProviderOperationsNotInParams{
Provider: m.Slug,
Operations: ops,
}); err != nil {
return fmt.Errorf("reconcile operations for %q: %w", m.Slug, err)
}
states := make([]string, 0, len(m.States))
for _, s := range m.States {
states = append(states, string(s))
if err := qtx.AddProviderState(ctx, AddProviderStateParams{
Provider: m.Slug,
State: string(s),
}); err != nil {
return fmt.Errorf("add state %q for %q: %w", s, m.Slug, err)
}
}
// Reconcile: drop any state this provider no longer declares (an empty
// declared set removes all rows — correct for a provider that dropped
// set_status).
if err := qtx.DeleteProviderStatesNotIn(ctx, DeleteProviderStatesNotInParams{
Provider: m.Slug,
States: states,
}); err != nil {
return fmt.Errorf("reconcile states for %q: %w", m.Slug, err)
}
for _, key := range m.ResourceKeys {
n, err := qtx.SetResourceKeyProvider(ctx, SetResourceKeyProviderParams{
ResourceKey: key,
Provider: sql.NullString{String: m.Slug, Valid: true},
})
if err != nil {
return fmt.Errorf("stamp resource key %q for %q: %w", key, m.Slug, err)
}
if n == 0 {
return fmt.Errorf("provider %q declares resource key %q, but no such row exists in core.resource_keys", m.Slug, key)
}
}
}
return tx.Commit()
}