Swagger Kalıtımı ve Kompozisyon


82

Benim "basitleştirilmiş" API'mde, tüm yanıtlar temel bir "yanıt" sınıfından türetilir ( miras alınır ). Tepki sınıfı olan oluşan meta ile dolu bir başlık, ve kullanıcı talep çekirdeğin verileri içeren gövdenin. Yanıt (JSON'da), tüm meta veriler ilk "katman" üzerinde olacak ve gövde, "gövde" adı verilen tek bir özellik olacak şekilde düzenlenmiştir.

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)

Bu ilişkiyi aşağıdaki JSON ile havalı olarak tanımlamaya çalıştım:

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

Daha sonra gövde / başlıktan miras alan çeşitli gövde / başlık sınıflarını oluşturarak farklı yanıtlar oluşturmaya ve ardından ilgili başlık / gövde sınıflarından oluşan çocuk yanıt sınıfları oluşturmaya çalışıyorum (altta kaynak kodda gösterilmiştir). Ancak, bunun bir şeyleri yapmanın yanlış yolu olduğundan veya uygulamamın yanlış olduğundan eminim. Swagger 2.0 spesifikasyonunda (aşağıda gösterilmektedir) bir kalıtım örneği bulamadım, ancak bir kompozisyon örneği buldum .

görüntü açıklamasını buraya girin

Bu "ayırt edicinin" oynayacak büyük bir rolü olduğundan oldukça eminim, ancak ne yapmam gerektiğinden emin değilim.

Soru

Birisi bana, tercihen aşağıdaki örnek kodumu "düzelterek" swagger 2.0'da (JSON) kompozisyon + kalıtımın nasıl uygulanacağını gösterebilir mi? Ayrıca, başlıktaki "sonuç" özniteliğinin her zaman "hata" olarak ayarlandığı yanıttan miras alan bir ErrorResponse sınıfı belirtebilseydim harika olurdu.

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

Diyagram Güncellemesi

Ne istediğimi denemek ve açıklığa kavuşturmak için, aşağıdaki tüm yanıtların herhangi bir response_header ve response_body nesneleri kombinasyonu kullanılarak (kompozisyon) tarafından oluşturulan "yanıt" nesnesinin somutlaştırmaları olduğunu göstermeyi amaçlayan çok basit diyagramı oluşturdum. Response_header ve response_body nesneleri, temel response_body sınıfının filename_response_body alt öğesini kullanan bir filename_response durumunda yapılan herhangi bir yanıt nesnesine genişletilebilir ve eklenebilir. Hem hata hem de başarılı yanıtlar "yanıt" nesnesini kullanır.

görüntü açıklamasını buraya girin


1
Beste için bir örnek var ama o kadar kötü ki paylaşmaya değmez. Spesifikasyonunuzun nasıl görünmesi gerektiği üzerinde çalışacağım. Kullanıcı arayüzünün şu anda bunu desteklemediğini, ancak 2.0 için tam destek mevcut olduğunda destekleyeceğini unutmayın.
Ron

1
Ve dalmadan önce bir şey daha var - kompozisyon mu yoksa miras mı arıyorsunuz? Kompozisyon temelde diyor I have the properties of X and my own properties.. Kalıtım bir ilişki olduğunu gösterir X is my parent. I have its properties and my own.. Kalıtım, belirli bir model kümesinin üst model için geçerli olduğunu söylemek istiyorsanız yararlıdır.
Ron

1
Daha doğrusu hem miras kullanımını göstermek için umuyordum ve bu örneğe tek seferde kompozisyon. Açıkçası, birinin kendi başına kolayca kullanabileceğinin farkındayım, ancak bu durumda tüm yanıtlar temel "yanıt" sınıfının çocuklarıdır. Ve yanıt sınıfı, başlık ve gövde olmak üzere diğer iki nesneden "oluşur".
Programcı

2
Net olamamış olabilirim. Kalıtım, kompozisyonun bir uzantısıdır. Miras varsa, kompozisyon da vardır. Kompozisyon varsa, mutlaka miras yoktur. Ayrıca, örnekleminizde "yanıt" modeli hiçbir yerde kullanılmamaktadır. Bunu görmezden gelmeli ve nasıl görünmesi gerektiğini göstermeli miyim?
Ron

ah, miras ve kompozisyon arasındaki ilişkinin farkında değildim. Bu yüzden her ikisini de göstermek için kalıtımı kullanın. Kullanılmayan yanıt modeliyle ilgili olarak, isteğin yanıt verdiği filename_response alt öğesinde "extends" ile birlikte kullanılmalıdır.
Programcı

Yanıtlar:


114

Havalı bir acemi olarak, polimorfizm ve kompozisyon hakkındaki resmi belgeleri anlaşılması kolay bulmuyorum çünkü bir örneği yok . İnternette arama yaptığımda , geçerli olduğunda swagger 1.2'ye atıfta bulunan birçok iyi örnek varextends .

Swagger 2.0 için , bu google grubu aracılığıyla github'daki swagger özellik kaynaklarında iyi bir örnek buldum

Yukarıdaki kaynaklara dayanarak, burada YAML'de geçerli kısa bir kalıtım örneği verilmiştir :

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

Gerçekten teşekkürler! Bu benim için çalışıyor. İçinde editor.swagger.ioküçük bir hata görüyorum: Modeller bölümünde Petmodeli defalarca görüyorum . Bu modellerin içeriği tamam. Sadece isimler yanlış.
schellingerht

@schellingerht editor2.swagger.ioBu sorunu görmeyeceksiniz
Shiplu Mokaddim

Bu şekilde kalıtımı tanımlarken bulduğum tek sorun, petType özelliğinin üretilen sınıfta biraz işe yaramaz olmasıdır. Boş olacak. Ama en azından düşündüğüm gibi sınıf hiyerarşisini oluşturuyor. Teşekkürler!
xarlymg89

Miras json'unu yukarıdaki gibi oluşturmak için, üst ve alt sınıflarınıza şu şekilde açıklama eklemeniz gerekir: @ApiModel (discriminator = "type", subTypes = {Cat.class, Dog.class}) public abstract class Animal {} @ ApiModel (parent = Animal.class) public calss Cat extends Animal {}
Janet

Ayırıcı yalnızca bir Arayüz uyguladığımızda mı kullanılır Pet, nasıl olur da A sınıfı B sınıfını genişletirse, onu da kullanmalı mıyız? Teşekkürler
Bionix1441

23

Tanımı olmasa bile kompozisyonun iyi çalıştığını buldum discriminator.

Örneğin, taban 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

Şu şekilde oluşturulur:

Yanıt görselleştirme

Özel resultalan şemasını iyileştirmek için bunu genişletebiliriz :

  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

Ve şu şekilde doğru şekilde görüntülenecektir:

FooServiceResponse görselleştirme

Unutmayın, bunun allOfçalışması için yeterlidir ve hiçbir discriminatoralan kullanılmaz. Bu iyi, çünkü işe yarıyor ve bu önemli, bence araçlar discriminatoralan olmadan kod üretebilecek .


Ben de kullandım allOf, ama bir şekilde openapi.yamlalt sınıfların süper sınıfın özelliklerini fazladan bir şekilde içerdiğini görüyorum, bu doğru mu?
Bionix1441

9

Buradaki tüm cevaplar zaten mükemmel, ancak kalıtıma karşı kompozisyon hakkında küçük bir not eklemek istiyorum . Göre Havalı / OpenAPI Spec uygulamak için bir bileşim kullanılarak, allOfolarak, özelliği yeterlidir @oblalex doğru işaret . Ancak, kalıtımı uygulamak için @ TomaszSętkowski'nin örneğinde olduğu gibi allOfile kullanmanız gerekir.discriminator .

Aynı zamanda, her ikisi de biraz daha Havalı örnekler bulunan bileşimin ve kalıtım API Handyman de. Herkesin kontrol etmesi gerektiğini düşündüğüm, Arnaud Lauret tarafından yazılan mükemmel Swagger / OpenAPI eğitim serisinin bir parçası .


1
@ İlgili bağlantıların yayınlanması iyi bir başlangıç ​​olsa da, aslında yararlı bir yanıt olması için, bağlantıda bulunacak ilgili metne de atıfta bulunmalısınız. Yalnızca bağlantı yanıtları tavsiye edilmez, çünkü bağlantılar sıklıkla kesilir.
Stijn de Witt

3

Paylaştığınız Swagger 2.0 standart örneği, bir kompozisyon ilişkisini tasvir ediyor, özellikle "bir tür" süper tip / alt tip ilişkisini yakalıyor ancak kendi başına polimorfizm değil.

Pet'in temel tanımına bir girdi parametresi olarak başvurabilir, ardından giriş isteği değeri olarak Cat'i seçebilir veya bir Cat JSON nesnesi girebilir ve bunun Swagger UI için kabul edilebilir olmasını sağlayabilirsiniz.

Bunun doğrudan çalışmasını sağlayamadım.

Çalışabildiğim en iyi şey, temel nesnede (ör. Pet) ek Özellikler'i true olarak ayarlamak, giriş şeması olarak JSON işaretçi referansını kullanarak Pet'i belirtmek ve son olarak Cat JSON değer nesnemi kopyalayıp Swagger UI'ye yapıştırmaktı. Ek özelliklere izin verildiğinden, Swagger UI geçerli bir girdi isteği yükü oluşturdu.


Kablo üzerinden polimorfizm yapmazsınız (veya veri varlıklarınızı açığa çıkarmazsınız) .. çalışması için belirli bir hack'e sıkıca bağlanmak istemediğiniz sürece.
user1496062

Çok biçimlilik, kalıtımla etkinleştirilir, ancak kalıtımı kullanmak için gerekli değildir. Mantıksal olarak, kalıtım bir "bir-bir" ilişkisi iken, kompozisyon bir "bir-bir" ilişkisidir. İkisi arasındaki çizgi, uygulama dili ve etki alanı kullanım durumlarına bağlı olarak bulanık olabilir. Ancak bu başlangıç ​​noktasıdır. Fwiw, ayırıcı, polimorfik tiplerin serileştirilmesini sağlar. Başka yaklaşımlar da vardır (örneğin, Java sınıf adları dahil). Ancak, kabul ediyorum, bunlar küflü olabilir ve taşınabilir olmayabilir. Örneğin, bir python istemcisi Java sınıf adlarıyla ne yapacak?
Charlie Reitzel
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.