feat: release generic composition mapping and middleware factories

This commit is contained in:
2026-09-25 12:43:34 +08:00
parent 8493f9dae9
commit 8b588a24cd
40 changed files with 2203 additions and 62 deletions

View File

@@ -19,10 +19,11 @@ import (
)
const frameworkModule = "git.yuntuo.hk/k2safe/go-omni"
const minimumVersion = "v0.16.0-preview"
const minimumVersion = "v0.17.0-preview"
const generatedHeader = "// Code generated by go-omni api. DO NOT EDIT.\n"
type Options struct {
generation GeneratorConfig
lockHeld bool
documentCatalogs map[string][]byte
Database, Storage string
@@ -97,6 +98,11 @@ func generate(o Options, c *Compiled, starter bool) (Output, error) {
return Output{}, err
}
o.Dir = directory
genConfig, hasGenConfig, err := readGeneratorConfig(directory)
if err != nil {
return Output{}, err
}
o.generation = genConfig
if err = checkRuntimeExtension(directory); err != nil {
return Output{}, err
}
@@ -192,6 +198,10 @@ func generate(o Options, c *Compiled, starter bool) (Output, error) {
for name, data := range base {
seeds[name] = data
}
if !hasGenConfig {
raw, _ := json.MarshalIndent(genConfig, "", " ")
seeds[generatorConfigFile] = append(raw, '\n')
}
// The API source remains developer-owned. A relocated project regenerates
// from its own api/ tree, including all imports.
entry := "api/" + c.Spec.Entry
@@ -242,6 +252,18 @@ func generate(o Options, c *Compiled, starter bool) (Output, error) {
}
}
}
if err := renderMappings(o, generated, seeds); err != nil {
return Output{}, err
}
currentConfig, stillExists, err := readGeneratorConfig(directory)
if err != nil {
return Output{}, err
}
beforeConfig, _ := json.Marshal(genConfig)
afterConfig, _ := json.Marshal(currentConfig)
if hasGenConfig != stillExists || string(beforeConfig) != string(afterConfig) {
return Output{}, errors.New("omni.gen.json changed during generation; retry")
}
out, err := apply(directory, o.Module, entry, generated, seeds, o.Check, o.DryRun, o.profile(), snapshotContract(c.Spec), o.lockHeld)
expectedVersion := minimumVersion
if info, ok := debug.ReadBuildInfo(); ok && info.Main.Path == frameworkModule && info.Main.Sum != "" && semver.IsValid(info.Main.Version) {
@@ -602,7 +624,9 @@ func renderProject(o Options, c *Compiled, starter bool) (map[string][]byte, map
}
put(generated, "internal/handler/"+group+"/handlersGen.go", generatedHeader+text)
put(seeds, "internal/logic/"+group+"/logic.go", fmt.Sprintf("package %s\nimport %q\ntype Logic struct {deps *svc.ServiceContext}\nfunc New(deps *svc.ServiceContext)*Logic{return &Logic{deps:deps}}\n", packageName, o.Module+"/internal/svc"))
put(seeds, "internal/repository/"+group+"/repository.go", "// Package "+packageName+" owns persistence contracts for this business group.\n// Define repository interfaces and implementations here; HTTP DTOs do not\n// determine a database schema. This file is never overwritten.\npackage "+packageName+"\n")
if o.generation.Repository == "group" {
put(seeds, "internal/repository/"+group+"/repository.go", "// Package "+packageName+" owns persistence contracts for this business group.\n// Define repository interfaces and implementations here; HTTP DTOs do not\n// determine a database schema. This file is never overwritten.\npackage "+packageName+"\n")
}
}
put(generated, "internal/handler/routesGen.go", generatedHeader+"package handler\n"+imports+")\n"+routes+"return nil\n}\n")
registry := "package middleware\nimport " + strconv.Quote(frameworkModule+"/httpx") + "\nfunc Defaults()map[string]httpx.Middleware{return map[string]httpx.Middleware{\n"
@@ -611,6 +635,9 @@ func renderProject(o Options, c *Compiled, starter bool) (map[string][]byte, map
put(seeds, "internal/middleware/"+lower(m)+".go", fmt.Sprintf("package middleware\nimport(%q; apierrors %q)\n// %s is a developer-owned hook. Implement it before enabling this route.\nfunc %s(next httpx.Handler)httpx.Handler{return func(c *httpx.Context)error {return apierrors.New(501,\"MIDDLEWARE_NOT_IMPLEMENTED\",\"middleware is not implemented\")}}\n", frameworkModule+"/httpx", frameworkModule+"/errors", upper(m), upper(m)))
}
put(generated, "internal/middleware/registryGen.go", generatedHeader+registry+"}}\n")
if o.generation.Middleware == "factories" {
renderMiddlewareFactories(middlewareNames, generated, seeds)
}
for name, source := range baseTemplates {
content := strings.ReplaceAll(source, "MODULE", o.Module)
content = strings.ReplaceAll(content, "LOGFORMAT", o.LogFormat)
@@ -630,6 +657,16 @@ func renderProject(o Options, c *Compiled, starter bool) (map[string][]byte, map
return nil, nil, err
}
for name, data := range infra {
if o.generation.Middleware == "factories" && name == "internal/svc/runtimeGen.go" {
formatted, err := format.Source(data)
if err != nil {
return nil, nil, err
}
source := string(formatted)
source = strings.Replace(source, "Middlewares: middleware.Defaults(),", "", 1)
source = strings.Replace(source, "return c, nil", "if len(c.Middlewares) != 0 { return nil, errors.New(\"factory middleware is registered automatically; remove manual Middlewares assignments from Init\") }\nc.Middlewares, err = middleware.New(c)\nif err != nil { return nil, err }\nreturn c, nil", 1)
data = []byte(source)
}
if strings.HasSuffix(name, "Gen.go") {
generated[name] = append([]byte(generatedHeader), data...)
} else {