mvc swagger

mvc - Swagger Herencia y Composición



swagger ui (4)

Como principiante en fanfarronería, no encuentro la documentación oficial sobre el polimorfismo y la 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 fue válido.

Para swagger 2.0 encontré un buen ejemplo en fuentes de especificaciones de swagger en github a través de este grupo de google

Basado en las fuentes anteriores, aquí hay un breve ejemplo de herencia válida en YAML:

definitions: Pet: discriminator: petType required: - name - petType # required for inheritance to work properties: name: type: string petType: type: string Cat: allOf: - $ref: ''#/definitions/Pet'' # Cat has all properties of a Pet - properties: # extra properties only for cats huntingSkill: type: string default: lazy enum: - lazy - aggressive Dog: allOf: - $ref: ''#/definitions/Pet'' # Dog has all properties of a Pet - properties: # extra properties only for dogs packSize: description: The size of the pack the dog is from type: integer

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 centrales que solicita el usuario. La respuesta (en JSON) se presenta de tal manera que todos los metadatos están en la primera "capa" y el cuerpo es un atributo único 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 en swagger 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 relevantes de encabezado / cuerpo (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 de swagger 2.0 (que se muestra a continuación) pero he encontrado un ejemplo de composition .

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" } } } } }

Diagram Update

Para tratar de aclarar lo que quiero, he creado el siguiente diagrama muy básico que tiene como objetivo mostrar que todas las respuestas son instancias del objeto "respuesta" que ha sido construido por (composición) usando cualquier combinación de objetos de respuesta_cuadro y respuesta_cuerpo. Los objetos response_header y response_body se pueden extender e insertar en cualquier objeto de respuesta, lo que se hace en el caso de un filename_response que utiliza el nombre de archivo_response_body hijo de la clase base response_body. Tanto el error como las respuestas exitosas usan el objeto "respuesta".


Descubrí que la composición funciona bien incluso sin la definición de discriminator .

Por ejemplo, Response base:

definitions: Response: description: Default API response properties: status: description: Response status `success` or `error` type: string enum: ["success", "error"] error_details: description: Exception message if called type: ["string", "object", "null"] error_message: description: Human readable error message type: ["string", "null"] result: description: Result body type: ["object", "null"] timestamp: description: UTC timestamp in ISO 8601 format type: string required: - status - timestamp - error_details - error_message - result

Se representa como:

Y podemos extenderlo para refinar el esquema personalizado del campo de result :

FooServiceResponse: description: Response for Foo service allOf: - $ref: ''#/definitions/Response'' - properties: result: type: object properties: foo_field: type: integer format: int32 bar_field: type: string required: - result

Y se representará correctamente como:

Tenga en cuenta que allOf es suficiente para que esto funcione y no se utiliza ningún campo discriminator . Esto es bueno, porque funciona y esto es importante, ya que creo que las herramientas podrán generar código sin campo discriminator .


El ejemplo estándar de Swagger 2.0 que ha compartido muestra una relación de composición, específicamente captura una relación de "supertipo / subtipo" es un tipo de "sin embargo, no es un polimorfismo en sí mismo".

Sería si pudieras 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 trabajar fue establecer Propiedades adicionales en true en el objeto base (por ejemplo, Pet), especificar Pet usando la referencia del puntero JSON como esquema de entrada y finalmente copiar y pegar mi objeto de valor Cat JSON en la interfaz de usuario de Swagger. Como las propiedades adicionales están permitidas, Swagger UI generó una carga útil de solicitud de entrada válida.


Todas las respuestas aquí son excelentes ya, pero solo quiero agregar una nota menor sobre composición versus herencia . Según la especificación Swagger / OpenAPI , para implementar la composición , usar la propiedad allOf es suficiente, como señala correctamente @oblalex . Sin embargo, para implementar la herencia , debe usar allOf con discriminator , como en el ejemplo de @ TomaszSętkowski .

Además, encontré algunos ejemplos más de Swagger de composition y inheritance en API Handyman. Son parte de una excelente serie de tutoriales Swagger / OpenAPI de Arnaud Lauret que creo que todos deberían consultar.