docs: add comprehensive client API reference
This commit is contained in:
10
scripts/api-docgen/generate.ps1
Normal file
10
scripts/api-docgen/generate.ps1
Normal file
@@ -0,0 +1,10 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
$env:GOWORK = "off"
|
||||
$env:GOLANG_PROTOBUF_REGISTRATION_CONFLICT = "warn"
|
||||
try {
|
||||
go run .
|
||||
if ($LASTEXITCODE -ne 0) { throw "API documentation generation failed" }
|
||||
} finally {
|
||||
Remove-Item Env:GOWORK -ErrorAction SilentlyContinue
|
||||
Remove-Item Env:GOLANG_PROTOBUF_REGISTRATION_CONFLICT -ErrorAction SilentlyContinue
|
||||
}
|
||||
7
scripts/api-docgen/generate.sh
Executable file
7
scripts/api-docgen/generate.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
export GOWORK=off
|
||||
export GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn
|
||||
go run .
|
||||
50
scripts/api-docgen/go.mod
Normal file
50
scripts/api-docgen/go.mod
Normal file
@@ -0,0 +1,50 @@
|
||||
module bsm/full/tools/api-docgen
|
||||
|
||||
go 1.26.5
|
||||
|
||||
require (
|
||||
bsm/full/module/base/ads v0.0.0
|
||||
bsm/full/module/base/cloud v0.0.0
|
||||
bsm/full/module/base/cms v0.0.0
|
||||
bsm/full/module/base/feedback v0.0.0
|
||||
bsm/full/module/base/initial v0.0.0
|
||||
bsm/full/module/base/passport v0.0.0
|
||||
bsm/full/module/base/sender v0.0.0
|
||||
bsm/full/module/ec/address v0.0.0
|
||||
bsm/full/module/ec/mall v0.0.0
|
||||
bsm/full/module/ec/market v0.0.0
|
||||
bsm/full/module/ec/order v0.0.0
|
||||
bsm/full/module/finance/wallet v0.0.0
|
||||
bsm/full/module/social/feed v0.0.0
|
||||
bsm/full/module/social/group v0.0.0
|
||||
bsm/full/module/social/relation v0.0.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 // indirect
|
||||
golang.org/x/net v0.57.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
golang.org/x/text v0.40.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260807164820-c8921c73eeea // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260807164820-c8921c73eeea // indirect
|
||||
google.golang.org/grpc v1.83.0 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
bsm/full/module/base/ads => ../../module/base/ads
|
||||
bsm/full/module/base/cloud => ../../module/base/cloud
|
||||
bsm/full/module/base/cms => ../../module/base/cms
|
||||
bsm/full/module/base/feedback => ../../module/base/feedback
|
||||
bsm/full/module/base/initial => ../../module/base/initial
|
||||
bsm/full/module/base/passport => ../../module/base/passport
|
||||
bsm/full/module/base/sender => ../../module/base/sender
|
||||
bsm/full/module/ec/address => ../../module/ec/address
|
||||
bsm/full/module/ec/mall => ../../module/ec/mall
|
||||
bsm/full/module/ec/market => ../../module/ec/market
|
||||
bsm/full/module/ec/order => ../../module/ec/order
|
||||
bsm/full/module/finance/wallet => ../../module/finance/wallet
|
||||
bsm/full/module/social/feed => ../../module/social/feed
|
||||
bsm/full/module/social/group => ../../module/social/group
|
||||
bsm/full/module/social/relation => ../../module/social/relation
|
||||
)
|
||||
42
scripts/api-docgen/go.sum
Normal file
42
scripts/api-docgen/go.sum
Normal file
@@ -0,0 +1,42 @@
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
|
||||
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 h1:/Tnpcb2E0Pz/tN9s3bfEY2Q8ePCEX9iuS+cneUwncnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0/go.mod h1:zOBXOsUaBSjKgmH4OGzV1esUpR3oUSCPYVd2cUBjKYY=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
|
||||
go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
|
||||
go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
|
||||
go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
|
||||
go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw=
|
||||
go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
|
||||
go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
|
||||
go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
|
||||
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260807164820-c8921c73eeea h1:Jifw/kjs/r3B0uszvls/m3c3tmZs2YHGM9C+rvxP9gY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260807164820-c8921c73eeea/go.mod h1:K/+WGbmBY7aNW1HDw1fJnKYo10i0DkAX6pows00dLig=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260807164820-c8921c73eeea h1:kVhQEPTpKQahD5+JSBTfBB19wcgQTTjAIn45MBqnyHk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260807164820-c8921c73eeea/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ=
|
||||
google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
301
scripts/api-docgen/main.go
Normal file
301
scripts/api-docgen/main.go
Normal file
@@ -0,0 +1,301 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
_ "bsm/full/module/base/ads/pb"
|
||||
_ "bsm/full/module/base/cloud/pb"
|
||||
_ "bsm/full/module/base/cms/pb"
|
||||
_ "bsm/full/module/base/feedback/pb"
|
||||
_ "bsm/full/module/base/initial/pb"
|
||||
_ "bsm/full/module/base/passport/pb"
|
||||
_ "bsm/full/module/base/sender/pb"
|
||||
_ "bsm/full/module/ec/address/pb"
|
||||
_ "bsm/full/module/ec/mall/pb"
|
||||
_ "bsm/full/module/ec/market/pb"
|
||||
_ "bsm/full/module/ec/order/pb"
|
||||
_ "bsm/full/module/finance/wallet/pb"
|
||||
_ "bsm/full/module/social/feed/pb"
|
||||
_ "bsm/full/module/social/group/pb"
|
||||
_ "bsm/full/module/social/relation/pb"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
)
|
||||
|
||||
type moduleInfo struct {
|
||||
Order int
|
||||
Name string
|
||||
Package protoreflect.FullName
|
||||
Description string
|
||||
}
|
||||
|
||||
var modules = []moduleInfo{
|
||||
{1, "ads", "ads", "广告获取与投放内容读取。"},
|
||||
{2, "cloud", "cloud", "云端相册、书签、网盘、笔记、私密数据、分享和空间管理。"},
|
||||
{3, "cms", "cms", "站点、页面、文章、栏目和标签内容管理。"},
|
||||
{4, "feedback", "feedback", "用户反馈提交与查询。"},
|
||||
{5, "initial", "initial", "客户端初始化、基础数据和版本更新检查。"},
|
||||
{6, "passport", "passport", "账号注册、登录、验证、找回密码和账户资料管理。"},
|
||||
{7, "sender", "sender", "邮件、短信发送及验证码校验。"},
|
||||
{8, "address", "address", "用户地址库管理。"},
|
||||
{9, "mall", "mall", "商城广告、类目、运费、公告、商品、员工和店铺管理。"},
|
||||
{10, "market", "market", "市场代理商、供应商及经营数据管理。"},
|
||||
{11, "order", "order", "购物车、优惠券、订单汇总及商家订单管理。"},
|
||||
{12, "wallet", "wallet", "钱包、支付方式、支付宝与微信支付。"},
|
||||
{13, "feed", "feed", "动态发布、标签、时间线和动态设置。"},
|
||||
{14, "group", "group", "群组及成员管理。"},
|
||||
{15, "relation", "relation", "关注、好友和关系匹配。"},
|
||||
}
|
||||
|
||||
func main() {
|
||||
root, err := filepath.Abs(filepath.Join("..", ".."))
|
||||
must(err)
|
||||
outDir := filepath.Join(root, "wiki", "api")
|
||||
must(os.MkdirAll(outDir, 0o755))
|
||||
|
||||
for _, module := range modules {
|
||||
services := servicesFor(module.Package)
|
||||
content := renderModule(module, services)
|
||||
name := fmt.Sprintf("%02d-%s.md", module.Order, module.Name)
|
||||
must(os.WriteFile(filepath.Join(outDir, name), content, 0o644))
|
||||
}
|
||||
}
|
||||
|
||||
func servicesFor(pkg protoreflect.FullName) []protoreflect.ServiceDescriptor {
|
||||
var services []protoreflect.ServiceDescriptor
|
||||
protoregistry.GlobalFiles.RangeFiles(func(file protoreflect.FileDescriptor) bool {
|
||||
if file.Package() != pkg {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < file.Services().Len(); i++ {
|
||||
services = append(services, file.Services().Get(i))
|
||||
}
|
||||
return true
|
||||
})
|
||||
sort.Slice(services, func(i, j int) bool { return services[i].FullName() < services[j].FullName() })
|
||||
return services
|
||||
}
|
||||
|
||||
func renderModule(module moduleInfo, services []protoreflect.ServiceDescriptor) []byte {
|
||||
var out bytes.Buffer
|
||||
fmt.Fprintf(&out, "# %s API\n\n%s\n\n", strings.ToUpper(module.Name), module.Description)
|
||||
out.WriteString("> 本文档由 protobuf descriptor 生成。字段名采用 protobuf JSON 名称;64 位整数在 JSON 中应按字符串处理。\n\n")
|
||||
out.WriteString("## 接入方式\n\n")
|
||||
fmt.Fprintf(&out, "- 动态 HTTP:`POST /rpc/%s/{Service}/{Method}`\n", module.Package)
|
||||
fmt.Fprintf(&out, "- 原生 gRPC:`/%s.{Service}/{Method}`\n", module.Package)
|
||||
fmt.Fprintf(&out, "- grpc-gateway:`POST /%s.{Service}/{Method}`\n", module.Package)
|
||||
out.WriteString("- 鉴权:需要登录的接口通过 `Authorization: Bearer <token>` 传递凭证。\n")
|
||||
out.WriteString("- 动态 HTTP 成功或失败均返回 HTTP 200,业务状态见 `code`、`message`、`details`。\n\n")
|
||||
|
||||
out.WriteString("## 服务概览\n\n")
|
||||
out.WriteString("| 服务 | 方法数 | 说明 |\n|---|---:|---|\n")
|
||||
for _, service := range services {
|
||||
fmt.Fprintf(&out, "| `%s` | %d | %s 服务 |\n", service.Name(), service.Methods().Len(), service.Name())
|
||||
}
|
||||
|
||||
schemas := map[protoreflect.FullName]protoreflect.MessageDescriptor{}
|
||||
enums := map[protoreflect.FullName]protoreflect.EnumDescriptor{}
|
||||
for _, service := range services {
|
||||
fmt.Fprintf(&out, "\n## %s\n\n", service.Name())
|
||||
out.WriteString("| 方法 | 动态 HTTP 路径 | 请求 | 响应 | 类型 |\n|---|---|---|---|---|\n")
|
||||
for i := 0; i < service.Methods().Len(); i++ {
|
||||
method := service.Methods().Get(i)
|
||||
kind := "Unary"
|
||||
if method.IsStreamingClient() || method.IsStreamingServer() {
|
||||
kind = "Streaming(动态 HTTP 暂不支持)"
|
||||
}
|
||||
fmt.Fprintf(&out, "| `%s` | `POST /rpc/%s/%s/%s` | [`%s`](#%s) | [`%s`](#%s) | %s |\n",
|
||||
method.Name(), module.Package, service.Name(), method.Name(),
|
||||
method.Input().FullName(), anchor(method.Input().FullName()), method.Output().FullName(), anchor(method.Output().FullName()), kind)
|
||||
collectMessage(method.Input(), schemas, enums)
|
||||
collectMessage(method.Output(), schemas, enums)
|
||||
}
|
||||
|
||||
for i := 0; i < service.Methods().Len(); i++ {
|
||||
method := service.Methods().Get(i)
|
||||
fmt.Fprintf(&out, "\n### %s.%s\n\n", service.Name(), method.Name())
|
||||
fmt.Fprintf(&out, "- 动态 HTTP:`POST /rpc/%s/%s/%s`\n", module.Package, service.Name(), method.Name())
|
||||
fmt.Fprintf(&out, "- gRPC:`/%s/%s`\n", service.FullName(), method.Name())
|
||||
fmt.Fprintf(&out, "- 白名单键:`%s.%s`\n", service.FullName(), method.Name())
|
||||
out.WriteString("- 请求示例:\n\n```json\n")
|
||||
out.Write(exampleJSON(method.Input()))
|
||||
out.WriteString("\n```\n")
|
||||
}
|
||||
}
|
||||
|
||||
messageNames := sortedMessageNames(schemas)
|
||||
if len(messageNames) > 0 {
|
||||
out.WriteString("\n## 消息结构\n")
|
||||
}
|
||||
for _, name := range messageNames {
|
||||
renderMessage(&out, schemas[name], enums)
|
||||
}
|
||||
|
||||
enumNames := make([]string, 0, len(enums))
|
||||
for name := range enums {
|
||||
enumNames = append(enumNames, string(name))
|
||||
}
|
||||
sort.Strings(enumNames)
|
||||
if len(enumNames) > 0 {
|
||||
out.WriteString("\n## 枚举\n")
|
||||
}
|
||||
for _, rawName := range enumNames {
|
||||
renderEnum(&out, enums[protoreflect.FullName(rawName)])
|
||||
}
|
||||
return out.Bytes()
|
||||
}
|
||||
|
||||
func collectMessage(message protoreflect.MessageDescriptor, messages map[protoreflect.FullName]protoreflect.MessageDescriptor, enums map[protoreflect.FullName]protoreflect.EnumDescriptor) {
|
||||
if _, exists := messages[message.FullName()]; exists {
|
||||
return
|
||||
}
|
||||
messages[message.FullName()] = message
|
||||
for i := 0; i < message.Fields().Len(); i++ {
|
||||
field := message.Fields().Get(i)
|
||||
if field.Message() != nil {
|
||||
collectMessage(field.Message(), messages, enums)
|
||||
}
|
||||
if field.Enum() != nil {
|
||||
enums[field.Enum().FullName()] = field.Enum()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func renderMessage(out *bytes.Buffer, message protoreflect.MessageDescriptor, enums map[protoreflect.FullName]protoreflect.EnumDescriptor) {
|
||||
fmt.Fprintf(out, "\n### %s\n\n", message.FullName())
|
||||
if message.Fields().Len() == 0 {
|
||||
out.WriteString("空消息:请求时发送 `{}`。\n")
|
||||
return
|
||||
}
|
||||
out.WriteString("| JSON 字段 | 类型 | 规则 |\n|---|---|---|\n")
|
||||
for i := 0; i < message.Fields().Len(); i++ {
|
||||
field := message.Fields().Get(i)
|
||||
rule := "可选"
|
||||
if field.Cardinality() == protoreflect.Repeated {
|
||||
rule = "数组"
|
||||
}
|
||||
if field.IsMap() {
|
||||
rule = "对象映射"
|
||||
}
|
||||
if field.ContainingOneof() != nil && !field.ContainingOneof().IsSynthetic() {
|
||||
rule = "oneof: " + string(field.ContainingOneof().Name())
|
||||
}
|
||||
fmt.Fprintf(out, "| `%s` | `%s` | %s |\n", field.JSONName(), fieldType(field), rule)
|
||||
if field.Enum() != nil {
|
||||
enums[field.Enum().FullName()] = field.Enum()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func renderEnum(out *bytes.Buffer, enum protoreflect.EnumDescriptor) {
|
||||
fmt.Fprintf(out, "\n### %s\n\n", enum.FullName())
|
||||
out.WriteString("| 名称 | 数值 |\n|---|---:|\n")
|
||||
for i := 0; i < enum.Values().Len(); i++ {
|
||||
value := enum.Values().Get(i)
|
||||
fmt.Fprintf(out, "| `%s` | %d |\n", value.Name(), value.Number())
|
||||
}
|
||||
}
|
||||
|
||||
func fieldType(field protoreflect.FieldDescriptor) string {
|
||||
var value string
|
||||
switch field.Kind() {
|
||||
case protoreflect.MessageKind, protoreflect.GroupKind:
|
||||
value = string(field.Message().FullName())
|
||||
case protoreflect.EnumKind:
|
||||
value = string(field.Enum().FullName())
|
||||
default:
|
||||
value = field.Kind().String()
|
||||
}
|
||||
if field.IsMap() {
|
||||
value = "map<" + fieldType(field.MapKey()) + ", " + fieldType(field.MapValue()) + ">"
|
||||
} else if field.Cardinality() == protoreflect.Repeated {
|
||||
value = "[]" + value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func exampleJSON(message protoreflect.MessageDescriptor) []byte {
|
||||
value := exampleMessage(message, map[protoreflect.FullName]bool{}, 0)
|
||||
data, err := json.MarshalIndent(value, "", " ")
|
||||
must(err)
|
||||
return data
|
||||
}
|
||||
|
||||
func exampleMessage(message protoreflect.MessageDescriptor, visiting map[protoreflect.FullName]bool, depth int) map[string]any {
|
||||
if depth > 2 || visiting[message.FullName()] {
|
||||
return map[string]any{}
|
||||
}
|
||||
visiting[message.FullName()] = true
|
||||
defer delete(visiting, message.FullName())
|
||||
result := map[string]any{}
|
||||
seenOneof := map[protoreflect.Name]bool{}
|
||||
for i := 0; i < message.Fields().Len(); i++ {
|
||||
field := message.Fields().Get(i)
|
||||
if oneof := field.ContainingOneof(); oneof != nil && !oneof.IsSynthetic() {
|
||||
if seenOneof[oneof.Name()] {
|
||||
continue
|
||||
}
|
||||
seenOneof[oneof.Name()] = true
|
||||
}
|
||||
value := exampleField(field, visiting, depth)
|
||||
if field.Cardinality() == protoreflect.Repeated && !field.IsMap() {
|
||||
value = []any{value}
|
||||
}
|
||||
if field.IsMap() {
|
||||
value = map[string]any{"key": exampleField(field.MapValue(), visiting, depth)}
|
||||
}
|
||||
result[field.JSONName()] = value
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func exampleField(field protoreflect.FieldDescriptor, visiting map[protoreflect.FullName]bool, depth int) any {
|
||||
switch field.Kind() {
|
||||
case protoreflect.BoolKind:
|
||||
return false
|
||||
case protoreflect.StringKind:
|
||||
return "string"
|
||||
case protoreflect.BytesKind:
|
||||
return "BASE64"
|
||||
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind,
|
||||
protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
|
||||
return "0"
|
||||
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind,
|
||||
protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.FloatKind, protoreflect.DoubleKind:
|
||||
return 0
|
||||
case protoreflect.EnumKind:
|
||||
if field.Enum().Values().Len() > 0 {
|
||||
return string(field.Enum().Values().Get(0).Name())
|
||||
}
|
||||
return 0
|
||||
case protoreflect.MessageKind, protoreflect.GroupKind:
|
||||
return exampleMessage(field.Message(), visiting, depth+1)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func sortedMessageNames(messages map[protoreflect.FullName]protoreflect.MessageDescriptor) []protoreflect.FullName {
|
||||
names := make([]protoreflect.FullName, 0, len(messages))
|
||||
for name := range messages {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Slice(names, func(i, j int) bool { return names[i] < names[j] })
|
||||
return names
|
||||
}
|
||||
|
||||
func anchor(name protoreflect.FullName) string {
|
||||
return strings.ToLower(strings.ReplaceAll(string(name), ".", ""))
|
||||
}
|
||||
|
||||
func must(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user