Reputation: 4560
We're trying to create a valid OpenAPI doc that will be used to validate incoming requests performed over an existing web api. The initial openapi doc was build with Swagger and the api is written in net 9.0 Unfortunately, it simply didn't work as expected, so I've decided to write manually.
Ok, this is my current version of the document (it's a simplified scenario, but it's enough to reproduce the issue we're seeing):
openapi: 3.0.1
info:
title: OpenAPI Poc
description: Testes schema OpenAPI
version: v1
servers:
- url: https://localhost:7274
paths:
/api/equipamentos:
put:
tags:
- Equipamentos
summary: Modifica os dados de um equipamento existente.
description: Modifica os dados de um equipamento existente e devolve o seu ID.
requestBody:
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/MsgAtualizacaoComputador'
- $ref: '#/components/schemas/MsgAtualizacaoEquipamentoAtivo'
- $ref: '#/components/schemas/MsgAtualizacaoEquipamentoGenerico'
- $ref: '#/components/schemas/MsgAtualizacaoImpressora'
- $ref: '#/components/schemas/MsgAtualizacaoSoftware'
description: Mensagem que identifica os dados a atualizar no equipamento.
discriminator:
propertyName: $type
mapping:
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoComputador, OpenApiPoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoComputador'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoEquipamentoAtivo, OpenApiPoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoEquipamentoAtivo'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoEquipamentoGenerico, OpenApiPoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoEquipamentoGenerico'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoImpressora, OpenApiPoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoImpressora'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoSoftware, OpenApiPoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoSoftware'
responses:
'200':
description: OK
content:
text/plain:
schema:
$ref: '#/components/schemas/MsgGravacaoEquipamento'
application/json:
schema:
$ref: '#/components/schemas/MsgGravacaoEquipamento'
text/json:
schema:
$ref: '#/components/schemas/MsgGravacaoEquipamento'
'401':
description: Unauthorized
content:
text/plain:
schema:
$ref: '#/components/schemas/ProblemDetails'
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
text/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
'500':
description: Internal Server Error
content:
text/plain:
schema:
$ref: '#/components/schemas/ProblemDetails'
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
text/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
components:
schemas:
DadosGerfipv2:
type: object
properties:
numeroInventario:
type: string
nullable: true
dataImportacaoInventario:
type: string
format: date-time
nullable: true
procedimentoAquisitivo:
type: string
nullable: true
additionalProperties: false
EquipamentoInfo:
type: object
properties:
id:
type: integer
format: int32
version:
type: integer
format: int32
additionalProperties: false
EstadoEquipamento:
enum:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
type: integer
format: int32
MsgAtualizacaoComputador:
allOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamento'
- type: object
properties:
idsEquipamentosAssociar:
type: array
items:
$ref: '#/components/schemas/EquipamentoInfo'
nullable: true
idsEquipamentosCancelar:
type: array
items:
$ref: '#/components/schemas/EquipamentoInfo'
nullable: true
idsEquipamentosPropagar:
type: array
items:
$ref: '#/components/schemas/EquipamentoInfo'
nullable: true
nomeNetBIOS:
type: string
nullable: true
ip:
type: string
nullable: true
processador:
type: string
nullable: true
memoria:
type: string
nullable: true
disco:
type: string
nullable: true
gateway:
type: string
nullable: true
idTipoComputador:
type: integer
format: int32
MsgAtualizacaoEquipamento:
required:
- $type
type: object
properties:
$type:
type: string
id:
type: integer
format: int32
version:
type: integer
format: int32
serialNumber:
type: string
nullable: true
tag:
type: string
nullable: true
modelo:
type: string
nullable: true
username:
type: string
nullable: true
partNumber:
type: string
nullable: true
idFabricante:
type: integer
format: int32
dataAquisicao:
type: string
format: date-time
dataFimGarantia:
type: string
format: date-time
data:
type: string
format: date-time
idFornecedor:
type: integer
format: int32
observacoes:
type: string
nullable: true
estadoEquipamento:
$ref: '#/components/schemas/EstadoEquipamento'
idLocalTrabalho:
type: integer
format: int32
idFuncionario:
type: integer
format: int32
nullable: true
localizacaoArmazem:
type: string
nullable: true
importGuid:
type: string
format: uuid
etiquetaImpressaEm:
type: string
format: date-time
nullable: true
dataRetoma:
type: string
format: date-time
nullable: true
idImportacao:
type: integer
format: int32
discriminator:
propertyName: $type
mapping:
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoComputador, OpenApiPoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoComputador'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoEquipamentoAtivo, OpenApiPoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoEquipamentoAtivo'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoEquipamentoGenerico, OpenApiPoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoEquipamentoGenerico'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoImpressora, OpenApiPoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoImpressora'
Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoSoftware, OpenApiPoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: '#/components/schemas/MsgAtualizacaoSoftware'
MsgAtualizacaoEquipamentoAtivo:
allOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamento'
- type: object
properties:
nome:
type: string
nullable: true
numPortas:
type: integer
format: int32
ip:
type: string
nullable: true
velocidade:
type: string
nullable: true
idTipoEquipamentoAtivo:
type: integer
format: int32
MsgAtualizacaoEquipamentoGenerico:
allOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamento'
- type: object
properties:
idTipoEquipamentoGenerico:
type: integer
format: int32
MsgAtualizacaoImpressora:
allOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamento'
- type: object
properties:
idTipoImpressora:
type: integer
format: int32
ip:
type: string
nullable: true
nomeNetBIOS:
type: string
nullable: true
MsgAtualizacaoSoftware:
allOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamento'
- type: object
properties:
idsEquipamentosAssociar:
type: array
items:
$ref: '#/components/schemas/EquipamentoInfo'
nullable: true
idsEquipamentosCancelar:
type: array
items:
$ref: '#/components/schemas/EquipamentoInfo'
nullable: true
idsEquipamentosPropagar:
type: array
items:
$ref: '#/components/schemas/EquipamentoInfo'
nullable: true
numLicencasAdquiridas:
type: integer
format: int32
nome:
type: string
nullable: true
versao:
type: string
nullable: true
upgrade:
type: boolean
MsgGravacao:
type: object
properties:
id:
type: integer
format: int32
version:
type: integer
format: int32
MsgGravacaoEquipamento:
allOf:
- $ref: '#/components/schemas/MsgGravacao'
- type: object
properties:
tag:
type: string
nullable: true
additionalProperties: false
ProblemDetails:
type: object
properties:
type:
type: string
nullable: true
title:
type: string
nullable: true
status:
type: integer
format: int32
nullable: true
detail:
type: string
nullable: true
instance:
type: string
nullable: true
additionalProperties: {}
I think I've already applied several recommendations I've picked along the way, but it still keeps validating when the request is checked by our firewall. Here's my current test message in JSON which keeps firing the validation error:
{
"$type": "Sra.Assistencias.Dtos.Equipamentos.MsgAtualizacaoEquipamentoGenerico, OpenApiPoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"idTipoEquipamentoGenerico": 3,
"id": 83695,
"version": 6,
"serialNumber": "PWOCWVQD",
"tag": "XPTO_123",
"modelo": "Some model",
"username": "",
"partNumber": "21MR004BPG",
"idFabricante": 153,
"dataAquisicao": "2024-12-16T00:00:00+00:00",
"dataFimGarantia": "2027-12-15T00:00:00+00:00",
"data": "0001-01-01T00:00:00",
"idFornecedor": 57,
"observacoes": "Testing",
"estadoEquipamento": 4,
"idLocalTrabalho": 807,
"idFuncionario": 35112,
"localizacaoArmazem": "",
"importGuid": "00000000-0000-0000-0000-000000000000",
"etiquetaImpressaEm": "2025-01-07T10:27:25.6946923+00:00",
"dataRetoma": null,
"idImportacao": 0
}
I've tested the open doc api against swagger and it seems to be fine. In fact, the messages it generates from the open api doc seem to be the ones expected by the service...however, the firewall keeps firing an error about the request body:
API Validation violation - Request body validation failure - validation error({"oneOf":{"errors":[{},{},{},{},{}],"instanceRef":"#","schemaRef":"#"}}) : Failed to validate schema openapipoc13.yaml
If required, I can also post the c# code for the classes that were used to build the initial open api doc with swagger. They're simple DTO classes. I've removed some of those types from the openapi doc and copied their propertiesinto the schemas that were reinheriting those base types.
Am I missing something? Surelly, I must be doing something wrong, right? Any ideas?
Thanks again.
Upvotes: 0
Views: 46
Reputation: 3307
because your schemas don't have any other constraints except for required: ["$type"]
, they will all pass oneOf
validation, causing an error for matching more than one.
trying adding a required
constraint to all of the second allOf
subschemas to force those to be unique.
Some examples here:
MsgAtualizacaoEquipamentoGenerico:
allOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamento'
- type: object
required:
- idTipoEquipamentoGenerico # << this one
properties:
idTipoEquipamentoGenerico:
type: integer
format: int32
MsgAtualizacaoImpressora:
allOf:
- $ref: '#/components/schemas/MsgAtualizacaoEquipamento'
- type: object
required:
- idTipoImpressora
properties:
idTipoImpressora:
type: integer
format: int32
ip:
type: string
nullable: true
nomeNetBIOS:
type: string
nullable: true
Upvotes: 0