package openapi import ( "encoding/json" "git.yuntuo.hk/k2safe/go-omni/response" "sort" "strings" ) // EnvelopeWith describes the same configurable envelope used at runtime. data // describes the business value; it is never embedded under a fixed field name. // A nil data schema describes an error response and selects ErrorBody if set. func EnvelopeWith(data *Schema, cfg response.Config) (*Schema, error) { f, err := response.New(cfg) if err != nil { return nil, err } c := f.Config() body := c.Body if data == nil && c.ErrorBody != nil { body = c.ErrorBody } if body != nil { return templateSchema(body, data, c), nil } s := &Schema{Type: "object", Properties: map[string]*Schema{}} if c.CodeField != "-" { s.Properties[c.CodeField] = codeSchema(c.CodeType) s.Required = append(s.Required, c.CodeField) } if c.MessageField != "-" { s.Properties[c.MessageField] = &Schema{Type: "string"} s.Required = append(s.Required, c.MessageField) } if c.RequestIDField != "-" { s.Properties[c.RequestIDField] = &Schema{Type: "string"} } if c.DataField != "-" && (data != nil || c.DataMode == "always") { if data == nil { data = &Schema{Type: "object", Nullable: true, Enum: []any{nil}} } s.Properties[c.DataField] = data s.Required = append(s.Required, c.DataField) } return s, nil } func templateSchema(value any, data *Schema, cfg response.Config) *Schema { switch response.Slot(value) { case "code": return codeSchema(cfg.CodeType) case "message", "requestId": return &Schema{Type: "string"} case "ok": return &Schema{Type: "boolean"} case "status": return &Schema{Type: "integer"} case "data": if data != nil { return data } return &Schema{Type: "object", Nullable: true, Enum: []any{nil}} } switch v := value.(type) { case map[string]any: s := &Schema{Type: "object", Properties: map[string]*Schema{}} keys := make([]string, 0, len(v)) for key := range v { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { item := v[key] if response.Slot(item) == "data" && data == nil && cfg.DataMode != "always" { continue } s.Properties[key] = templateSchema(item, data, cfg) if response.Slot(item) != "requestId" { s.Required = append(s.Required, key) } } return s case []any: items := &Schema{} for _, item := range v { items.AnyOf = append(items.AnyOf, templateSchema(item, data, cfg)) } size := len(v) return &Schema{Type: "array", Items: items, MinItems: &size, MaxItems: &size} case string: if strings.HasPrefix(v, "$${") { v = v[1:] } return &Schema{Type: "string", Enum: []any{v}} case bool: return &Schema{Type: "boolean", Enum: []any{v}} case nil: return &Schema{Type: "object", Nullable: true, Enum: []any{nil}} default: typeName := "number" encoded, _ := json.Marshal(v) if _, err := json.Number(string(encoded)).Int64(); err == nil { typeName = "integer" } return &Schema{Type: typeName, Enum: []any{v}} } } func codeSchema(kind string) *Schema { if kind == "int32" || kind == "int64" { return &Schema{Type: "integer", Format: kind} } return &Schema{Type: kind} }