New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug: codegen-openapi incorrectly handles multipart/form-data requests #3063
Comments
Any updates on this? |
@Shaker-Pelcro : if there haven't been any comments or releases, there are no updates. There hasn't been any active work on the codegen stuff in several weeks as far as I know. |
If there is no active development on the codegen, what people are using instead? Are there any reliable tools for generating TypeScript code from OpenAPI spec, or do people implement client code by hand? |
@ajaskiewiczpl : to be clear, the current codegen packages work in general. "No active development" does not mean "this code does not work at all" - it just means "the current code exists as-is, and no one is specifically trying to fix bugs or add new features right this second". But, there's only a couple of active Redux maintainers, and Lenz is the only one who's worked on the codegen packages. So, no ETA on when we will have time to try to make changes to those packages. |
I totally get it. I don't blame you, I just wanted to know what are the alternatives, or more specifically - how people deal with generating OpenAPI clients in general. |
Yeah, I know about the situation with multiple codegen PRs being open and unmerged, and I'm really sorry for that. The plan is to not necessarily add own code, but at least get through the PRs that will be open at that time, get them merged and cut a new release. So now would be a good moment to open PRs for outstanding bugs ;) |
Any update on this? My company is currently facing the exact same issue, which is a shame since this workflow works very well for any other use case. |
I often enhance the generated API that must use {
"openapi": "3.0.1",
"info": {
"title": "OpenAPI definition",
"version": "v0"
},
"servers": [
{
"url": "http://localhost:8080/",
"description": "Generated server url"
}
],
"paths": {
"/api/Avatar": {
"post": {
"tags": ["Avatar"],
"requestBody": {
"content": {
"multipart/form-data": {
"schema": {
"required": ["file"],
"type": "object",
"properties": {
"file": {
"type": "string",
"format": "binary"
}
}
},
"encoding": {
"file": {
"style": "form"
}
}
}
}
},
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"type": "string"
}
},
"application/json": {
"schema": {
"type": "string"
}
},
"text/json": {
"schema": {
"type": "string"
}
}
}
}
}
}
}
}
}
Generated: .injectEndpoints({
endpoints: (build) => ({
postApiAvatar: build.mutation<
PostApiAvatarApiResponse,
PostApiAvatarApiArg
>({
query: (queryArg) => ({
url: `/api/Avatar`,
method: 'POST',
body: queryArg.body,
}),
invalidatesTags: ['Avatar'],
}),
}), Enhanced: .enhanceEndpoints({
endpoints: {
postApiAvatar: {
query: (arg) => {
const formData = new FormData();
formData.append('file', arg.body.file);
return {
url: `/api/Avatar`,
method: 'POST',
body: formData,
};
},
},
},
}) However, I believe this should be a temporary measure. |
I'm using |
So Since no efforts has been made in this so far (No pressure on maintainers, I understand how hard it is to do this, and for free nonetheless, thank you!) I've had a solution for this and I want to share it in case it can help anyone or spark a conversation that could improve it. Preface: I made it a standard in my code base that all generated API files must have an enhanced API (using Below is a snippet from our application that handles file uploads. export default generatedAiContentCoderApi.enhanceEndpoints({
endpoints: {
aiContentCoderResponsesUploadFileCreate: {
query: ({ uploadFileCreate }) => {
const formData = new FormData();
formData.append("file", uploadFileCreate.file);
return {
url: "/api/v1/ai_content_coder/responses/upload_file/",
method: "POST",
body: formData,
formData: true,
};
},
invalidatesTags: [],
},
},
}); now what I did is that I use the generated type as it is, and in my enhanced API I override This is, in my opinion, much better than overriding the generated code directly as it won't be overridden when you update the API unlike the other solution. Still, it's not exactly ideal, maintainability is not great:
Let me know your thoughts! |
This helped me for graphql |
@Moe-Hassan-123 and how do you use that enhance api? do you need to export hook from your custom code? |
@Moe-Hassan-123 I manage to get without enchanced endpoints. Just create function handleSubmitImportCsv(e: FormEvent) {
e.preventDefault();
const formData = new FormData();
formData.append("city", "Chicago");
formData.append("industry", "Marketing");
formData.append("csv_file", csv_file);
importCsv({
prospectsImportCsvRequest: formData,
});
} . typescript will complain:
|
Not sure if anyone is interested, but I use a helper function to ensure type safety. It is a bit react-native specific because of the way Blob values are handled, but generally it should work in any environment. Helper: executeMultipartFormDataMutationinterface ArgsBase {
body: Record<string, unknown>
[key: string]: unknown
}
type TransformArgs<T> = {
[P in keyof T]: P extends 'body'
? {
[P2 in keyof T[P]]: T[P][P2] extends Blob
? { uri: string; type: string; name: string }
: T[P][P2]
}
: T[P]
}
/**
* RTK Query OpenAPI codegen doesn't support typing for multipart/form-data requests. This helper function takes the generated mutation
* function signature and properly structures the request
* @see https://github.com/reduxjs/redux-toolkit/issues/1827
*/
export const executeMultipartFormDataMutation = <Args extends ArgsBase, R>(
fn: (args: Args) => R,
args: TransformArgs<Args>
): R => {
const form = new FormData()
Object.entries(args.body).forEach(([k, v]: [string, unknown]) => {
let value
switch (typeof v) {
case 'object':
if (v === null) {
value = ''
} else if ('uri' in v) {
// react-native specific local file handling
value = v as unknown as Blob
} else {
value = JSON.stringify(v)
}
break
case 'string':
case 'bigint':
case 'boolean':
case 'function':
case 'number':
case 'symbol':
value = v.toString()
break
case 'undefined':
default:
value = ''
break
}
form.append(k, value)
})
// @ts-expect-error
return fn({
...args,
body: form,
})
} Usage: dispatch(
executeMultipartFormDataMutation(myApi.endpoints.myEndpoint.initiate, {
body: {
client_id: clientId,
token: token.access_token,
},
}) |
@andrejpavlovic what is |
ok, now the simplest solution I've got so far: to get this submit code: function handleSubmit(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
const formData = new FormData(e.currentTarget);
// @ts-expect-error: rtk query gen
importCsv({ prospectsImportCsvRequest: formData });
} |
OpenAPI spec:
When you generate API using above specification and send a request you will see that the request
content-type
isapplication/json
, but should bemultipart/form-data
. This obviosly results in415 (Unsupported Media Type)
returned from the server.The text was updated successfully, but these errors were encountered: