redux-toolkit
redux-toolkit copied to clipboard
Codegen - Enum integer values
Running into an issue when I have an enum in my openapi schema.
"SpecialTypes": {
"enum": [
0,
1,
2
],
"type": "integer",
"format": "int32"
},
Here is the stack trace
% yarn openapi
yarn run v1.22.10
$ esr openapi.config.ts && yarn gen-api && yarn gen-schema
Successfully updated openapi config
$ npx @rtk-incubator/[email protected] --hooks ./openapi.config.json --baseQuery ./src/app/apiBaseQuery.ts:apiBaseQueryWithDeauth --file ./src/app/finsense-api.generated.ts
npx: installed 105 in 5.81s
TypeError: s.replace is not a function
at escapeString (/Users/austinfelix/.npm/_npx/61405/lib/node_modules/@rtk-incubator/rtk-query-codegen-openapi/node_modules/typescript/lib/typescript.js:17825:18)
at escapeNonAsciiString (/Users/austinfelix/.npm/_npx/61405/lib/node_modules/@rtk-incubator/rtk-query-codegen-openapi/node_modules/typescript/lib/typescript.js:17830:13)
at Object.getLiteralText (/Users/austinfelix/.npm/_npx/61405/lib/node_modules/@rtk-incubator/rtk-query-codegen-openapi/node_modules/typescript/lib/typescript.js:14695:34)
at getLiteralTextOfNode (/Users/austinfelix/.npm/_npx/61405/lib/node_modules/@rtk-incubator/rtk-query-codegen-openapi/node_modules/typescript/lib/typescript.js:111632:23)
at emitLiteral (/Users/austinfelix/.npm/_npx/61405/lib/node_modules/@rtk-incubator/rtk-query-codegen-openapi/node_modules/typescript/lib/typescript.js:109021:24)
at pipelineEmitWithHintWorker (/Users/austinfelix/.npm/_npx/61405/lib/node_modules/@rtk-incubator/rtk-query-codegen-openapi/node_modules/typescript/lib/typescript.js:108822:32)
at pipelineEmitWithHint (/Users/austinfelix/.npm/_npx/61405/lib/node_modules/@rtk-incubator/rtk-query-codegen-openapi/node_modules/typescript/lib/typescript.js:108448:17)
at pipelineEmitWithComments (/Users/austinfelix/.npm/_npx/61405/lib/node_modules/@rtk-incubator/rtk-query-codegen-openapi/node_modules/typescript/lib/typescript.js:112002:13)
at pipelineEmit (/Users/austinfelix/.npm/_npx/61405/lib/node_modules/@rtk-incubator/rtk-query-codegen-openapi/node_modules/typescript/lib/typescript.js:108388:13)
at emitExpression (/Users/austinfelix/.npm/_npx/61405/lib/node_modules/@rtk-incubator/rtk-query-codegen-openapi/node_modules/typescript/lib/typescript.js:108372:13)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
problem is resolved by manually changing the enum integers into strings like so:
"SpecialTypes": {
"enum": [
"0",
"1",
"2"
],
"type": "integer",
"format": "int32"
},
$ npx @rtk-query/codegen-openapi openapi-config.json
Generating ./src/store/testApi.ts
TypeError: s.replace is not a function
at escapeString (/Users/floatrx/Projects/CORE/support-app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:17825:18)
at escapeNonAsciiString (/Users/floatrx/Projects/CORE/support-app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:17830:13)
at Object.getLiteralText (/Users/floatrx/Projects/CORE/support-app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:14695:34)
at getLiteralTextOfNode (/Users/floatrx/Projects/CORE/support-app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:111651:23)
at emitLiteral (/Users/floatrx/Projects/CORE/support-app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:109040:24)
at pipelineEmitWithHintWorker (/Users/floatrx/Projects/CORE/support-app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108841:32)
at pipelineEmitWithHint (/Users/floatrx/Projects/CORE/support-app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108467:17)
at pipelineEmitWithComments (/Users/floatrx/Projects/CORE/support-app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:112021:13)
at pipelineEmit (/Users/floatrx/Projects/CORE/support-app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108407:13)
at emitExpression (/Users/floatrx/Projects/CORE/support-app/node_modules/@rtk-query/codegen-openapi/node_modules/typescript/lib/typescript.js:108391:13)
same issue -> same fix! 😀
My temporary solution -> "openapi-download"
Simple util script (nodejs)
├── openapi
│ ├── config.ts
│ ├── index.js
│ └── schemas.json
├── package.json
package.json
"scripts": {
"openapi": "node openapi",
},
I used @rtk-incubator/rtk-query-codegen-openapi
instead of @rtk-query/codegen-openapi
-> coz:
npx @rtk-query/codegen-openapi openapi-config.ts
npx: installed 64 in 13.608s
Encountered a TypeScript configfile, but neither esbuild-runner nor ts-node are installed.
Downloader openapi/index.js
// * OpenAPI – Code generator
console.log(`\n* 👻 RTKQ OpenAPI code generator v0.0.1`);
require('dotenv').config(); // load .env
const fs = require('fs');
const path = require('path');
const util = require('util');
const exec = util.promisify(require('child_process').exec);
// Config
const specsUrl = process.env.OPENAPI_URL;
const schemaFile = path.resolve(__dirname, 'schemas.json'); // rewrite mode
const openApiConfig = path.resolve(__dirname, 'config.ts');
console.log('With config', { specsUrl, schemaFile, openApiConfig });
// Step 1
const download = () =>
new Promise(async (resolve, reject) => {
console.log('\nStep 1. Downloading JSON from:', specsUrl);
// Download latest openapi specs
await exec(`curl ${specsUrl} -o ${schemaFile} --silent`, (error, stdout) => {
if (error) {
console.error('Failed. Error: ' + error);
reject(error);
}
console.log('Downloaded');
resolve(require(schemaFile));
});
});
// Step 2
const fixEnums = (openapi) => {
console.log('Step 2. Fix enums... [1,2,3]->["1","2","3"]');
if (!fs.existsSync(schemaFile)) {
return console.error(`File ${schemaFile} not exists`);
}
if (!openapi.components.schemas) {
console.error('Bad file!');
return;
}
for (let scheme in openapi.components.schemas) {
let _enum = openapi.components.schemas[scheme].enum;
const isNumbersOnly = !!_enum?.every((v) => typeof v === 'number'); // all array items are numbers!
if (isNumbersOnly) {
openapi.components.schemas[scheme].enum = _enum.map((v) => String(v));
}
}
return openapi;
};
// Step 3
const save = async (data) => {
try {
await fs.writeFileSync(schemaFile, JSON.stringify(data));
console.log('Success');
} catch (error) {
console.error('File save error', error);
console.error(error);
}
};
// Code generation
// https://redux-toolkit.js.org/rtk-query/usage/code-generation
const generate = async () => {
console.log(
'Step 3. Generate endpoints\nCheck docs: https://redux-toolkit.js.org/rtk-query/usage/code-generation\nConfig:',
openApiConfig,
);
await exec(`npx @rtk-incubator/rtk-query-codegen-openapi ${openApiConfig}`, (error, stdout) => {
if (error) {
console.error('Failed with error ' + error);
}
// console.log(stdout); <-- uncomment for debug
});
};
// Actualize
download().then(fixEnums).then(save).then(generate);
config.ts
import * as fs from 'fs';
import { camelCase } from 'lodash'; // for camelCase
import { ConfigFile } from '@rtk-query/codegen-openapi';
// Paths
const path = require('path');
const basePath = path.resolve(__dirname, '../src/api'); // <-- destination path
const schemaFile = path.resolve(__dirname, 'schemas.json'); // <-- source openapi.json
!fs.existsSync(basePath) && fs.mkdirSync(basePath); // Fix: create folder if not exist
// Imports & relations
const apiFile = '@/store/emptyApi.ts';
const apiImport = 'emptyApi';
// Entities list (sync it manually)
const entities: string[] = [
'Auth',
'Events',
'Profile',
'Customers',
'Orders',
'Files',
'Discounts',
];
// Generate files by endpoint pattern
// Returns object according to docs
// https://redux-toolkit.js.org/rtk-query/usage/code-generation#multiple-output-files
const outputFiles = entities.reduce((acc, name) => {
const entityName = camelCase(name);
const regExFilter = new RegExp(entityName, 'i');
const fileName = `${basePath}/${entityName}.ts`; // name file
return {
...acc,
[fileName]: { filterEndpoints: [regExFilter] },
};
}, {});
// console.log(outputFiles); <-- uncomment for debug
const config: ConfigFile = {
schemaFile,
apiFile,
apiImport,
hooks: true,
outputFiles,
};
export default config;
ps; still waiting for an official fix... 🍻
@floatrx To use it with @rtk-query/codegen-openapi :
await exec(`node ./node_modules/@rtk-query/codegen-openapi/lib/bin/cli.js ${openApiConfig}`, (error, stdout) => {
This is the code which is causing the issue. It's actually in the oazapfts
package, which is a dependency of the RTK codegen, rather than in the RTK codegen itself.
if (schema.enum) {
// enum -> union of literal types
const types = schema.enum.map((s) => {
if (s === null) return cg.keywordType.null;
if (typeof s === "boolean")
return s
? factory.createLiteralTypeNode(
ts.factory.createToken(ts.SyntaxKind.TrueKeyword)
)
: factory.createLiteralTypeNode(
ts.factory.createToken(ts.SyntaxKind.FalseKeyword)
);
return factory.createLiteralTypeNode(factory.createStringLiteral(s));
});
return types.length > 1 ? factory.createUnionTypeNode(types) : types[0];
}
There is a check for null
and boolean
avlues but not for number
. So it calls factory.createStringLiteral(s)
with a number
and fails as this function requires a string
. It seems pretty straight-forward to fix -- I'll see what I can do 🤞.
It turns out that this has already been fixed in the oazapfts
repo. That PR was approved and merged but they haven't put out a new release since then 😞
Would be great to have a new release soon, because of this
I think this should already be fixed with the new version
@GeorchW no its not :( issue persists in v2.2.3
@iliapnmrv
issue persists in v2.2.3
I'm depending on "@reduxjs/toolkit": "^1.9.3"
and it works great for me; probably works in v2.2.3 as well but I haven't tried.
Using https://github.com/reduxjs/redux-toolkit/issues/1788#issuecomment-999086924 as a starting point...
- Remove the fixEnums function call on the last line:
download().then(save).then(generate);
- Modify your config.ts/config.js file to use the enum type flag:
const config = {
...
useEnumType: true,
}
And that's it! Re-run the script to generate your type definitions and it should have proper Typescript enums.