En mi API "simplificada", todas las respuestas se derivan ( heredan ) de una clase de "respuesta" base. La clase de respuesta se compone de un encabezado lleno de metadatos y el cuerpo que contiene los datos básicos que solicita el usuario. La respuesta (en JSON) se presenta de manera que todos los metadatos están en la primera "capa" y el cuerpo es un único atributo llamado "cuerpo" como tal
response
|--metadata attribute 1 (string/int/object)
|--metadata attribute 2 (string/int/object)
|--body (object)
|--body attribute 1 (string/int/object)
|--body attribute 2 (string/int/object)
He intentado definir esta relación con arrogancia con el siguiente JSON:
{
...
"definitions": {
"response": {
"allOf": [
{
"$ref": "#/definitions/response_header"
},
{
"properties": {
"body": {
"description": "The body of the response (not metadata)",
"schema": {
"$ref": "#/definitions/response_body"
}
}
}
}
]
},
"response_header": {
"type": "object",
"required": [
"result"
],
"properties": {
"result": {
"type": "string",
"description": "value of 'success', for a successful response, or 'error' if there is an error",
"enum": [
"error",
"success"
]
},
"message": {
"type": "string",
"description": "A suitable error message if something went wrong."
}
}
},
"response_body": {
"type": "object"
}
}
}
Luego trato de crear diferentes respuestas creando las diversas clases de cuerpo / encabezado que heredan del cuerpo / encabezado, y luego creo clases de respuesta secundaria que se componen de las clases de encabezado / cuerpo relevantes (que se muestran en el código fuente en la parte inferior). Sin embargo, estoy seguro de que esta es la forma incorrecta de hacer las cosas o que mi implementación es incorrecta. No he podido encontrar un ejemplo de herencia en la especificación swagger 2.0 (que se muestra a continuación) pero encontré un ejemplo de composición .
Estoy bastante seguro de que este "discriminador" tiene un papel importante que desempeñar, pero no estoy seguro de lo que tengo que hacer.
Pregunta
¿Podría alguien mostrarme cómo se supone que uno debe implementar la composición + herencia en swagger 2.0 (JSON), preferiblemente "arreglando" mi código de ejemplo a continuación? También sería genial si pudiera especificar una clase ErrorResponse que herede de la respuesta donde el atributo "resultado" en el encabezado siempre se establece en "error".
{
"swagger": "2.0",
"info": {
"title": "Test API",
"description": "Request data from the system.",
"version": "1.0.0"
},
"host": "xxx.xxx.com",
"schemes": [
"https"
],
"basePath": "/",
"produces": [
"application/json"
],
"paths": {
"/request_filename": {
"post": {
"summary": "Request Filename",
"description": "Generates an appropriate filename for a given data request.",
"responses": {
"200": {
"description": "A JSON response with the generated filename",
"schema": {
"$ref": "#/definitions/filename_response"
}
}
}
}
}
},
"definitions": {
"response": {
"allOf": [
{
"$ref": "#/definitions/response_header"
},
{
"properties": {
"body": {
"description": "The body of the response (not metadata)",
"schema": {
"$ref": "#/definitions/response_body"
}
}
}
}
]
},
"response_header": {
"type": "object",
"required": [
"result"
],
"properties": {
"result": {
"type": "string",
"description": "value of 'success', for a successful response, or 'error' if there is an error",
"enum": [
"error",
"success"
]
},
"message": {
"type": "string",
"description": "A suitable error message if something went wrong."
}
}
},
"response_body": {
"type": "object"
},
"filename_response": {
"extends": "response",
"allOf": [
{
"$ref": "#definitions/response_header"
},
{
"properties": {
"body": {
"schema": {
"$ref": "#definitions/filename_response_body"
}
}
}
}
]
},
"filename_response_body": {
"extends": "#/definitions/response_body",
"properties": {
"filename": {
"type": "string",
"description": "The automatically generated filename"
}
}
}
}
}
Actualización del diagrama
Para tratar de aclarar lo que quiero, he creado el diagrama muy básico a continuación que tiene como objetivo mostrar que todas las respuestas son instancias del objeto "respuesta" que han sido construidas por (composición) usando cualquier combinación de objetos response_header y response_body. Los objetos response_header y response_body pueden extenderse e insertarse en cualquier objeto de respuesta, lo que se hace en el caso de un filename_response que usa el hijo filename_response_body de la clase base response_body. Tanto el error como las respuestas correctas utilizan el objeto "respuesta".
I have the properties of X and my own properties.
. La herencia sugiere una relaciónX is my parent. I have its properties and my own.
. La herencia es útil si quiere decir que un determinado conjunto de modelos son aplicables del padre que se está utilizando.Respuestas:
Como principiante en fanfarronería, no encuentro la documentación oficial sobre polimorfismo y composición fácil de entender, porque carece de un ejemplo . Cuando busqué en la red, hay muchos buenos ejemplos que se refieren a swagger 1.2 cuando
extends
era válido.Para swagger 2.0 encontré un buen ejemplo en las fuentes de especificaciones de swagger en github a través de este grupo de Google
Según las fuentes anteriores, aquí hay un breve ejemplo de herencia válida en YAML:
fuente
editor.swagger.io
, veo un pequeño error: en la sección de modelos, veo elPet
modelo varias veces. El contenido de estos modelos está bien. Solo los nombres están equivocados.editor2.swagger.io
no verá este problemaPet
¿Qué tal si la clase A extiende la clase B, también deberíamos usarla? GraciasDescubrí que la composición funciona bien incluso sin una definición de
discriminator
.Por ejemplo, base
Response
:Se representa como:
Y podemos extenderlo para refinar el esquema personalizado de
result
campo:Y se representará correctamente como:
Tenga en cuenta que eso
allOf
es suficiente para que esto funcione y nodiscriminator
se utilice ningún campo. Esto es bueno, porque funciona y esto es importante, creo que las herramientas podrán generar código sindiscriminator
campo.fuente
allOf
, pero de alguna manera en elopenapi.yaml
, encuentro que las subclases contienen las propiedades de la superclase de manera redundante, ¿es correcto?Todas las respuestas aquí ya son excelentes, pero solo quiero agregar una nota menor sobre la composición versus la herencia . Según Swagger / OpenAPI Spec , para implementar la composición
allOf
basta con usar la propiedad, como bien señala @oblalex . Sin embargo, para implementar la herencia , debe usarallOf
condiscriminator
, como en el ejemplo de @ TomaszSętkowski .Además, encontré algunos ejemplos más de Swagger tanto de composición como de herencia en API Handyman. Son parte de una excelente serie de tutoriales Swagger / OpenAPI de Arnaud Lauret que creo que todos deberían ver.
fuente
El ejemplo estándar de Swagger 2.0 que ha compartido muestra una relación de composición, específicamente captura una relación "es una especie de" supertipo / subtipo, sin embargo, no es polimorfismo en sí mismo.
Lo sería si pudiera hacer referencia a la definición base de Pet como un parámetro de entrada, luego elegir Cat o ingresar un objeto Cat JSON como el valor para la solicitud de entrada, y tener esto aceptable para Swagger UI.
No pude hacer que esto funcione directamente.
Lo mejor que pude hacer fue establecer additionalProperties en verdadero en el objeto base (por ejemplo, Pet), especificar Pet usando la referencia de puntero JSON como esquema de entrada y finalmente copiar y pegar mi objeto de valor Cat JSON en Swagger UI. Dado que las propiedades adicionales están permitidas, Swagger UI generó una carga útil de solicitud de entrada válida.
fuente