Skip to content

Commit

Permalink
feat(openapi): 添加 Operation.PathID 和 Operation.Desc
Browse files Browse the repository at this point in the history
  • Loading branch information
caixw committed Nov 22, 2024
1 parent 0646882 commit a29fac7
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 13 deletions.
8 changes: 5 additions & 3 deletions openapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,10 @@ func (req *Request) addComponents(c *components) {
req.Body.addComponents(c)
}

for _, s := range req.Content {
s.addComponents(c)
if len(req.Content) > 0 {
for _, s := range req.Content {
s.addComponents(c)
}
}
}

Expand Down Expand Up @@ -348,7 +350,7 @@ func (req *Request) buildRenderer(p *message.Printer, d *Document) *requestRende
return &requestRenderer{
Content: content,
Required: !req.Ignorable,
Description: req.Description.LocaleString(p),
Description: sprint(p, req.Description),
}
}

Expand Down
3 changes: 3 additions & 0 deletions openapi/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ const (
FormatFloat = "float"
FormatDouble = "double"
FormatPassword = "password"
FormatDate = "date"
FormatTime = "time"
FormatDateTime = "date-time"

SecuritySchemeTypeHTTP = "http"
SecuritySchemeTypeAPIKey = "apiKey"
Expand Down
45 changes: 38 additions & 7 deletions openapi/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ func (o *Operation) Tag(tag ...string) *Operation {
return o
}

func (o *Operation) Desc(summary, description web.LocaleStringer) *Operation {
o.Summary = summary
o.Description = description
return o
}

func (o *Operation) Server(url string, desc web.LocaleStringer, vars ...*ServerVariable) *Operation {
s := &Server{
URL: url,
Expand Down Expand Up @@ -72,6 +78,13 @@ func (o *Operation) Path(name, typ string, desc web.LocaleStringer, f func(*Para
return o
}

// PathID 指定类型为大于 0 的路径参数
func (o *Operation) PathID(name string, desc web.LocaleStringer) *Operation {
return o.Path(name, TypeInteger, desc, func(p *Parameter) {
p.Schema.Minimum = 1
})
}

func (o *Operation) PathRef(ref string) *Operation {
o.Paths = append(o.Paths, &Parameter{Ref: &Ref{Ref: ref}, Required: true})
return o
Expand All @@ -88,16 +101,21 @@ func (o *Operation) QueryRef(ref string) *Operation {
return o
}

var timeType = reflect.TypeFor[time.Time]()

// QueryObject 从参数 o 中获取相应的查询参数
//
// 对于 o 的要求与 [web.Context.QueryObject] 是相同的。
// 对于 obj 的要求与 [web.Context.QueryObject] 是相同的。
// f 是对每个字段的修改,可以为空,其原型为
//
// func(p *Parameter)
//
// 可通过 p.Name 确定的参数名称
func (m *Operation) QueryObject(o any, f func(*Parameter)) *Operation {
t := reflect.TypeOf(o)
func (o *Operation) QueryObject(obj any, f func(*Parameter)) *Operation {
return o.queryObject(reflect.TypeOf(obj), f)
}

func (o *Operation) queryObject(t reflect.Type, f func(*Parameter)) *Operation {
for t.Kind() == reflect.Pointer {
t = t.Elem()
}
Expand All @@ -108,6 +126,12 @@ func (m *Operation) QueryObject(o any, f func(*Parameter)) *Operation {

for i := 0; i < t.NumField(); i++ {
field := t.Field(i)

if field.Anonymous {
o.QueryObject(field.Type, f)
continue
}

if !field.IsExported() {
continue
}
Expand All @@ -131,7 +155,7 @@ func (m *Operation) QueryObject(o any, f func(*Parameter)) *Operation {
if err := p.valid(true); err != nil {
panic(err)
}
m.Queries = append(m.Queries, p)
o.Queries = append(o.Queries, p)
}
switch field.Type.Kind() {
case reflect.String:
Expand All @@ -150,14 +174,21 @@ func (m *Operation) QueryObject(o any, f func(*Parameter)) *Operation {
p.Schema = &Schema{Type: TypeInteger, Minimum: 0}
q(p)
case reflect.Array, reflect.Slice:
p.Schema = &Schema{Type: TypeArray, Items: schemaFromType(m.d, reflect.TypeOf(field.Type.Elem()), false, "", nil)}
p.Schema = &Schema{Type: TypeArray, Items: schemaFromType(o.d, reflect.TypeOf(field.Type.Elem()), false, "", nil)}
q(p)
case reflect.Struct:
if field.Type == timeType {
p.Schema = &Schema{Type: TypeString, Format: FormatDateTime}
q(p)
} else {
panic(fmt.Sprintf("查询参数不支持复杂的类型 %v:%v", field.Type.Kind(), field.Name))
}
default:
panic(fmt.Sprintf("查询参数不支持复杂的类型 %v", field.Type.Kind()))
panic(fmt.Sprintf("查询参数不支持复杂的类型 %v:%v", field.Type.Kind(), field.Name))
}
}

return m
return o
}

// Header 添加报头
Expand Down
2 changes: 1 addition & 1 deletion openapi/middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func TestDocument_API(t *testing.T) {
Path("p1", TypeInteger, web.Phrase("lang"), nil).
Body(&object{}, true, web.Phrase("lang"), nil).
Response("200", 5, web.Phrase("desc"), nil).
Description = web.Phrase("lang")
Desc(nil, web.Phrase("lang"))
})
m.Middleware(nil, http.MethodGet, "/path/{p1}/abc", "")

Expand Down
2 changes: 1 addition & 1 deletion openapi/openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ type documentQuery struct {
// - html 通过将 accept 报头设置为 [html.Mimetype] 返回 HTML 格式的数据。
// 需要通过 [WithHTML] 进行配置,可参考 [github.com/issue9/web/mimetype/html];
//
// NOTE: Handler 支持的输出格式限定在以上几种,但是最终是否能正常输出以上几种格式,
// NOTE: 支持的输出格式限定在以上几种,但是最终是否能正常输出以上几种格式,
// 还需要由 [web.Server] 是否配置相应的解码方式。
//
// 该路由接受 tag 查询参数,在未指定参数的情况下,表示返回所有接口,
Expand Down
5 changes: 4 additions & 1 deletion openapi/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ func WithOptions(enable bool) Option {
// assets 资源基地址,apidoc 的浏览工具一般都是在一个 html 文件中引入一些依赖文件,
// assets 就是这些依赖文件的基地址,可是指向一个 CDN 的地址,比如 swagger 的
// https://unpkg.com/[email protected]
// favicon 图标,请使用 icon 或是 png 这种大部分浏览器都支持的图标格式,可以为空;
// favicon 图标,请使用 icon 或是 png 这种大部分浏览器都支持的图标格式,
// 可以为空。尽量提供比较大的图标,可能会被用在除 favicon 之外的地方;
//
// NOTE: [github.com/issue9/webuse/openapi] 下实现了部分简单的模板。
func WithHTML(tpl, assets, favicon string) Option {
return func(d *Document) {
d.templateName = tpl
Expand Down

0 comments on commit a29fac7

Please sign in to comment.