Наследование и композиция
В моем "упрощенном" API все ответы производятся (наследуются) из базового класса "ответ". Класс ответа состоит из заголовка, заполненного метаданными, и тела, содержащего основные данные, запрашиваемые пользователем. Ответ (в JSON) выложен таким образом, что все метаданные находятся на первом "слое", а тело - это один атрибут, называемый "тело" как таковой
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)
Я попытался определить эту связь в swagger со следующим 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"
}
}
}
Затем я пытаюсь создать разные ответы, создав различные классы body/header, которые наследуют от body/header, а затем создадут классы дочерних ответов, которые состоят из соответствующих классов заголовка/тела (показаны в исходном коде внизу). Тем не менее, я уверен, что либо это неправильный способ сделать что-то, либо что моя реализация неверна. Я не смог найти пример наследования в спецификации swagger 2.0 (показано ниже), но нашел пример composition.
![enter image description here]()
Я вполне уверен, что этот "дискриминатор" играет большую роль, но не уверен, что мне нужно делать.
Вопрос
Может ли кто-нибудь показать мне, как предполагается реализовать композицию + наследование в swagger 2.0 (JSON), предпочтительно путем "исправления" моего примера кода ниже. Было бы замечательно, если бы я мог указать класс ErrorResponse, который наследует от ответа, где атрибут "result" в заголовке всегда задан как "ошибка".
{
"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"
}
}
}
}
}
Обновление диаграммы
Чтобы попытаться прояснить, что я хочу, я создал основную диаграмму ниже, которая направлена на то, чтобы показать, что все ответы являются экземплярами объекта ответа, которые были построены (состав), используя любую комбинацию объектов response_header и response_body, Объекты response_header и response_body могут быть расширены и вставлены в любой объект ответа, который выполняется в случае filename_response, который использует дочернее имя filename_response_body базового класса response_body. И ошибки, и успешные ответы используют объект "ответ".
![enter image description here]()
Ответы
Ответ 1
Как новичок в swagger, я не могу найти официальную документацию о полиморфизме и композиции, которую легко раскрыть, потому что в ней отсутствует пример, Когда я искал сеть, есть много хороших примеров, ссылающихся на swagger 1.2, когда extends
был действительным.
Для swagger 2.0 Я нашел хороший пример в источниках спецификаций swagger на github через google group
Основываясь на приведенных выше источниках, вот короткий пример действительного наследования в 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
Ответ 2
Я обнаружил, что композиция прекрасно работает даже без определения discriminator
.
Например, base Response
:
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
Отображается как:
![Визуализация ответа]()
И мы можем расширить его, чтобы уточнить настраиваемую схему поля 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
И он будет правильно отображаться как:
![FooServiceResponse визуализация]()
Обратите внимание, что для этого достаточно allOf
, и не используется поле discriminator
. Это хорошо, потому что это работает, и это важно, как я думаю, инструменты смогут генерировать код без поля discriminator
.
Ответ 3
Все ответы здесь превосходны уже, но я просто хочу добавить небольшую заметку о композиции против наследования. В соответствии с Swagger/OpenAPI Spec для реализации композиции, с использованием свойства allOf
достаточно, поскольку @oblalex правильно указывает. Однако для реализации наследования вам нужно использовать allOf
с discriminator
, как в примере by @TomaszSętkowski.
Кроме того, я нашел еще несколько примеров Swagger как состав, и inheritance в Handican API. Они являются частью отличной учебной серии Swagger/OpenAPI от Арно Лоурета, которую я думаю, что каждый должен проверить.
Ответ 4
В стандартном примере Swagger 2.0, который вы поделили, изображено отношение композиции, в частности, оно фиксирует "является своего рода" отношением типа супер-типа/подтипа ", но оно само по себе не является полиморфизмом.
Было бы, если бы вы могли ссылаться на базовое определение Pet в качестве входного параметра, а затем выбрать Cat или ввести объект Cat JSON в качестве значения для запроса ввода и сделать это приемлемым для пользовательского интерфейса Swagger.
Я не мог заставить это работать напрямую.
Лучшее, что я мог получить, это установить для параметра extraProperties значение true для базового объекта (например, Pet), указать Pet с использованием ссылки на указатель JSON в качестве входной схемы и, наконец, скопировать и вставить объект ценности Cat JSON в интерфейс Swagger. Так как дополнительные свойства разрешены, пользовательский интерфейс Swagger генерирует действительную полезную нагрузку запроса ввода.