11package doc
22
33import (
4- "encoding/json"
5- "strings"
4+ "encoding/json"
5+ "strings"
66
7- "github.com/duke-git/lancet/v2/slice"
8- openapi_v2 "github.com/google/gnostic-models/openapiv2"
9- "github.com/weibaohui/kom/utils"
10- "k8s.io/klog/v2"
7+ "github.com/duke-git/lancet/v2/slice"
8+ openapi_v2 "github.com/google/gnostic-models/openapiv2"
9+ "github.com/weibaohui/kom/utils"
10+ "k8s.io/klog/v2"
1111)
1212
1313// 移除全局变量 trees
@@ -96,16 +96,21 @@ type Definitions struct {
9696 AdditionalProperties []map [string ]interface {} `json:"additional_properties"`
9797}
9898
99- // definitionsMap 存储所有定义,以便处理引用
100- var definitionsMap map [string ]SchemaDefinition
99+ // 注意:移除全局 definitionsMap,避免并发写入导致崩溃
101100
102101var blackList = []string {
103- "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps" ,
104- "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON" ,
105- "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps" ,
102+ "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps" ,
103+ "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSON" ,
104+ "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps" ,
106105}
107106
108107// parseOpenAPISchema 解析 OpenAPI Schema JSON 字符串并返回根 TreeNode
108+ // 参数:
109+ // - schemaJSON: OpenAPI 单个 definition 的 JSON 字符串
110+ // - defs: definitions 映射,用于处理 $ref 引用,采用局部传参避免并发问题
111+ // 返回:
112+ // - TreeNode: 构建的树根节点
113+ // - error: 解析错误
109114// Example:
110115//
111116// JSON样例
@@ -116,17 +121,16 @@ var blackList = []string{
116121// },
117122// "vendor_extension": [ {},{}]
118123// }
119- func parseOpenAPISchema (schemaJSON string ) (TreeNode , error ) {
120- var def SchemaDefinition
121- err := json .Unmarshal ([]byte (schemaJSON ), & def )
122- if err != nil {
123- return TreeNode {}, err
124- }
125- // klog.V(2).Infof("add def cache %s", def.Name)
126- definitionsMap [def .Name ] = def
127- // klog.V(2).Infof("add def length %d", len(definitionsMap))
128-
129- return buildTree (def , "" ), nil
124+ func parseOpenAPISchema (schemaJSON string , defs map [string ]SchemaDefinition ) (TreeNode , error ) {
125+ var def SchemaDefinition
126+ err := json .Unmarshal ([]byte (schemaJSON ), & def )
127+ if err != nil {
128+ return TreeNode {}, err
129+ }
130+ // 将解析后的定义写入局部映射,避免全局并发写入
131+ defs [def .Name ] = def
132+ // 构建树
133+ return buildTree (def , "" , defs ), nil
130134}
131135func parseID (id string ) (group , version , kind string ) {
132136 parts := strings .Split (id , "." )
@@ -150,9 +154,15 @@ func parseID(id string) (group, version, kind string) {
150154}
151155
152156// buildTree 根据 SchemaDefinition 构建 TreeNode
153- func buildTree (def SchemaDefinition , parentId string ) TreeNode {
154- // todo 应该使用GVK作为
155- klog .V (8 ).Infof ("buildTree %s" , def .Name )
157+ // 参数:
158+ // - def: 单个 SchemaDefinition
159+ // - parentId: 父节点ID,用于构建 FullId
160+ // - defs: definitions 映射,用于处理 $ref 引用
161+ // 返回:
162+ // - TreeNode: 构建的树节点
163+ func buildTree (def SchemaDefinition , parentId string , defs map [string ]SchemaDefinition ) TreeNode {
164+ // 打印中文日志,便于定位构建过程
165+ klog .V (8 ).Infof ("构建树节点: %s" , def .Name )
156166
157167 labelParts := strings .Split (def .Name , "." )
158168 label := labelParts [len (labelParts )- 1 ]
@@ -163,16 +173,16 @@ func buildTree(def SchemaDefinition, parentId string) TreeNode {
163173 }
164174 var children []* TreeNode
165175
166- for _ , prop := range def .Value .Properties .AdditionalProperties {
167- children = append (children , buildPropertyNode (prop , def .Name ))
168- }
176+ for _ , prop := range def .Value .Properties .AdditionalProperties {
177+ children = append (children , buildPropertyNode (prop , def .Name , defs ))
178+ }
169179
170180 group , version , kind := parseID (def .Name )
171181 return TreeNode {
172182 ID : def .Name ,
173183 FullId : parentId + "." + def .Name ,
174184 Label : label ,
175- Value : utils .RandNLengthString (20 ),
185+ Value : utils .RandNLengthString (20 ),
176186 Description : def .Value .Description ,
177187 Type : nodeType ,
178188 Children : children ,
@@ -184,13 +194,19 @@ func buildTree(def SchemaDefinition, parentId string) TreeNode {
184194}
185195
186196// buildPropertyNode 根据 Property 构建 TreeNode
187- func buildPropertyNode (prop Property , parentId string ) * TreeNode {
188- label := prop .Name
189- nodeID := prop .Name
190- fullID := parentId + "." + prop .Name
191- description := prop .Value .Description
192- nodeType := ""
193- ref := ""
197+ // 参数:
198+ // - prop: 属性定义
199+ // - parentId: 父节点ID
200+ // - defs: definitions 映射,用于解析 $ref 引用
201+ // 返回:
202+ // - *TreeNode: 构建的属性节点
203+ func buildPropertyNode (prop Property , parentId string , defs map [string ]SchemaDefinition ) * TreeNode {
204+ label := prop .Name
205+ nodeID := prop .Name
206+ fullID := parentId + "." + prop .Name
207+ description := prop .Value .Description
208+ nodeType := ""
209+ ref := ""
194210
195211 if prop .Value .Type != nil && len (prop .Value .Type .Value ) > 0 {
196212 nodeType = prop .Value .Type .Value [0 ]
@@ -202,34 +218,34 @@ func buildPropertyNode(prop Property, parentId string) *TreeNode {
202218 var children []* TreeNode
203219
204220 // 如果有引用,查找定义并递归构建子节点
205- if ref != "" && ! slice .Contain (blackList , ref ) {
206- // 假设 ref 的格式为 "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
207- refParts := strings .Split (ref , "/" )
208- refName := refParts [len (refParts )- 1 ]
209- // 构建完整的引用路径
210- // fullRef := strings.Join(refParts[1:], ".")
211-
212- // 这个可能会导致 循环引用溢出
213- if def , exists := definitionsMap [refName ]; exists {
214- if ! slice .Contain (blackList , refName ) {
215- childNode := buildTree (def , fullID )
216- children = append (children , & childNode )
217- }
218- } else {
219- // 如果引用的定义不存在,可以记录为一个叶子节点或处理为需要进一步扩展
220- children = append (children , & TreeNode {
221- ID : refName ,
222- FullId : fullID + "." + refName ,
223- Label : refName ,
224- Value : refName ,
225- Description : "Referenced definition not found " ,
226- })
227- }
228- }
229-
230- for _ , pp := range prop .Value .Properties .AdditionalProperties {
231- children = append (children , buildPropertyNode (pp , fullID ))
232- }
221+ if ref != "" && ! slice .Contain (blackList , ref ) {
222+ // 假设 ref 的格式为 "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
223+ refParts := strings .Split (ref , "/" )
224+ refName := refParts [len (refParts )- 1 ]
225+ // 构建完整的引用路径
226+ // fullRef := strings.Join(refParts[1:], ".")
227+
228+ // 这个可能会导致 循环引用溢出
229+ if def , exists := defs [refName ]; exists {
230+ if ! slice .Contain (blackList , refName ) {
231+ childNode := buildTree (def , fullID , defs )
232+ children = append (children , & childNode )
233+ }
234+ } else {
235+ // 如果引用的定义不存在,可以记录为一个叶子节点或处理为需要进一步扩展
236+ children = append (children , & TreeNode {
237+ ID : refName ,
238+ FullId : fullID + "." + refName ,
239+ Label : refName ,
240+ Value : refName ,
241+ Description : "未找到引用的 definition" ,
242+ })
243+ }
244+ }
245+
246+ for _ , pp := range prop .Value .Properties .AdditionalProperties {
247+ children = append (children , buildPropertyNode (pp , fullID , defs ))
248+ }
233249
234250 return & TreeNode {
235251 ID : nodeID ,
@@ -264,44 +280,49 @@ func printTree(node *TreeNode, level int) {
264280 }
265281}
266282
267- // InitTrees 解析 OpenAPI Schema 并构建 Docs 结构体,避免全局变量,减少内存占用
283+ // InitTrees 解析 OpenAPI Schema 并构建 Docs 结构体,避免全局变量,减少并发风险
284+ // 参数:
285+ // - schema: OpenAPI v2 文档对象
286+ // 返回:
287+ // - *Docs: 构建好的文档树
268288func InitTrees (schema * openapi_v2.Document ) * Docs {
269- definitionsMap = make (map [string ]SchemaDefinition )
289+ // 使用局部 definitionsMap,避免并发写入导致崩溃
290+ definitionsMap := make (map [string ]SchemaDefinition )
270291
271292 // 将 OpenAPI Schema 转换为 JSON 字符串
272- schemaBytes , err := json .Marshal (schema )
273- if err != nil {
274- klog .V (2 ).Infof ("Error marshaling OpenAPI schema to JSON: %v\n " , err )
275- return nil
276- }
293+ schemaBytes , err := json .Marshal (schema )
294+ if err != nil {
295+ klog .V (2 ).Infof ("序列化 OpenAPI Schema 为 JSON 失败 : %v" , err )
296+ return nil
297+ }
277298
278299 root := & RootDefinitions {}
279- err = json .Unmarshal (schemaBytes , root )
280- if err != nil {
281- klog .V (2 ).Infof ("Error unmarshaling OpenAPI schema : %v\n " , err )
282- return nil
283- }
300+ err = json .Unmarshal (schemaBytes , root )
301+ if err != nil {
302+ klog .V (2 ).Infof ("反序列化 OpenAPI Schema 失败 : %v" , err )
303+ return nil
304+ }
284305 definitionList := root .Definitions .AdditionalProperties
285306
286307 var trees []TreeNode
287308 // 进行第一遍处理,此时Ref并没有读取,只是记录了引用
288- for _ , definition := range definitionList {
289- str := utils .ToJSON (definition )
290- // 解析 Schema 并构建树形结构
291- treeRoot , err := parseOpenAPISchema (str )
292- if err != nil {
293- klog .V (2 ).Infof ("Error parsing OpenAPI schema : %v\n " , err )
294- continue
295- }
296- trees = append (trees , treeRoot )
297- }
309+ for _ , definition := range definitionList {
310+ str := utils .ToJSON (definition )
311+ // 解析 Schema 并构建树形结构(中文日志)
312+ treeRoot , err := parseOpenAPISchema (str , definitionsMap )
313+ if err != nil {
314+ klog .V (2 ).Infof ("解析 OpenAPI Schema 失败 : %v" , err )
315+ continue
316+ }
317+ trees = append (trees , treeRoot )
318+ }
298319
299320 docs := & Docs {Trees : trees }
300321 // 进行遍历处理,将child中ref对应的类型提取出来
301322 // 此时应该所有的类型都已经存在了
302- for i := range docs .Trees {
303- docs .loadChild (& docs .Trees [i ])
304- }
323+ for i := range docs .Trees {
324+ docs .loadChild (& docs .Trees [i ])
325+ }
305326 for i := range docs .Trees {
306327 docs .loadArrayItems (& docs .Trees [i ])
307328 }
@@ -314,10 +335,10 @@ func InitTrees(schema *openapi_v2.Document) *Docs {
314335 }
315336 // 将所有节点的ID,改为唯一的
316337
317- for i := range docs .Trees {
318- docs .uniqueID (& docs .Trees [i ])
319- }
320- return docs
338+ for i := range docs .Trees {
339+ docs .uniqueID (& docs .Trees [i ])
340+ }
341+ return docs
321342}
322343
323344// loadArrayItems 递归处理数组类型引用,作为 Docs 的方法
0 commit comments