diff --git a/cmd/server/main.go b/cmd/server/main.go index a6fa626..ee71971 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -23,7 +23,7 @@ import ( "github.com/gitrus/digikeeper-log/internal/httpapi" apicmd "github.com/gitrus/digikeeper-log/internal/httpapi/command" apiqry "github.com/gitrus/digikeeper-log/internal/httpapi/query" - apireg "github.com/gitrus/digikeeper-log/internal/httpapi/registry" + apisreg "github.com/gitrus/digikeeper-log/internal/httpapi/schemaregistry" "github.com/gitrus/digikeeper-log/internal/infrastructure/candidatestore" store "github.com/gitrus/digikeeper-log/internal/infrastructure/commandstore" "github.com/gitrus/digikeeper-log/internal/infrastructure/index" @@ -82,8 +82,12 @@ func run() error { // Services cmdSvc := command.NewService(logStore, srcRepo, logger) - candidateSvc := domainCandidate.NewService(candidateStore, logStore, logger) - compactionSvc := domainCompaction.NewService(logStore, candidateStore, idx, logger) + candidateSvc := domainCandidate.NewService( + candidateStore, logStore, logger, + ) + compactionSvc := domainCompaction.NewService( + logStore, candidateStore, idx, logger, + ) qrySvc := query.NewService(qryStore, qryStore, logger) // Handlers @@ -91,7 +95,7 @@ func run() error { candidateHandler := apicmd.NewCandidateHandler(candidateSvc) compactionHandler := apicmd.NewCompactionHandler(compactionSvc) qryHandler := apiqry.NewHandler(qrySvc, srcRepo.ResolveName) - regHandler, err := apireg.NewHandler() + sregHandler, err := apisreg.NewHandler() if err != nil { return fmt.Errorf("init registry: %w", err) } @@ -149,14 +153,14 @@ func run() error { Path: "/v1/registry", Summary: "List all entry type schemas", DefaultStatus: http.StatusOK, - }, regHandler.ListSchemas) + }, sregHandler.ListSchemas) huma.Register(api, huma.Operation{ OperationID: "get-schema", Method: http.MethodGet, Path: "/v1/registry/{type}", Summary: "Get schema for an entry type", DefaultStatus: http.StatusOK, - }, regHandler.GetSchema) + }, sregHandler.GetSchema) mux.HandleFunc("GET /healthz", healthz.Handle) mux.Handle("/debug/", http.DefaultServeMux) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index dec17e4..2e2ad63 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -35,7 +35,7 @@ Dependencies flow inward. Interfaces defined at the usage-model-level. ## Handler Segregation Handlers under `internal/httpapi/` mirror the CQS split so that read and write paths evolve independently — different validation rules, status codes, and future middleware (e.g. rate limiting writes only). -`httpapi/command` owns mutation concerns; `httpapi/query` owns read concerns; `httpapi/registry` is stateless schema discovery with no domain coupling. +`httpapi/command` owns mutation concerns; `httpapi/query` owns read concerns; `httpapi/schemaregistry` is stateless schema discovery with no domain coupling. Shared utilities (`response.go`, `errors.go`, `middleware.go`) are kept at the `httpapi/` root to avoid duplication without blurring the command/query boundary. ## Infrastructure Split @@ -61,7 +61,7 @@ The API follows the main guidelines of [JSON:API](https://jsonapi.org/) specific Before making API design decisions, consult the spec and its addendums first. ## See Also -- [Registry Handlers](REGISTRY_HANDLERS.md) +- [Schema Registry Handlers](SCHEMA_REGISTRY_HANDLERS.md) - [Candidate Compaction](CANDIDATE_COMPACTION.md) ## Trade-offs diff --git a/docs/REGISTRY_HANDLERS.md b/docs/SCHEMA_REGISTRY_HANDLERS.md similarity index 73% rename from docs/REGISTRY_HANDLERS.md rename to docs/SCHEMA_REGISTRY_HANDLERS.md index ef3df6e..c1b258c 100644 --- a/docs/REGISTRY_HANDLERS.md +++ b/docs/SCHEMA_REGISTRY_HANDLERS.md @@ -1,20 +1,20 @@ -# Registry Handlers +# Schema Registry Handlers ## Description -Registry handlers expose supported log entry schemas over HTTP. -They live in `internal/httpapi/registry` and are wired from `cmd/server`. +Schema registry handlers expose supported log entry schemas over HTTP. +They live in `internal/httpapi/schemaregistry` and are wired from `cmd/server`. Current endpoints: - `GET /v1/registry` returns all known entry schemas. - `GET /v1/registry/{type}` returns one schema by entry type. -Schemas are JSON files in `internal/httpapi/registry/schemas`. +Schemas are JSON files in `internal/httpapi/schemaregistry/schemas`. The handler embeds them with `go:embed`, loads them at startup, and keeps each schema as `json.RawMessage`. The filename without `.json` becomes the public entry type name. Example: -- `schemas/note.json` becomes registry type `note`. +- `schemas/note.json` becomes schema registry type `note`. ## Why It Exists @@ -24,7 +24,7 @@ It cna be used by agentic client as well. ## Boundaries -The registry is read-only application metadata, not user data. +The schema registry is read-only application metadata, not user data. Schema changes should go through code review and deployment. The handler stays in `internal/httpapi` because it has no business workflow or mutable storage. diff --git a/internal/httpapi/registry/handler.go b/internal/httpapi/schemaregistry/handler.go similarity index 91% rename from internal/httpapi/registry/handler.go rename to internal/httpapi/schemaregistry/handler.go index 251eb54..81ac276 100644 --- a/internal/httpapi/registry/handler.go +++ b/internal/httpapi/schemaregistry/handler.go @@ -1,4 +1,4 @@ -package registry +package schemaregistry import ( "context" @@ -27,7 +27,7 @@ type Handler struct { func NewHandler() (*Handler, error) { entries, err := schemasFS.ReadDir("schemas") if err != nil { - return nil, fmt.Errorf("registry: read embedded schemas: %w", err) + return nil, fmt.Errorf("schemaregistry: read embedded schemas: %w", err) } schemas := make(map[string]json.RawMessage, len(entries)) @@ -38,7 +38,7 @@ func NewHandler() (*Handler, error) { } data, err := schemasFS.ReadFile("schemas/" + e.Name()) if err != nil { - return nil, fmt.Errorf("registry: read %s: %w", e.Name(), err) + return nil, fmt.Errorf("schemaregistry: read %s: %w", e.Name(), err) } name := strings.TrimSuffix(e.Name(), ".json") schemas[name] = json.RawMessage(data) diff --git a/internal/httpapi/registry/handler_test.go b/internal/httpapi/schemaregistry/handler_test.go similarity index 92% rename from internal/httpapi/registry/handler_test.go rename to internal/httpapi/schemaregistry/handler_test.go index f21bada..0894c42 100644 --- a/internal/httpapi/registry/handler_test.go +++ b/internal/httpapi/schemaregistry/handler_test.go @@ -1,4 +1,4 @@ -package registry_test +package schemaregistry_test import ( "encoding/json" @@ -12,13 +12,13 @@ import ( "github.com/stretchr/testify/require" "github.com/gitrus/digikeeper-log/internal/httpapi" - apireg "github.com/gitrus/digikeeper-log/internal/httpapi/registry" + apisreg "github.com/gitrus/digikeeper-log/internal/httpapi/schemaregistry" ) func setupRegistryServer(t *testing.T) *httptest.Server { t.Helper() - h, err := apireg.NewHandler() + h, err := apisreg.NewHandler() require.NoError(t, err) mux := http.NewServeMux() diff --git a/internal/httpapi/registry/schemas/note.json b/internal/httpapi/schemaregistry/schemas/note.json similarity index 100% rename from internal/httpapi/registry/schemas/note.json rename to internal/httpapi/schemaregistry/schemas/note.json diff --git a/tests/integration_test.go b/tests/integration_test.go index d041437..75f9642 100644 --- a/tests/integration_test.go +++ b/tests/integration_test.go @@ -22,7 +22,7 @@ import ( "github.com/gitrus/digikeeper-log/internal/httpapi" apicmd "github.com/gitrus/digikeeper-log/internal/httpapi/command" apiqry "github.com/gitrus/digikeeper-log/internal/httpapi/query" - apireg "github.com/gitrus/digikeeper-log/internal/httpapi/registry" + apisreg "github.com/gitrus/digikeeper-log/internal/httpapi/schemaregistry" "github.com/gitrus/digikeeper-log/internal/infrastructure/candidatestore" store "github.com/gitrus/digikeeper-log/internal/infrastructure/commandstore" "github.com/gitrus/digikeeper-log/internal/infrastructure/index" @@ -131,8 +131,8 @@ func setupTestServer(t *testing.T) *httptest.Server { candidateHandler := apicmd.NewCandidateHandler(candidateSvc) compactionHandler := apicmd.NewCompactionHandler(compactionSvc) qryHandler := apiqry.NewHandler(qrySvc, srcRepo.ResolveName) - regHandler, err := apireg.NewHandler() - require.NoError(t, err, "init registry") + sregHandler, err := apisreg.NewHandler() + require.NoError(t, err, "init schema registry") mux := http.NewServeMux() api := humago.New(mux, httpapi.NewHumaConfig("Digikeeper Log", "1.0.0")) @@ -187,14 +187,14 @@ func setupTestServer(t *testing.T) *httptest.Server { Path: "/v1/registry", Summary: "List all entry type schemas", DefaultStatus: http.StatusOK, - }, regHandler.ListSchemas) + }, sregHandler.ListSchemas) huma.Register(api, huma.Operation{ OperationID: "get-schema", Method: http.MethodGet, Path: "/v1/registry/{type}", Summary: "Get schema for an entry type", DefaultStatus: http.StatusOK, - }, regHandler.GetSchema) + }, sregHandler.GetSchema) sloghttp.RequestIDHeaderKey = "X-Request-ID" handler := sloghttp.NewWithConfig(logger, sloghttp.Config{ @@ -498,7 +498,7 @@ func TestAppendGeneratesRequestIDWhenMissing(t *testing.T) { assert.NotEmpty(t, got.Data.Attributes.RequestID) } -func TestRegistryListSchemas(t *testing.T) { +func TestSchemaRegistryListSchemas(t *testing.T) { srv := setupTestServer(t) resp := getURL(t, srv.URL+"/v1/registry") @@ -517,7 +517,7 @@ func TestRegistryListSchemas(t *testing.T) { assert.Equal(t, "note", got.Schemas[0].Type) } -func TestRegistryGetSchema(t *testing.T) { +func TestSchemaRegistryGetSchema(t *testing.T) { srv := setupTestServer(t) resp := getURL(t, srv.URL+"/v1/registry/note")