c8e71a0c34
Three packages (api, reconciler, webhook) each carried a private 30-line toPluginWorkload() copy that had drifted — only the api version logged malformed public_faces JSON; the others swallowed it. Hoist the single implementation to plugin.WorkloadFromStore() (convert.go); store is already a plugin dependency so no new import edge or cycle forms. Likewise the dockerfile and static sources each had a private removeContainerByName() that disagreed (remove-all vs stop-at-first). Docker enforces unique container names, so the two were equivalent for every reachable state; converge on plugin.RemoveContainerByName() (container.go, stop-at-first) with a note on why remove-all was moot. Callers migrated; old copies removed. Adds convert_test.go pinning the field-by-field contract and JSON edge cases.
53 lines
2.0 KiB
Go
53 lines
2.0 KiB
Go
package plugin
|
|
|
|
import (
|
|
"encoding/json"
|
|
"log/slog"
|
|
|
|
"github.com/alexei/tinyforge/internal/store"
|
|
)
|
|
|
|
// WorkloadFromStore converts a persisted store.Workload row into the value
|
|
// shape that Source / Trigger plugins consume. It is the single converter
|
|
// shared by every caller (api, reconciler, webhook) — previously each kept
|
|
// its own byte-identical copy, which drifted (only the api copy logged bad
|
|
// PublicFaces JSON; the others swallowed it).
|
|
//
|
|
// Living in the plugin package is safe: plugin already imports store (Deps
|
|
// holds a *store.Store), so this adds no new edge to the dependency graph
|
|
// and store does not import plugin.
|
|
//
|
|
// SourceConfig / TriggerConfig are passed through as raw JSON; the matching
|
|
// plugin decodes them with plugin.SourceConfigOf[T] / TriggerConfigOf[T].
|
|
// PublicFaces is decoded eagerly because every consumer needs the parsed
|
|
// slice (proxy registration, UI, validation); invalid JSON is logged and
|
|
// treated as empty rather than failing the conversion.
|
|
func WorkloadFromStore(w store.Workload) Workload {
|
|
var faces []PublicFace
|
|
if w.PublicFaces != "" {
|
|
if err := json.Unmarshal([]byte(w.PublicFaces), &faces); err != nil {
|
|
slog.Warn("workload: invalid public_faces JSON, treating as empty",
|
|
"workload", w.ID, "error", err)
|
|
faces = nil
|
|
}
|
|
}
|
|
return Workload{
|
|
ID: w.ID,
|
|
Name: w.Name,
|
|
GroupID: w.AppID,
|
|
ParentWorkloadID: w.ParentWorkloadID,
|
|
SourceKind: w.SourceKind,
|
|
SourceConfig: json.RawMessage(w.SourceConfig),
|
|
TriggerKind: w.TriggerKind,
|
|
TriggerConfig: json.RawMessage(w.TriggerConfig),
|
|
PublicFaces: faces,
|
|
NotificationURL: w.NotificationURL,
|
|
NotificationSecret: w.NotificationSecret,
|
|
WebhookSecret: w.WebhookSecret,
|
|
WebhookSigningSecret: w.WebhookSigningSecret,
|
|
WebhookRequireSignature: w.WebhookRequireSignature,
|
|
CreatedAt: w.CreatedAt,
|
|
UpdatedAt: w.UpdatedAt,
|
|
}
|
|
}
|