Customize the operationId to be used
Description
The impetus for this request is to see if something similar to the --remove-operation-id-prefix option of openapi-generator could be done.
Since the operationId needs to be unique, I am adding a tag to the operationId ({tag}_get)
However, the generated client wants to remove this tag. I want it to be client.{tag}.get, not client.{tag}.{tag}Get.
Could this package provide some similar functionality?
I thought it would be nice to have various options in services.operationId:
export default defineConfig({
input: './openapi.yml',
output: './client',
services: {
// operationId: boolean | "remove-prefix" | ((operationId: string) => string)
operationId: (operationId: string): string => operationId.split('_')[1],
},
name: 'ApiClient',
});
OAS
openapi: 3.0.0
info:
title: Test
tags:
- name: Tag
components:
schemas:
Tag:
type: object
properties:
id:
type: string
required:
- id
paths:
/api/a:
get:
operationId: Tag_get
responses:
"200":
content:
application/json:
schema:
$ref: "#/components/schemas/Tag"
tags:
- Tag
Stackblitz
https://stackblitz.com/edit/hey-api-example-gtgx6x?file=openapi.yml
This package is very awesome to use. Thank you ๐
I see why you'd want to do this @anchan828, I thought about it before. However, please be aware of the upcoming clients feature. Part of that release will be flattening the services layer so every method will be exported as a separate function. This will make it possible to do tree shaking on the final bundle. Unfortunately, this also means that your function names will have to be truly unique, just like operation IDs. Would you still need to transform operation ID in that case?
@mrlubos Thanks for the details. I understand how it will be updated in the future. Depending on the unique function name, I might want to swap the tag name and order.
Tag_get -> client.tagGet
Tag_get -> transform operationId -> client.getTag
To maintain backward compatibility, conversion will be necessary in one of the tools, as operationId is used in various places like Swagger UI URLs. Especially for released document URLs, if the issue is specific to operationId in openapi_ts, I would prefer to handle the conversion there to avoid changing the URLs if possible.
If I use openapi_ts in a new product in the future, I will use getTag for operationId from the beginning, so the conversion will not be necessary.
I see. We could give you operation ID transformers, hang on!
@mrlubos I want this as well. Duplication sucks :(
export class CategoriesApi {
public static categoriesApiList(): CancelablePromise<CategoriesApiListResponse> {
/// ...
}
public static categoriesApiGet(data: CategoriesApiGetData): CancelablePromise<CategoriesApiGetResponse> {
///...
}
}
What would be a good solution for you @syabro?
@mrlubos ideally in config something like
service: {
methodNameGenarator: (operationId, serviceClassName) => {
const newMethodName = operationId.replace(serviceClassName, '');
return newMethodName.charAt(0).toLowerCase() + newMethodName.slice(1)
}
}
For the record I added npx fix_names.ts after the generation but I think it's fragile...
Are you executing openapi-ts programmatically through Node.js? That would be the way to add support for executable function as you described it... can you share what's your fix_names script?
...
"generate-openapi": "pnpm openapi-ts; pnpm tsx ./src/openapi/fix_names.ts",
...
import * as fs from 'fs'
import * as path from 'path'
import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
function removeClassNameFromMethods(filePath: string) {
const content = fs.readFileSync(filePath, 'utf-8')
const classNameMatch = content.match(/class\s+(\w+)\s*{/)
let updatedContent = content
for (const match of content.matchAll(/class\s+(\w+)\s*{/g)) {
const className = match[1]
const prefix = className.charAt(0).toLowerCase() + className.slice(1)
const pattern = new RegExp(`(public\\s+static\\s+)${prefix}(\\w+)(\\s*\\()`, 'g')
updatedContent = updatedContent.replace(pattern, (fullMatch, p1, p2, p3) => {
const newMethodName = p2.charAt(0).toLowerCase() + p2.slice(1)
return `${p1}${newMethodName}${p3}`
})
console.log(`Removing class name from methods in class ${className}`)
}
fs.writeFileSync(filePath, updatedContent, 'utf-8')
}
const filePath = path.join(__dirname, 'services.gen.ts')
removeClassNameFromMethods(filePath)
Ew. Yeah we will have to improve this ๐งน
@mrlubos should I try to check and make a PR?
Hi, This issue can be resolved by this PR. Should I leave this issue open? https://github.com/hey-api/openapi-ts/pull/663
Have you tried it @anchan828? If it fulfils all your requirements, feel free to close the issue ๐ค
@syabro Can you check if there's anything missing for your use case?
In practice, the operationId passed in the methodNameGenarator is not the actual value, but rather the camel-cased version of it, so I will create an issue as soon as I create a reproduction project, but I think the approach itself is very good. My issue idea will affect everything, and there seems to be a conflict with ParamType.
If there are no comments from anyone in particular, I will close it after waiting a few days.
@mrlubos will check a bit later, thanks!
I will create an issue as soon as I create a reproduction project
Created issue and PR. https://github.com/hey-api/openapi-ts/issues/695 https://github.com/hey-api/openapi-ts/pull/696
I've created the PR, but I don't mind at all if it's not merged because I want to hear your opinions.๐
@Stono check out this thread
Seems legit to me ๐
Let's provide the whole operation to the user, they can pick whichever properties they like. Should be a straightforward migration for you, but let me know if you disagree
Happy to do the migration. The more options you give to the user in a method designed to give them control makes sense.
Appreciate the consideration though :)
I created has been resolved issue now that the PR has been merged. If anyone is still experiencing issues, I think you can create a new one. Thank you, everyone! https://github.com/hey-api/openapi-ts/pull/696