feat: complete contract-driven business development and MySQL integration

This commit is contained in:
2026-09-24 21:56:04 +08:00
parent f9afb8f8dd
commit f5478cdc76
108 changed files with 6557 additions and 550 deletions

23
errors/export.go Normal file
View File

@@ -0,0 +1,23 @@
package apierrors
import (
"encoding/json"
"errors"
"io"
)
// ExportCatalog writes normalized, public-only error metadata. Invoke this
// explicitly from project tooling, using the same Catalog function registered
// at runtime. The API CLI reads the artifact; it never executes project code.
func ExportCatalog(w io.Writer, c Catalog) error {
if w == nil {
return errors.New("errors: catalog export writer required")
}
registry, err := NewRegistry(c)
if err != nil {
return err
}
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
return encoder.Encode(registry.Catalog())
}

40
errors/export_test.go Normal file
View File

@@ -0,0 +1,40 @@
package apierrors
import (
"bytes"
"errors"
"strings"
"testing"
)
type failedExport struct{ err error }
func (w failedExport) Write([]byte) (int, error) { return 0, w.err }
func TestCatalogExport(t *testing.T) {
catalog := Catalog{DefaultStatus: 200, Definitions: map[string]Definition{"INVALID": {Code: int64(9007199254740993), Message: "invalid"}}}
var out bytes.Buffer
if err := ExportCatalog(&out, catalog); err != nil {
t.Fatal(err)
}
if !strings.Contains(out.String(), "9007199254740993") {
t.Fatal(out.String())
}
var decoded Catalog
if err := DecodeCatalogJSON(out.Bytes(), &decoded); err != nil {
t.Fatal(err)
}
registry, err := NewRegistry(decoded)
if err != nil {
t.Fatal(err)
}
if registry.Resolve(ByCode("INVALID")).Status != 200 {
t.Fatal("status lost")
}
cause := errors.New("write failed")
if err = ExportCatalog(failedExport{cause}, catalog); !errors.Is(err, cause) {
t.Fatal("writer failure lost", err)
}
if err = ExportCatalog(nil, catalog); err == nil {
t.Fatal("nil writer accepted")
}
}