Escrito por

Analista Desenvolvedora Jr at Bridger
Artigo Heloisa Paiva · Abr. 30 17m read

OpenAPI Suite - Parte 1

Olá Comunidade,

Gostaria de apresentar meu último pacoteOpenAPI-Suite.Este é um conjunto de ferramentas para gerar código ObjectScript a partir deuma especificação OpenAPI versão 3.0..  IEm resumo, estes pacotes permitem:

  • Gerar classes de servidor. É bem parecido com o código gerado por ^%REST mas o valor adicionado é o suporte à versão 3.0.
  • Gerar classes de cliente HTTP.
  • Gerar classes de produção de cliente (business services, business operation, business process, Ens.Request, Ens.Response).
  • Uma interface web para gerar e baixar o código ou gerar e compilar diretamente no servidor.
  • Converter especificações das versões 1.x, 2.x para a versão 3.0.

Visão Geral

O OpenAPI Suite é dividido em vários pacotes e utiliza diferentes bibliotecas da comunidade de desenvolvedores e também serviços REST públicos. Você pode ver no esquema abaixo todos os pacotes que foram desenvolvidos, e as bibliotecas e serviços web utilizados:

Observação: Em caso de problemas ao usar serviços REST públicos, é possível iniciar uma instância Docker do serviço de conversão e validação.

O que cada pacote faz?

O OpenAPI Suite foi projetado em diferentes pacotes para facilitar a manutenção, melhoria e extensão futura. Cada pacote tem um papel. Vamos dar uma olhada!

openapi-common-lib

Isto contém todo o código comum aos outros pacotes. Por exemploopenapi-client-gen e openapi-server-genaceitam a seguinte entrada para uma especificação OpenAPI:

  • URL
  • Caminho de arquivo
  • %Stream.Object 
  • %DynamicObject
  • Formato YAML
  • Formato JSON
  • OpenAPI versão1.x, 2.x, 3.0.x.

No entanto, apenas uma especificação 3.0.x em um %DynamicObject pode ser processada. O código para a transformação está localizado neste pacote. Ele também contém várias utilidades.

swagger-converter-cli

É uma dependência do openapi-common-lib. Este é um cliente HTTP que utiliza o serviço REST públicoconverter.swagger.iopara converter OpenAPI versão 1.x ou 2.x para a versão 3.0.

swagger-validator-cli

É também uma dependência do openapi-common-lib, mesmo que seu nome seja "validator", ele não é usado para validar a especificação. O  converter.swagger.iofornece o serviço "parse" que permite simplificar a estrutura de uma especificação OpenAPI. Por exemplo: criar uma definição para uma "nested object definition" e substituí-la por um "$ref". Isso reduz o número de casos a serem tratados no algoritmo de geração de código.

openapi-client-gen

Este pacote é dedicado à geração de código do lado do cliente para ajudar os desenvolvedores a consumir serviços REST.

Ele inclui um cliente HTTP simples ou um cliente de Produção(business services, process, operation, Production classes). Originalmente criado para suportar OpenAPI 2.x, foi completamente refatorado para suportar a versão 3.x.

openapi-server-gen

O oposto do openapi-client-gen, é dedicado à geração de código do lado do servidor. Não há interesse na versão 2.0 da especificação porque o ^%RESTexiste, mas o objetivo deste pacote é o suporte à versão 3.0.  

openapi-suite

Ele reúne todos os pacotes acima e fornece uma API REST para:

  • Gerar o código e compilar o código na instância IRIS.
  • Gerar código sem compilar para download apenas.

Uma interface web também é fornecida para consumir esta API REST e, assim, explorar as funcionalidades do OpenAPI Suite.

E as bibliotecas?

Aqui estão algumas das bibliotecas existentes no DC que foram úteis neste desenvolvimento:

objectscript-openapi-definition

Uma biblioteca útil para gerar classes de modelo a partir de uma especificação OpenAPI. Esta é uma parte muito importante deste projeto e eu também sou um contribuidor.

ssl-client

Permite criar configurações SSL. Usado principalmente para criar um nome de configuração "DefaultSSL" para requisições HTTPS.

yaml-utils

No caso de especificação em formato YAML, esta biblioteca é usada para converter para o formato JSON. Essencial neste projeto. A propósito, foi desenvolvida inicialmente para testar a especificação YAML com a versão 1 do openapi-client-gen.

io-redirect

Esta é uma das minhas bibliotecas. Ela permite redirecionar a saída de "write" para um stream, arquivo, global ou variável string. É usada pelo serviço REST para manter um rastreamento dos logs. É inspirada nesta postagem da comunidade.

Instalação IPM

Para instalar a suíte, seu melhor amigo é oIPM (zpm)Existem muitos pacotes e dependências, e usar o IPM é definitivamente conveniente.

zpm "install openapi-suite"; opcional; zpm "install swagger-ui"

 

Instalação Docker

Não há nada de especial, este projeto utiliza o intersystems-iris-dev-template.

git clone git@github.com:lscalese/openapi-suite.git
cd openapi-suite
docker-compose up -d

Se você tiver um erro ao iniciar o Iris, talvez seja um problema de permissão no arquivo iris-main.log.

Você pode tentar:

touch iris-main.log && chmod 777 iris-main.log

Observação: Adicionar permissão de leitura e escrita (RW) para o usuário irisowner deve ser suficiente.

Como Usar

O OpenAPI-Suite fornece uma interface web para "Gerar e baixar" ou "Gerar e instalar" código.

A interface está disponível em http://localhost:52796/openapisuite/ui/index.csp(*adapte com o seu número de porta, se necessário).

É muito simples, preencha o formulário:

  1. Nome do pacote da aplicação: este é o pacote usado para as classes geradas. Deve ser um nome de pacote inexistente.
  2. Selecione o que você deseja gerar: Cliente HTTP, Produção Cliente ou Servidor REST.
  3. Selecione o namespace onde o código será gerado. Isso faz sentido apenas se você clicar em "Install On Server"; caso contrário, este campo será ignorado.
  4. Nome da Aplicação Web é opcional e está disponível apenas se você selecionar "Servidor REST" para gerar. Deixe vazio se você não quiser criar uma Aplicação Web relacionada à classe de despacho REST gerada.
  5. O campo Especificação OpenAPI pode ser uma URL apontando para a especificação ou uma cópia/cola da própria especificação (neste caso, a especificação deve estar em formato JSON).

Se você clicar no botão "Download Only", o código será gerado e retornado em um arquivo XML, e então as classes serão excluídas do servidor. O namespace usado para armazenar temporariamente as classes geradas é o namespace onde o OpenAPI-Suite está instalado (por padrão IRISAPP se você usar uma instalação Docker).

No entanto, se você clicar no botão "Install On Server", o código será gerado e compilado, e o servidor retornará uma mensagem JSON com o status da geração/compilação do código e também os logs.

Por padrão, este recurso está desabilitado. Para habilitá-lo, basta abrir um terminal IRIS e:

Set^openapisuite.config("web","enable-install-onserver") = 1

Explore a API REST do OpenAPI-Suite

O formulário CSP utiliza serviços REST disponíveis emhttp://localhost:52796/openapisuite.

Abra o swagger-ui http://localhost:52796/swagger-ui/index.html  e explore http://localhost:52796/openapisuite/_spec 

Este é o primeiro passo para criar uma aplicação front-end mais avançada com o framework Angular.

Gere código programaticamente

Claro, não é obrigatório usar a interface do usuário. Nesta seção, veremos como gerar o código programaticamente e como usar os serviços gerados.

Todos os trechos de código também estão disponíveis na classe dc.openapi.suite.samples.PetStore.

cliente HTTP

Set features("simpleHttpClientOnly") = 1Set sc = ##class(dc.openapi.client.Spec).generateApp("petstoreclient", "https://petstore3.swagger.io/api/v3/openapi.json", .features)

 

O primeiro argumento é o pacote onde as classes serão geradas, portanto, certifique-se de passar um nome de pacote válido. O segundo argumento pode ser uma URL apontando para a especificação, um nome de arquivo, um stream ou um %DynamicObject. "features" é um array, atualmente apenas os seguintes subscritos estão disponíveis:

simpleHttpClientOnly: se for 1, apenas um cliente HTTP simples será gerado; caso contrário, uma produção também será gerada (comportamento padrão).

compile: se for 0, o código gerado não será compilado. Isso pode ser útil se você quiser gerar código apenas para fins de exportação. Por padrão, compile = 1.

Abaixo está um exemplo de como usar o serviço "addPet" com o cliente HTTP recém-gerado:

Set messageRequest = ##class(petstoreclient.requests.addPet).%New()
Set messageRequest.%ContentType = "application/json"Do messageRequest.PetNewObject().%JSONImport({"id":456,"name":"Mittens","photoUrls":["https://static.wikia.nocookie.net/disney/images/c/cb/Profile_-_Mittens.jpg/revision/latest?cb=20200709180903"],"status":"available"})
  
Set httpClient = ##class(petstoreclient.HttpClient).%New("https://petstore3.swagger.io/api/v3","DefaultSSL")
; MessageResponse será uma instância de petstoreclient.responses.addPetSet sc = httpClient.addPet(messageRequest, .messageResponse)
If$$$ISERR(sc) Do$SYSTEM.Status.DisplayError(sc) Quit sc
  
Write !,"Http Status code : ", messageResponse.httpStatusCode,!
Do messageResponse.Pet.%JSONExport()

 

 

Click to show generated classes.

Class petstoreclient.HttpClient Extends%RegisteredObject [ ProcedureBlock ]
{

Parameter SERVER = "https://petstore3.swagger.io/api/v3";Parameter SSLCONFIGURATION = "DefaultSSL";Property HttpRequest [ InitialExpression = {##class(%Net.HttpRequest).%New()} ];Property SSLConfiguration As%String [ InitialExpression = {..#SSLCONFIGURATION} ];Property Server As%String [ InitialExpression = {..#SERVER} ];Property URLComponents [ MultiDimensional ];
Method %OnNew(Server As%String, SSLConfiguration As%String) As%Status
{
    Set:$Data(Server) ..Server = Server
    Set:$Data(SSLConfiguration) ..SSLConfiguration = SSLConfiguration
    Quit..InitializeHttpRequestObject()
}

Method InitializeHttpRequestObject() As%Status
{
    Set..HttpRequest = ##class(%Net.HttpRequest).%New()
    Do##class(%Net.URLParser).Decompose(..Server, .components)
    Set:$Data(components("host"), host) ..HttpRequest.Server = host
    Set:$Data(components("port"), port) ..HttpRequest.Port = port
    Set:$$$LOWER($Get(components("scheme")))="https"..HttpRequest.Https = $$$YES, ..HttpRequest.SSLConfiguration = ..SSLConfigurationMerge:$Data(components) ..URLComponents = components
    Quit$$$OK
}

/// Implementar operationId : addPet/// post /pet
Method addPet(requestMessage As petstoreclient.requests.addPet, Output responseMessage As petstoreclient.responses.addPet = {##class(petstoreclient.responses.addPet).%New()}) As%Status
{
    Set sc = $$$OK$$$QuitOnError(requestMessage.LoadHttpRequestObject(..HttpRequest))
    $$$QuitOnError(..HttpRequest.Send("POST", $Get(..URLComponents("path")) _ requestMessage.%URL))
    $$$QuitOnError(responseMessage.LoadFromResponse(..HttpRequest.HttpResponse, "addPet"))
    Quit sc
}
...
}

 

Class petstoreclient.requests.addPet Extends%RegisteredObject [ ProcedureBlock ]
{

Parameter METHOD = "post";Parameter URL = "/pet";Property%ConsumeAs%String;Property%ContentTypeAs%String;Property%URLAs%String [ InitialExpression = {..#URL} ];/// Use essa propriedade para conteúdo do corpo com content-type = application/json.<br/>/// Use essa propriedade para conteúdo do corpo com  content-type = application/xml.<br/>/// Use essa propriedade para conteúdo do corpo com  content-type = application/x-www-form-urlencoded.Property Pet As petstoreclient.model.Pet;/// Carregue %Net.HttpRequest com esse objeto de propriedade
Method LoadHttpRequestObject(ByRef httpRequest As%Net.HttpRequest) As%Status
{
    Set sc = $$$OKSet httpRequest.ContentType = ..%ContentTypeDo httpRequest.SetHeader("accept", ..%Consume)
    If$Piece($$$LOWER(..%ContentType),";",1) = "application/json"Do..Pet.%JSONExportToStream(httpRequest.EntityBody)
    If$Piece($$$LOWER(..%ContentType),";",1) = "application/xml"Do..Pet.XMLExportToStream(httpRequest.EntityBody)
    If$Piece($$$LOWER(..%ContentType),";",1) = "application/x-www-form-urlencoded" {
        ; Para implementar. Não há código gerado neste caso.$$$ThrowStatus($$$ERROR($$$NotImplemented))
    }
    Quit sc
}

}

 

Class petstoreclient.responses.addPet Extends petstoreclient.responses.GenericResponse [ ProcedureBlock ]
{

/// http status code = 200 content-type = application/xml/// http status code = 200 content-type = application/json/// Property Pet As petstoreclient.model.Pet;/// Implementar operationId : addPet/// post /pet
Method LoadFromResponse(httpResponse As%Net.HttpResponse, caller As%String = "") As%Status
{
    Set sc = $$$OKDo##super(httpResponse, caller)
    If$$$LOWER($Piece(httpResponse.ContentType,";",1))="application/xml",httpResponse.StatusCode = "200" {
        $$$ThrowStatus($$$ERROR($$$NotImplemented))
    }
    If$$$LOWER($Piece(httpResponse.ContentType,";",1))="application/json",httpResponse.StatusCode = "200" {
        Set..Pet = ##class(petstoreclient.model.Pet).%New()
        Do..Pet.%JSONImport(httpResponse.Data)
        Return sc
    }
    Quit sc
}

}

 

Produção de cliente HTTP

Set sc = ##class(dc.openapi.client.Spec).generateApp("petstoreproduction", "https://petstore3.swagger.io/api/v3/openapi.json")

O primeiro argumento é o nome do pacote. Se você testar a geração de código de um cliente HTTP simples e de uma produção de cliente, certifique-se de usar um nome de pacote diferente. O segundo e o terceiro seguem as mesmas regras do cliente HTTP.

Antes de testar, inicie a produção através do portal de gerenciamento usando este comando:

Do##class(Ens.Director).StartProduction("petstoreproduction.Production")

Abaixo está um exemplo de como usar o serviço "addPet", mas desta vez com a produção gerada:

Set messageRequest = ##class(petstoreproduction.requests.addPet).%New()
Set messageRequest.%ContentType = "application/json"Do messageRequest.PetNewObject().%JSONImport({"id":123,"name":"Kitty Galore","photoUrls":["https://www.tippett.com/wp-content/uploads/2017/01/ca2DC049.130.1264.jpg"],"status":"pending"})
; MessageResponse será uma instâncoa de petstoreclient.responses.addPetSet sc = ##class(petstoreproduction.Utils).invokeHostSync("petstoreproduction.bp.SyncProcess", messageRequest, "petstoreproduction.bs.ProxyService", , .messageResponse)
Write !, "Take a look in visual trace (management portal)"If$$$ISERR(sc) Do$SYSTEM.Status.DisplayError(sc)
Write !,"Http Status code : ", messageResponse.httpStatusCode,!
Do messageResponse.Pet.%JSONExport()

Agora, você pode abrir o rastreamento visual para ver os detalhes:

As classes geradas nos pacotes model, requests e responses são bem semelhantes ao código gerado para um cliente HTTP simples. As classes do pacote requests herdam de Ens.Request, e as classes do pacote responses herdam de Ens.Response. A implementação padrão da operação de negócio é muito simples, veja este trecho de código:

Class petstoreproduction.bo.Operation Extends Ens.BusinessOperation [ ProcedureBlock ]
{

Parameter ADAPTER = "EnsLib.HTTP.OutboundAdapter";Property Adapter As EnsLib.HTTP.OutboundAdapter;/// Implementar operationId : addPet/// post /pet
Method addPet(requestMessage As petstoreproduction.requests.addPet, Output responseMessage As petstoreproduction.responses.addPet) As%Status
{
    Set sc = $$$OK, pHttpRequestIn = ##class(%Net.HttpRequest).%New(), responseMessage = ##class(petstoreproduction.responses.addPet).%New()
    $$$QuitOnError(requestMessage.LoadHttpRequestObject(pHttpRequestIn))
    $$$QuitOnError(..Adapter.SendFormDataArray(.pHttpResponse, "post", pHttpRequestIn, , , ..Adapter.URL_requestMessage.%URL))
    $$$QuitOnError(responseMessage.LoadFromResponse(pHttpResponse, "addPet"))
    Quit sc
}
...
}
}

 

Serviço REST HTTP do lado do servidor

Set sc = ##class(dc.openapi.server.ServerAppGenerator).Generate("petstoreserver", "https://petstore3.swagger.io/api/v3/openapi.json", "/petstore/api")

O primeiro argumento é o nome do pacote para gerar as classes. O segundo segue as mesmas regras do cliente HTTP. O terceiro argumento não é obrigatório, mas se presente, uma aplicação web será criada com o nome fornecido (tenha cuidado ao fornecer um nome de aplicação web válido).

A classe petstoreserver.disp (dispatch, despacho, %CSP.REST class) parece um código gerado por ^%REST, realizando muitas verificações para aceitar ou rejeitar a requisição e chamando a implementação do ClassMethod de serviço correspondente em petstoreserver.impl. A principal diferença é o argumento passado para o método de implementação, que é um objeto petstoreserver.requests. Exemplo:

Class petstoreserver.disp Extends%CSP.REST [ ProcedureBlock ]
{

Parameter CHARSET = "utf-8";Parameter CONVERTINPUTSTREAM = 1;Parameter IgnoreWrites = 1;Parameter SpecificationClass = "petstoreserver.Spec";/// Processar request post /petClassMethod addPet() As%Status
{
    Set sc = $$$OKTry{
        Set acceptedMedia = $ListFromString("application/json,application/xml,application/x-www-form-urlencoded")
        If '$ListFind(acceptedMedia,$$$LOWER(%request.ContentType)) {
             Do##class(%REST.Impl).%ReportRESTError(..#HTTP415UNSUPPORTEDMEDIATYPE,$$$ERROR($$$RESTContentType,%request.ContentType)) Quit
        }
        Do##class(%REST.Impl).%SetContentType($Get(%request.CgiEnvs("HTTP_ACCEPT")))
        If '##class(%REST.Impl).%CheckAccepts("application/xml,application/json") Do##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) QuitIf '$isobject(%request.Content) Do##class(%REST.Impl).%ReportRESTError(..#HTTP400BADREQUEST,$$$ERROR($$$RESTRequired,"body")) QuitSet requestMessage = ##class(petstoreserver.requests.addPet).%New()
        Do requestMessage.LoadFromRequest(%request)
        Set scValidateRequest = requestMessage.RequestValidate()
        If$$$ISERR(scValidateRequest) Do##class(%REST.Impl).%ReportRESTError(..#HTTP400BADREQUEST,$$$ERROR(5001,"Invalid requestMessage object.")) QuitSet response = ##class(petstoreserver.impl).addPet(requestMessage)
        Do##class(petstoreserver.impl).%WriteResponse(response)
    } Catch(ex) {
        Do##class(%REST.Impl).%ReportRESTError(..#HTTP500INTERNALSERVERERROR,ex.AsStatus(),$parameter("petstoreserver.impl","ExposeServerExceptions"))
    }
    Quit sc
}
...
}

Como você pode ver, a classe de despacho chama “LoadFromRequest” e “RequestValidate” antes de invocar o método de implementação. Esses métodos possuem uma implementação padrão, mas o gerador de código não consegue cobrir todos os casos. Atualmente, os casos mais comuns são tratados automaticamente como parâmetros em "query" (consulta), "headers" (cabeçalhos), "path" (caminho) e no corpo (body) com os tipos de conteúdo “application/json”, “application/octet-stream” ou “multipart/form-data”. O desenvolvedor precisa verificar a implementação para checar/completar se necessário (por padrão, o gerador de código define $$$ThrowStatus($$$ERROR($$$NotImplemented))para casos não suportados).

 

Exemplo de classe de requisição:

Class petstoreserver.requests.addPet Extends%RegisteredObject [ ProcedureBlock ]
{

Parameter METHOD = "post";Parameter URL = "/pet";Property%ConsumeAs%String;Property%ContentTypeAs%String;Property%URLAs%String [ InitialExpression = {..#URL} ];/// Use essa propriedade para conteúdo de corpo com content-type = application/json.<br/>/// Use essa propriedade para conteúdo de corpo com content-type = application/xml.<br/>/// Use essa propriedade para conteúdo de corpo com content-type = application/x-www-form-urlencoded.Property Pet As petstoreserver.model.Pet;/// Carregue propriedades de objeto do objeto %CSP.Request.
Method LoadFromRequest(request As%CSP.Request = {%request}) As%Status
{
    Set sc = $$$OKSet ..%ContentType = $Piece(request.ContentType, ";", 1)
    If ..%ContentType = "application/json"{
        Do..PetNewObject().%JSONImport(request.Content)
    }
    If ..%ContentType = "application/xml" {
        ; A implementar. Não há geração de código ainda para este caso.$$$ThrowStatus($$$ERROR($$$NotImplemented))
    }
    If ..%ContentType = "application/x-www-form-urlencoded" {
        ; A implementar. Não há geração de código ainda para este caso.$$$ThrowStatus($$$ERROR($$$NotImplemented))
    }
    Quit sc
}

/// Carregue propriedades de objeto do objeto %CSP.Request.
Method RequestValidate() As%Status
{
    Set sc = $$$OK$$$QuitOnError(..%ValidateObject())
    If ''$ListFind($ListFromString("application/json,application/xml,application/x-www-form-urlencoded"), ..%ContentType) {
        Quit:..Pet=""$$$ERROR(5659, "Pet")
    }
    If$IsObject(..Pet) $$$QuitOnError(..Pet.%ValidateObject())
    Quit sc
}

}

 

Assim como no uso de ^%REST, a classe "petstoreserver.impl" contém todos os métodos relacionados aos serviços que o desenvolvedor precisa implementar.

Class petstoreserver.impl Extends%REST.Impl [ ProcedureBlock ]
{

Parameter ExposeServerExceptions = 1;/// Implementação do serviço para post /petClassMethod addPet(messageRequest As petstoreserver.requests.addPet) As%Status
{
    ; Implemente seu serviço aqui; Retorne {}$$$ThrowStatus($$$ERROR($$$NotImplemented))
}

...
}

 

Breve descrição dos pacotes gerados:

Nome do Pacote \ Nome da Classe

Tipo

Descrição

petstoreclient.model


petstoreproduction.model

Client-side e server-side

Ele contém todos os modelos. Essas classes estendem %JSON.Adaptor para facilitar o carregamento de objetos a partir de JSON.

Se uma produção for gerada, essas classes também estenderão %Persistent.

petstoreclient.requests


petstoreproduction.requests

Client-side e server-side

Objeto usado para inicializar facilmente %Net.HttpRequest. Existe uma classe por operação definida na especificação.

Se a produção for gerada, essas classes estenderão Ens.Request.

Nota: A implementação desta classe é diferente se for gerada para propósitos do lado do servidor ou do lado do cliente. No caso do lado do cliente, todas as classes contêm um método "LoadHttpRequestObject" permitindo carregar um "%Net.HttpRequest" a partir das propriedades desta classe.

Se as classes forem geradas para propósitos do lado do servidor, cada classe contém um método "LoadFromRequest" para carregar a instância a partir do objeto "%request".

petstoreclient.responses


petstoreproduction.responses

Client-side e server-side

É o oposto de petstoreclient.requests. Ele permite manipular a resposta de um %Net.HttpRequest.

Se a produção for gerada, essas classes estenderão Ens.Response.

petstoreclient.HttpClient

Client-side

Contém todos os métodos para executar requisições HTTP; existe um método por operação definida na especificação OpenAPI.

petstoreproduction. bo.Operation

Client-side

A classe de Operação possui um método por operação definida na especificação OpenAPI.

petstoreproduction.bp

Client-side

Dois processos de negócio padrão são definidos: síncrono e assíncrono.

petstoreproduction.bs

Client-side

Contém todos os serviços de negócio vazios para implementar.

petstoreproduction.Production

Client-side

Configuração de produção.

petstoreserver.disp

Server-side

Classe de despacho %CSP.REST.

petstoreserver.Spec

Server-side

Esta classe contém a especificação OpenAPI em um bloco XData.

petstoreserver.impl

Server-side

Ele contém todos os métodos vazios relacionados às operações definidas na especificação OpenAPI. Esta é a classe (que estende %REST.Impl) onde os desenvolvedores precisam implementar os serviços.

Status de desenvolvimento

O OpenAPI-Suite ainda é um produto muito novo e precisa ser mais testado e então aprimorado. O suporte ao OpenAPI 3 é parcial, mais possibilidades poderiam ser suportadas.

Os testes foram realizados com a especificação públicahttps://petstore3.swagger.io/api/v3/openapi.json e outras duas relativamente simples. Obviamente, isso não é suficiente para cobrir todos os casos. Se você tiver alguma especificação para compartilhar, ficaria feliz em usá-las para meus testes.

Acredito que a base do projeto é boa e ele pode evoluir facilmente, por exemplo, ser estendido para suportarAsyncAPI.

Não hesite em deixar feedback.

Espero que você goste desta aplicação e que ela mereça seu apoio para o concurso de ferramentas para desenvolvedores.

Obrigado por ler.