orval
orval copied to clipboard
Enums are parsed to invalid Typescript
What are the steps to reproduce this issue?
-
npm i
this minimal example repo: https://github.com/vonkanehoffen/orval-enum-bug-repro - Run
npm run codegen
What happens?
If you look in generated/model/loginStatus.ts
you'll notice Orval has generated invalid syntax:
export const LoginStatus = {
'0_-_Unknown': 0 - Unknown,
'1_-_Success': 1 - Success,
'2_-_TwoFactorRequiredYubikey': 2 - TwoFactorRequiredYubikey,
'3_-_TwoFactorRequiredGoogleAuthenticator': 3 - TwoFactorRequiredGoogleAuthenticator,
'4_-_InvalidEmailOrPassword': 4 - InvalidEmailOrPassword,
'5_-_InvalidToken': 5 - InvalidToken,
'6_-_InvalidRefreshToken': 6 - InvalidRefreshToken,
'7_-_InvalidTwoFactorCode': 7 - InvalidTwoFactorCode,
} as const;
This is from a valid schema:
components:
schemas:
LoginStatus:
enum:
- 0 - Unknown
- 1 - Success
- 2 - TwoFactorRequiredYubikey
- 3 - TwoFactorRequiredGoogleAuthenticator
- 4 - InvalidEmailOrPassword
- 5 - InvalidToken
- 6 - InvalidRefreshToken
- 7 - InvalidTwoFactorCode
type: integer
format: int32
(this is part of a schema originally generated by Swashbockle in .Net)
What were you expecting to happen?
I'd expect the generation to be something like:
export const LoginStatus = {
'0_-_Unknown': 0 ,
'1_-_Success': 1,
'2_-_TwoFactorRequiredYubikey': 2,
'3_-_TwoFactorRequiredGoogleAuthenticator': 3,
'4_-_InvalidEmailOrPassword': 4,
'5_-_InvalidToken': 5,
'6_-_InvalidRefreshToken': 6,
'7_-_InvalidTwoFactorCode': 7,
} as const;
Any logs, error output, etc?
β¦
Any other comments?
The OpenAPI spec is a bit vague around enum from what I could find. The example passes validation but is there some other way it should look to fix this?
What versions are you using?
Operating System: Masc OS Ventura Package Version: 6.16.0 Browser Version: N/A
A workaround would be to use the x-enumNames
property in your openapi spec to overwrite the enum keys.
You might want to use the transformer feature to add this to your generated spec.
example:
{
"ChainingOperator": {
"description": "The operator used to chain condition",
"enum": ["&&", "||"],
"type": "string",
"x-enumNames": ["AND", "OR"]
}
}
generates:
export const ChainingOperator = {
AND: '&&',
OR: '||',
} as const
@anymaniax This should probably added to the docs
Ah thanks so much @NimmLor ! That works great. I had no idea that x-enumNames
was a thing π
For anyone else needing this, here's the fix with the regenerated code applied to my demo repo:
https://github.com/vonkanehoffen/orval-enum-bug-repro/compare/master...fix/enum
this is also happening to me
@dsthode does your latest fix help with this issue?
@melloware I'm not sure which fix you are talking about, has it been released?
Its not released yet but @dsthode just fixed this: https://github.com/anymaniax/orval/pull/1058
@melloware @dsthode incredible, thank you lots
@zernie maybe you can verify if its fixed or not when 6.21.0 is released and report back?
@melloware sure, ping me when it's ready
6.21.0 is out if you want to try the new setting for enums.
@melloware unfortunately, I have a similar problem with a GetApiAdminAccountsAccountIdPlacesOrderField
, GetApiAdminAccountsAccountIdPlacesOrderType
consts:
/**
* Generated by orval v6.21.0 πΊ
* Do not edit manually.
* Amtta Admin
* OpenAPI spec version: api
*/
export type GetApiAdminAccountsAccountIdPlacesParams = {
/**
* ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌΡΡ
ΡΡΡΠ½ΠΎΡΡΠ΅ΠΉ
*/
limit: number;
/**
* ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΏΡΠΎΠΏΡΡΠ΅Π½Π½ΡΡ
ΡΡΡΠ½ΠΎΡΡΠ΅ΠΉ
*/
offset: number;
/**
* ΠΠΎΠ»Π΅ ΠΏΠΎ ΠΊΠΎΡΠΎΡΠΎΠΌΡ ΡΠΎΡΡΠΈΡΠΎΠ²Π°ΡΡ. ΠΠΎΡΡΡΠΏΠ½ΡΠ΅ ΠΏΠΎΠ»Ρ Π΄Π»Ρ ΡΠΎΡΡΠΈΡΠΎΠ²ΠΊΠΈ - accountId, juridicalPersonId, title, phone, rating, workStatus, takeawayDiscountPercent, promoteStatus, promotedTo, createdAt, updatedAt, id
*/
orderField?: typeof GetApiAdminAccountsAccountIdPlacesOrderField[keyof typeof GetApiAdminAccountsAccountIdPlacesOrderField] ;
// eslint-disable-next-line @typescript-eslint/no-redeclare
export const GetApiAdminAccountsAccountIdPlacesOrderField = { id: 'id',
accountId: 'accountId',
juridicalPersonId: 'juridicalPersonId',
title: 'title',
phone: 'phone',
rating: 'rating',
workStatus: 'workStatus',
promoteStatus: 'promoteStatus',
promotedTo: 'promotedTo',
takeawayDiscountPercent: 'takeawayDiscountPercent',
updatedAt: 'updatedAt',
createdAt: 'createdAt',
} as const;
/**
* Π’ΠΈΠΏ ΡΠΎΡΡΠΈΡΠΎΠ²ΠΊΠΈ ASC ΠΈΠ»ΠΈ DESC
*/
orderType?: typeof GetApiAdminAccountsAccountIdPlacesOrderType[keyof typeof GetApiAdminAccountsAccountIdPlacesOrderType] ;
// eslint-disable-next-line @typescript-eslint/no-redeclare
export const GetApiAdminAccountsAccountIdPlacesOrderType = { ASC: 'ASC',
DESC: 'DESC',
} as const;
};
Relevant swagger:
"get": {
"tags": [
"Places"
],
"description": "ΠΠΎΠ»ΡΡΠΈΡΡ ΡΠΏΠΈΡΠΎΠΊ Places Ρ Π°ΠΊΠΊΠ°ΡΠ½ΡΠ°",
"parameters": [
{
"schema": {
"minimum": 1,
"maximum": 100,
"type": "integer"
},
"in": "query",
"name": "limit",
"required": true,
"description": "ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌΡΡ
ΡΡΡΠ½ΠΎΡΡΠ΅ΠΉ"
},
{
"schema": {
"minimum": 0,
"maximum": 9007199254740991,
"type": "integer"
},
"in": "query",
"name": "offset",
"required": true,
"description": "ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΏΡΠΎΠΏΡΡΠ΅Π½Π½ΡΡ
ΡΡΡΠ½ΠΎΡΡΠ΅ΠΉ"
},
{
"schema": {
"anyOf": [
{
"type": "string",
"enum": [
"id"
]
},
{
"type": "string",
"enum": [
"accountId"
]
},
{
"type": "string",
"enum": [
"juridicalPersonId"
]
},
{
"type": "string",
"enum": [
"title"
]
},
{
"type": "string",
"enum": [
"phone"
]
},
{
"type": "string",
"enum": [
"rating"
]
},
{
"type": "string",
"enum": [
"workStatus"
]
},
{
"type": "string",
"enum": [
"promoteStatus"
]
},
{
"type": "string",
"enum": [
"promotedTo"
]
},
{
"type": "string",
"enum": [
"takeawayDiscountPercent"
]
},
{
"type": "string",
"enum": [
"updatedAt"
]
},
{
"type": "string",
"enum": [
"createdAt"
]
}
]
},
"in": "query",
"name": "orderField",
"required": false,
"description": "ΠΠΎΠ»Π΅ ΠΏΠΎ ΠΊΠΎΡΠΎΡΠΎΠΌΡ ΡΠΎΡΡΠΈΡΠΎΠ²Π°ΡΡ. ΠΠΎΡΡΡΠΏΠ½ΡΠ΅ ΠΏΠΎΠ»Ρ Π΄Π»Ρ ΡΠΎΡΡΠΈΡΠΎΠ²ΠΊΠΈ - accountId, juridicalPersonId, title, phone, rating, workStatus, takeawayDiscountPercent, promoteStatus, promotedTo, createdAt, updatedAt, id"
},
{
"schema": {
"anyOf": [
{
"type": "string",
"enum": [
"ASC"
]
},
{
"type": "string",
"enum": [
"DESC"
]
}
]
},
"in": "query",
"name": "orderType",
"required": false,
"description": "Π’ΠΈΠΏ ΡΠΎΡΡΠΈΡΠΎΠ²ΠΊΠΈ ASC ΠΈΠ»ΠΈ DESC"
},
{
"schema": {
"type": "integer"
},
"in": "path",
"name": "accountId",
"required": true
}
],
"security": [
{
"access_token": []
}
],
"responses": {
"200": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"count": {
"minimum": 0,
"type": "integer"
},
"limit": {
"minimum": 1,
"maximum": 100,
"description": "ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΠΌΡΡ
ΡΡΡΠ½ΠΎΡΡΠ΅ΠΉ",
"type": "integer"
},
"offset": {
"minimum": 0,
"maximum": 9007199254740991,
"description": "ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΏΡΠΎΠΏΡΡΠ΅Π½Π½ΡΡ
ΡΡΡΠ½ΠΎΡΡΠ΅ΠΉ",
"type": "integer"
},
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"accountId": {
"type": "integer"
},
"juridicalPersonId": {
"type": "integer"
},
"title": {
"minLength": 1,
"maxLength": 100,
"description": "ΠΠ°Π·Π²Π°Π½ΠΈΠ΅",
"type": "string"
},
"phone": {
"pattern": "^7[0-9]{10}$",
"type": "string"
},
"cardImageUrl": {
"minLength": 1,
"maxLength": 1024,
"description": "Url ΠΊΠ°ΡΡΠΎΡΠΊΠΈ",
"type": "string"
},
"backgroundImageUrl": {
"description": "Url Π±ΡΠΊΠ³ΡΠ°ΡΠ½Π΄Π°",
"anyOf": [
{
"minLength": 1,
"maxLength": 1024,
"type": "string"
},
{
"type": "null"
}
]
},
"rating": {
"description": "Π Π΅ΠΉΡΠΈΠ½Π³",
"anyOf": [
{
"pattern": "^[0-5]{1}.[0-9]{2}$",
"type": "string"
},
{
"type": "null"
}
]
},
"workStatus": {
"description": "Π‘ΡΠ°ΡΡΡ ΡΠ°Π±ΠΎΡΡ",
"anyOf": [
{
"type": "string",
"enum": [
"closed"
]
},
{
"type": "string",
"enum": [
"temp-closed"
]
},
{
"type": "string",
"enum": [
"open"
]
}
]
},
"promoteStatus": {
"description": "Π‘ΡΠ°ΡΡΡ ΠΏΡΠΎΠ΄Π²ΠΈΠΆΠ΅Π½ΠΈΡ",
"anyOf": [
{
"type": "string",
"enum": [
"new"
]
},
{
"type": "string",
"enum": [
"none"
]
}
]
},
"promotedTo": {
"description": "ΠΠΎ ΠΊΠ°ΠΊΠΎΠ³ΠΎ ΡΠΈΡΠ»Π° Π΄Π΅ΠΉΡΡΠ²ΡΠ΅Ρ ΠΏΡΠΎΠ΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅. Π€ΠΎΡΠΌΠ°Ρ - ISO 8601 date string",
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"type": "null"
}
]
},
"availableDeliveryTypes": {
"minItems": 1,
"type": "array",
"items": {
"description": "Π’ΠΈΠΏ Π΄ΠΎΡΡΠ°Π²ΠΊΠΈ",
"anyOf": [
{
"type": "string",
"enum": [
"takeaway"
]
},
{
"type": "string",
"enum": [
"inhouse"
]
}
]
}
},
"availablePaymentTypes": {
"minItems": 1,
"type": "array",
"items": {
"description": "Π’ΠΈΠΏ ΠΎΠΏΠ»Π°ΡΡ",
"anyOf": [
{
"type": "string",
"enum": [
"card-online"
]
},
{
"type": "string",
"enum": [
"card-courier"
]
},
{
"type": "string",
"enum": [
"mobile-transfer"
]
},
{
"type": "string",
"enum": [
"cash-courier"
]
}
]
}
},
"takeawayDiscountPercent": {
"minimum": 0,
"maximum": 50,
"description": "Π‘ΠΊΠΈΠ΄ΠΊΠ° ΠΏΡΠΈ ΡΠ°ΠΌΠΎΠ²ΡΠ²ΠΎΠ·Π΅, Π² ΠΏΡΠΎΡΠ΅Π½ΡΠ°Ρ
",
"type": "integer"
},
"tags": {
"maxItems": 20,
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"minLength": 1,
"maxLength": 20,
"type": "string"
}
},
"required": [
"id",
"name"
]
}
},
"telegramChatId": {
"description": "ΠΠ ΡΠ΅Π»Π΅Π³ΡΠ°ΠΌ ΡΠ°ΡΠ° Π΄Π»Ρ Π½ΠΎΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΉ",
"anyOf": [
{
"minLength": 1,
"maxLength": 256,
"type": "string"
},
{
"type": "null"
}
]
},
"address": {
"minLength": 1,
"maxLength": 1024,
"description": "ΠΠ΄ΡΠ΅Ρ Π² Π²ΠΈΠ΄Π΅ ΡΡΡΠΎΠΊΠΈ",
"type": "string"
},
"addressPoint": {
"type": "object",
"properties": {
"longitude": {
"minimum": -180,
"maximum": 180,
"description": "ΠΠΎΠ»Π³ΠΎΡΠ°",
"type": "number"
},
"latitude": {
"minimum": -90,
"maximum": 90,
"description": "Π¨ΠΈΡΠΎΡΠ°",
"type": "number"
}
},
"required": [
"longitude",
"latitude"
]
},
"addressFlat": {
"description": "ΠΠ²Π°ΡΡΠΈΡΠ°/ΠΎΡΠΈΡ",
"anyOf": [
{
"minLength": 1,
"maxLength": 256,
"type": "string"
},
{
"type": "null"
}
]
},
"addressDoorcode": {
"description": "ΠΠΎΠΌΠΎΡΠΎΠ½",
"anyOf": [
{
"minLength": 1,
"maxLength": 256,
"type": "string"
},
{
"type": "null"
}
]
},
"addressEntrance": {
"description": "ΠΠΎΠ΄ΡΠ΅Π·Π΄",
"anyOf": [
{
"minLength": 1,
"maxLength": 256,
"type": "string"
},
{
"type": "null"
}
]
},
"addressFloor": {
"description": "ΠΡΠ°ΠΆ",
"anyOf": [
{
"minLength": 1,
"maxLength": 256,
"type": "string"
},
{
"type": "null"
}
]
},
"addressAdditionalInformation": {
"description": "ΠΠΎΠΏ. ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ",
"anyOf": [
{
"minLength": 1,
"maxLength": 256,
"type": "string"
},
{
"type": "null"
}
]
},
"updatedAt": {
"format": "date-time",
"type": "string"
},
"createdAt": {
"format": "date-time",
"type": "string"
}
},
"required": [
"id",
"accountId",
"juridicalPersonId",
"title",
"phone",
"cardImageUrl",
"backgroundImageUrl",
"rating",
"workStatus",
"promoteStatus",
"promotedTo",
"availableDeliveryTypes",
"availablePaymentTypes",
"takeawayDiscountPercent",
"tags",
"telegramChatId",
"address",
"addressPoint",
"addressFlat",
"addressDoorcode",
"addressEntrance",
"addressFloor",
"addressAdditionalInformation",
"updatedAt",
"createdAt"
]
}
}
},
"required": [
"count",
"limit",
"offset",
"data"
]
},
"example": {
"count": 123,
"limit": 1,
"offset": 0,
"data": [
{
"id": 3456,
"accountId": 213,
"workStatus": "closed",
"promoteStatus": "none",
"promotedTo": null,
"rating": null,
"juridicalPersonId": 434,
"title": "Amtta",
"phone": "79997775544",
"cardImageUrl": " /static/images/d0af2028-62e2-4abb-bfca-c021e83bda47",
"backgroundImageUrl": null,
"availableDeliveryTypes": [
"takeaway",
"inhouse"
],
"availablePaymentTypes": [
"cash-courier",
"mobile-transfer",
"card-online",
"card-courier"
],
"takeawayDiscountPercent": 10,
"address": "Π Π΅ΡΠΏ. ΠΠ°Π»ΠΌΡΠΊΠΈΡ, Π³. ΠΠ»ΠΈΡΡΠ°, ΠΏΡΠΎΠ΅Π·Π΄ 11-ΠΉ, Π΄. 8",
"addressPoint": {
"longitude": 44.255008,
"latitude": 46.291193
},
"addressFlat": "33",
"addressDoorcode": "33",
"addressEntrance": null,
"addressFloor": null,
"addressAdditionalInformation": "ΠΡ
ΠΎΠ΄ Ρ ΡΠΎΡΡΠ°",
"tags": [
{
"id": 1,
"name": "ΠΠ°Π»ΠΌΡΡΠΊΠ°Ρ ΠΊΡΡ
Π½Ρ"
},
{
"id": 2,
"name": "Π‘ΡΡΠΈ"
}
],
"updatedAt": "2023-05-04T13:21:59.771Z",
"createdAt": "2023-05-04T13:21:59.771Z",
"telegramChatId": "-4051259643"
}
]
}
}
}
},
"400": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"errorCode": {
"type": "string",
"enum": [
"BAD_REQUEST_ERROR"
]
},
"statusCode": {
"type": "number",
"enum": [
400
]
},
"details": {
"type": "array",
"items": {
"type": "array",
"items": [
{
"description": "Π£ΠΊΠ°Π·Π°ΡΠ΅Π»Ρ JSON Π½Π° ΠΌΠ΅ΡΡΠΎΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π² ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΠ΅ Π΄Π°Π½Π½ΡΡ
(Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, `\"body/1/subProp\"`)",
"type": "string"
},
{
"description": "Π‘ΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ ΠΎΠ± ΠΎΡΠΈΠ±ΠΊΠ΅",
"type": "string"
}
],
"additionalItems": false,
"minItems": 2,
"maxItems": 2
}
},
"requestId": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": [
"errorCode",
"statusCode",
"details",
"requestId",
"message"
]
},
"example": {
"errorCode": "BAD_REQUEST_ERROR",
"statusCode": 400,
"details": [
[
"body",
"must have have required property id"
],
[
"querystring",
"property limit must be greater than 0"
]
],
"requestId": "d0af2028-62e2-4abb-bfca-c021e83bda47",
"message": "Π§Π΅Π»ΠΎΠ²Π΅ΠΊΠΎΡΠΈΡΠ°Π΅ΠΌΠΎΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ ΠΎΠ± ΠΎΡΠΈΠ±ΠΊΠ΅"
}
}
}
},
"401": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"errorCode": {
"type": "string",
"enum": [
"UNAUTHORIZED_ERROR"
]
},
"statusCode": {
"type": "number",
"enum": [
401
]
},
"details": {
"type": "null"
},
"requestId": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": [
"errorCode",
"statusCode",
"details",
"requestId",
"message"
]
},
"example": {
"errorCode": "UNAUTHORIZED_ERROR",
"statusCode": 401,
"details": null,
"requestId": "d0af2028-62e2-4abb-bfca-c021e83bda47",
"message": "Π§Π΅Π»ΠΎΠ²Π΅ΠΊΠΎΡΠΈΡΠ°Π΅ΠΌΠΎΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ ΠΎΠ± ΠΎΡΠΈΠ±ΠΊΠ΅"
}
}
}
},
"403": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"errorCode": {
"type": "string",
"enum": [
"FORBIDDEN_ERROR"
]
},
"statusCode": {
"type": "number",
"enum": [
403
]
},
"details": {
"type": "null"
},
"requestId": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": [
"errorCode",
"statusCode",
"details",
"requestId",
"message"
]
},
"example": {
"errorCode": "FORBIDDEN_ERROR",
"statusCode": 403,
"details": null,
"requestId": "d0af2028-62e2-4abb-bfca-c021e83bda47",
"message": "Π§Π΅Π»ΠΎΠ²Π΅ΠΊΠΎΡΠΈΡΠ°Π΅ΠΌΠΎΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ ΠΎΠ± ΠΎΡΠΈΠ±ΠΊΠ΅"
}
}
}
},
"500": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"errorCode": {
"type": "string",
"enum": [
"INTERNAL_SERVER_ERROR"
]
},
"statusCode": {
"type": "number",
"enum": [
500
]
},
"details": {
"type": "null"
},
"requestId": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": [
"errorCode",
"statusCode",
"details",
"requestId",
"message"
]
},
"example": {
"errorCode": "INTERNAL_SERVER_ERROR",
"statusCode": 500,
"details": null,
"requestId": "d0af2028-62e2-4abb-bfca-c021e83bda47",
"message": "Π§Π΅Π»ΠΎΠ²Π΅ΠΊΠΎΡΠΈΡΠ°Π΅ΠΌΠΎΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ ΠΎΠ± ΠΎΡΠΈΠ±ΠΊΠ΅"
}
}
}
}
}
},
OK thanks for verifying will leave this bug open.
@melloware hey, any updates?:)
I have not looked into this further. PR's are welcome though!
@melloware I've no idea where to begin even. any tips?
well what I do usually grep the source code for what I am looking for like "enum" and work my way backwards to the code that is generating the enums. Then maybe you can figure out what is wrong?
@melloware, the problem seems to happen here:
https://github.com/anymaniax/orval/blob/6d41605a232e6efddecad64f918b2905b443e7fa/packages/core/src/getters/scalar.ts#L33
Json schemas similar to { anyOf: [] }
makes item.type
return undefined
which the getScalar
function treats as a normal object, generating a export type
definition instead of a object field type.