如何在Clojure中迁移OpenAPI的requestBody/responses到components并生成$ref
OpenAPI规范Clojure数据迁移完整实现
核心需求
将:paths下各端点的:requestBody迁移至:components/:requestBodies,:responses迁移至:components/:responses,原位置替换为$ref引用,同时保留:parameters节点不变。
实现思路
- 初始化组件节点:保留原规范中已有的
:components内容,若无则创建空的:requestBodies和:responses。 - 遍历所有路径与HTTP方法:逐个处理每个端点的请求体和响应定义。
- 生成唯一引用键:基于路径、方法和类型生成可读性强的唯一键,确保引用不冲突。
- 迁移并替换:将请求体/响应内容存入对应组件节点,原位置替换为
$ref引用。 - 合并结果:将修改后的路径和更新后的组件合并回原规范数据。
完整代码实现
(ns openapi-migrator.core (:require [clojure.string :as str])) ;; 生成唯一引用键:路径替换斜杠为下划线,拼接方法和类型 (defn generate-ref-key [path method type] (str (str/replace (subs path 1) "/" "_") "_" (name method) "_" type)) ;; 处理单个方法的requestBody:迁移至components并替换为$ref (defn process-request-body [path method method-data components] (if-let [req-body (:requestBody method-data)] (let [ref-key (generate-ref-key path method "requestBody") updated-components (update components :requestBodies assoc ref-key req-body) updated-method (assoc method-data :requestBody {"$ref" (str "#/components/requestBodies/" ref-key)})] [updated-method updated-components]) [method-data components])) ;; 处理单个方法的responses:逐个迁移响应至components并替换为$ref (defn process-responses [path method method-data components] (if-let [responses (:responses method-data)] (let [[updated-responses updated-components] (reduce (fn [[resp-map comps] [status-code resp]] (let [ref-key (generate-ref-key path method (str "response_" status-code)) new-comps (update comps :responses assoc ref-key resp)] [(assoc resp-map status-code {"$ref" (str "#/components/responses/" ref-key)}) new-comps])) [{} components] responses)] [(assoc method-data :responses updated-responses) updated-components]) [method-data components])) ;; 处理单个路径下的所有HTTP方法 (defn process-path [path path-data components] (reduce (fn [[updated-path comps] [method method-data]] (let [[method-with-ref1 comps1] (process-request-body path method method-data comps) [method-with-ref2 comps2] (process-responses path method method-with-ref1 comps1)] [(assoc updated-path method method-with-ref2) comps2])) [{} components] path-data)) ;; 主函数:执行完整迁移逻辑 (defn migrate-to-components [openapi-spec] ;; 初始化组件:保留原有内容,若无则创建空节点 (let [initial-components (merge {:requestBodies {} :responses {}} (:components openapi-spec)) ;; 遍历所有路径,处理后得到更新的路径和组件 [updated-paths final-components] (reduce (fn [[paths comps] [path path-data]] (let [[new-path-data new-comps] (process-path path path-data comps)] [(assoc paths path new-path-data) new-comps])) [{} initial-components] (:paths openapi-spec))] ;; 合并回原规范数据 (assoc openapi-spec :paths updated-paths :components final-components)))
使用示例
;; 测试用OpenAPI规范 (def sample-spec {:openapi "3.0.0" :paths {"/users" {:post {:requestBody {:content {"application/json" {:schema {:type "object" :properties {:name {:type "string"}}}}}} :responses {"200" {:description "OK" :content {"application/json" {:schema {:type "object" :properties {:id {:type "integer"}}}}}}}} "/users/{id}" {:get {:parameters [{:name "id" :in "path" :required true :schema {:type "integer"}}]} :responses {"200" {:description "User found" :content {"application/json" {:schema {:type "object" :properties {:name {:type "string"}}}}}}}}}}) ;; 执行迁移 (migrate-to-components sample-spec)
关键细节说明
- 引用键生成:默认用路径+方法+类型生成可读键,若需去重(相同内容复用引用),可替换为基于内容哈希的键,例如:
(defn generate-ref-key [content type] (str type "_" (hash content))) - 组件合并:使用
merge保留原规范中已有的:components内容,避免覆盖自定义组件。 - 参数保留:代码中未修改
:parameters节点,确保原参数定义完全保留。
内容的提问来源于stack exchange,提问作者user51
相关产品推荐
相关产品推荐

