我有一个服务器的 openapi 规范,其中一个端点返回
Thing
列表,但其中 Thing
是从不同 openapi 规范生成的极其复杂且大型的对象。将此服务器规范添加到该 openapi 定义或通过路径引用该对象是不可能的(阅读:这是可能的,但出于多种原因,这是最糟糕的选择)。 无论如何,该规范都会以各种语言生成大型模型库/包/板条箱,从this规范生成的客户端/服务器代码可以访问这些语言。
特别是,我想用 Rust 生成服务器代码。 然后,一些其他语言的客户端内容,但我觉得如果我可以通过 Rust 服务器生成克服这个问题,我就会知道该怎么做。 在此服务器规范中,它引用了
Thing
。 我希望生成器不要生成此模型,并将对其的任何引用替换为在 Thing
中找到的 our_crate::thing::Thing
。
首先,我不太确定要为模式写什么。 我已经尝试过这些方法,第三种似乎是最坏的最好的?
components:
schemas:
Thing1:
description: Doesn't generate anything because it's a free-form object.
type: object
properties: {}
Thing2:
description: |
Obviously generates a struct with a `String` field with any of the below approaches.
type: object
properties:
dummy_field:
type: string
Thing3:
description: |
Assigns it the type `HashMap<String, serde_json::Value>` or a `HashMap`
of `String` to a wrapper of `serde_json::Value`, depending on generator.
type: object
additionalProperties: true
Thing4:
description: Tried in conjunction with a schema/type mapping; same result as 3.
type: object
format: thing4
additionalProperties: true
官方文档说要使用 CLI 的
--import-mappings
参数,或者在使用 -c
传递的配置中,如下所示:
generatorName: rust-axum
skipSchemaValidation: true
additionalProperties:
generateAliasAsModel: true
importMappings:
Thing: "our_crate::thing::Thing"
这对
rust-axum
或 rust-server
生成器没有影响,并且输出如上面的模式描述中所述。 完全相同的文档后来说在几乎相同的场景中将--schema-mappings
与--type-mappings
结合使用,并且我尝试使用上面Thing4
中的模式
generatorName: rust-server
skipSchemaValidation: true
additionalProperties:
generateAliasAsModel: true
schemaMappings:
thing: "our_crate::thing::Thing"
typeMappings:
"object+thing": "thing"
产生空结构。
所以我现在有点迷失了。 我读过很多关于这个问题的其他帖子,所有的答案都与文档大致相似。 我对 Java 或编写小胡子模板不太熟悉,但此时我感觉到这是生成器本身的问题,或者我可以通过某种方式修改模板以了解此映射。
所以我的问题是,我现在应该集中精力做什么? 自定义模板可以实现这一点吗?
或者,我需要更换发电机本身吗? 我注意到在
rust-axum
generator 和 rust-server
generator
只提到过一次此导入映射选项,而且它们都是空的。 使用调试选项运行它确实表明没有提及此自定义映射。根据我对这个系统的非常有限的了解,小胡子模板只能在执行其操作时访问这些变量。
否则,有没有一种我还没有想到的方法来完成这个看似简单但可能很常见的事情?
这最终是对我有用的解决方案。 我仍然非常感谢以前做过这件事的人对一般问题的一些看法。 该文档含糊不清,在许多方面都具有误导性,而且我找不到的任何搜索结果都达到了解决方案的完整描述的水平。 我仍然有很多问题,最喜欢的是,“这真的是这样做的吗?” 我希望其他有同样问题需要解决的人立即找到这个答案,并发现它有用,而不是像我一样感到沮丧。 无论如何,我的 OpenAPI 模式的结构是这样的
├── openapi.yaml
├── parameters
│ ├── path
│ └── query
├── path_items
│ ├── things.yaml
└── schemas
├── errors
└── responses
我想要解析为从外部板条箱导入类型的模式是通过路径引用的:
# path_items/things.yaml
get:
operationId: GetThingsPage
description: Get a single page of things.
security:
- ThingsAuth:
- things:read
parameters:
- $ref: "../parameters/path/thing_id.yaml"
- $ref: "../parameters/query/user.yaml"
- $ref: "../parameters/query/page_token.yaml"
responses:
200:
description: OK
content:
application/json:
schema:
$ref: "../schemas/responses/getthingspageresponse.yaml"
400:
description: BAD_REQUEST
content:
application/json:
schema:
$ref: "../schemas/errors/error.yaml"
401:
description: UNAUTHORIZED
content:
application/json:
schema:
$ref: "../schemas/errors/error.yaml"
403:
description: FORBIDDEN
content:
application/json:
schema:
$ref: "../schemas/errors/error.yaml"
500:
description: INTERNAL_SERVER_ERROR
content:
application/json:
schema:
$ref: "../schemas/errors/error.yaml"
# schemas/responses/getthingspageresponse.yaml
type: object
properties:
next_page_token:
name: next_page_token
type: string
description: |
If present, used to fetch the next set of results, otherwise it's the last page.
things:
description: Page of things.
type: array
items:
$ref: "../../openapi.yaml#/components/schemas/Thing"
required:
- things
最后,
# openapi.yaml
components:
schemas:
Thing:
# All of the different ways I tried to express that this is
# a "placeholder," intended to be specified in some mapping option
# as an import.
最开始被收起来的东西是
# openapi.yaml
components:
schemas:
Thing:
description: A reference to `Thing` as defined in our global models.
type: ExternalThingSchema
然后是
typeMapping
(在使用
-c
选项传递到 CLI 的配置中):packageName: "things-internal-server"
skipSchemaValidation: true
additionalProperties:
generateAliasAsModel: true
typeMappings:
ExternalThingSchema: "the_crate::models::Thing"
这产生了 Rust 代码,最终意识到我的外部依赖:
/// A reference to `Thing` as defined in our global models.
#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct Thing(the_crate::models::Thing);
但是有这个新类型包装器很烦人,所以我想到将外部类型直接移到响应正文中:
# schemas/responses/getthingspageresponse.yaml
type: object
properties:
next_page_token:
type: string
description: |
If present, used to fetch the next set of results, otherwise it's the last page.
things:
description: Page of things.
type: array
items:
description: A reference to `Thing` as defined in our global models.
type: ExternalThingSchema
required:
- things
这不起作用:生成器抱怨“
[main] ERROR o.o.codegen.utils.ModelUtils - Undefined array inner type for 'null'. Default to String
”,并且在 debugModel 日志中我们看到分配的类型是
Vec<String>
。所以,最后,似乎(我也不能说为什么)ExternalThingSchema
只能在顶级 openapi.yaml 文件中提到:
components:
schemas:
GetThingsPageResponseBody:
type: object
properties:
next_page_token:
description: If present, used to fetch the next set of results, otherwise it's the last page.
type: string
things:
description: Page of things.
type: array
items:
description: A reference to `Thing` as defined in our global models.
type: ExternalThingSchema
required:
- things
产生
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct GetThingsPageResponseBody {
/// If present, used to fetch the next set of results, otherwise it's the last page.
#[serde(rename = "next_page_token")]
#[serde(skip_serializing_if="Option::is_none")]
pub next_page_token: Option<String>,
/// Page of things.
#[serde(rename = "things")]
pub things: Vec<the_crate::models::Thing>,
}
这终于是我想要的了。NB
:即使有了这个typeMappings
,即使它生成了我想要的东西,生成器仍然会发出警告:
[main] WARN o.o.codegen.DefaultCodegen - Unknown type found in the schema: ExternalThingSchema. To map it, please use the schema mapping option (e.g. --schema-mappings in CLI)
将任何可能的东西传递给
schemaMappings
都没有效果,我不在乎。