96 lines
2.9 KiB
Go
96 lines
2.9 KiB
Go
package apigen
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"git.yuntuo.hk/k2safe/go-omni/contract"
|
|
"mime"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func (p *parser) parseBody(r *Route) error {
|
|
props, err := p.metadata()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for key, value := range props {
|
|
switch key {
|
|
case "mode":
|
|
r.BodyMode = value
|
|
case "maxBytes":
|
|
r.MaxBodyBytes, err = strconv.ParseInt(value, 10, 64)
|
|
if err != nil || r.MaxBodyBytes <= 0 || r.MaxBodyBytes > 1<<40 {
|
|
return p.err("maxBytes must be 1..1099511627776 bytes")
|
|
}
|
|
default:
|
|
return fmt.Errorf("unknown body property %s", key)
|
|
}
|
|
}
|
|
if r.MaxBodyBytes == 0 {
|
|
r.MaxBodyBytes = contract.DefaultMaxBodyBytes
|
|
}
|
|
return nil
|
|
}
|
|
func validateRouteExtensions(r Route) error {
|
|
if r.BodyMode != "" && r.BodyMode != "json" && r.BodyMode != "raw" && r.BodyMode != "multipart" {
|
|
return errors.New("body mode must be json, raw or multipart")
|
|
}
|
|
if (r.BodyMode == "raw" || r.BodyMode == "multipart") && !r.Manual {
|
|
return errors.New("raw/multipart body requires @handlerMode manual")
|
|
}
|
|
if r.CustomBinding && (r.Manual || r.Request == "") {
|
|
return errors.New("@binding custom requires an automatic handler with a request DTO")
|
|
}
|
|
if r.MaxBodyBytes > 0 && (r.Method == "get" || r.Method == "head") {
|
|
return errors.New("GET/HEAD cannot declare a body limit")
|
|
}
|
|
if r.Produces != "" {
|
|
if !r.Manual {
|
|
return errors.New("@produces requires @handlerMode manual")
|
|
}
|
|
media, params, err := mime.ParseMediaType(r.Produces)
|
|
if err != nil || len(params) != 0 || media != r.Produces || strings.Contains(media, "*") {
|
|
return errors.New("produces requires a concrete media type without parameters")
|
|
}
|
|
if r.Response != "" || r.Status == 204 || r.Method == "head" {
|
|
return errors.New("@produces uses a manual response without a DTO, 204 or HEAD")
|
|
}
|
|
}
|
|
if r.Manual && r.Status == 204 && r.Response != "" {
|
|
return errors.New("204 cannot declare a response")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func decorateRequestOptions(op map[string]any, r Route) {
|
|
if r.Manual {
|
|
op["x-omni-handler-mode"] = "manual"
|
|
}
|
|
if r.CustomBinding {
|
|
op["x-omni-binding"] = "custom"
|
|
}
|
|
if r.MaxBodyBytes > 0 {
|
|
op["x-omni-max-body-bytes"] = r.MaxBodyBytes
|
|
}
|
|
if r.BodyMode == "raw" || r.BodyMode == "multipart" {
|
|
media := "application/octet-stream"
|
|
schema := map[string]any{"type": "string", "format": "binary"}
|
|
if r.BodyMode == "multipart" {
|
|
media = "multipart/form-data"
|
|
schema = map[string]any{"type": "object", "additionalProperties": true}
|
|
}
|
|
op["requestBody"] = map[string]any{"required": true, "content": map[string]any{media: map[string]any{"schema": schema}}}
|
|
}
|
|
if r.Produces != "" {
|
|
schema := map[string]any{"type": "string"}
|
|
if r.Produces == "application/octet-stream" {
|
|
schema["format"] = "binary"
|
|
}
|
|
if r.Produces == "application/json" {
|
|
schema = map[string]any{}
|
|
}
|
|
op["responses"].(map[string]any)[strconv.Itoa(r.Status)] = map[string]any{"description": "Success", "content": map[string]any{r.Produces: map[string]any{"schema": schema}}}
|
|
}
|
|
}
|