orval icon indicating copy to clipboard operation
orval copied to clipboard

[React query][mutator customInstance] Add QueryFunctionContext argument to useCustomInstance to get signal cancellation and extra parameters

Open raulpuente opened this issue 2 years ago • 6 comments

It would be nice to receive the argument QueryFunctionContext to the useCustomInstance to implement the new way to cancel any request

Mainly i passed the argument: context?: QueryFunctionContext,

Example:

export const useGetBitcoinMasterNodeCollectionHook = () => {
    const getBitcoinMasterNodeCollection = useCustomInstance<GetBitcoinMasterNodeCollection200One | BitcoinMasterNode[]>();

    return (
        params?: GetBitcoinMasterNodeCollectionParams,
        context?: QueryFunctionContext,
    ) => getBitcoinMasterNodeCollection(
        {
            url: `/api/bitcoin_master_nodes`, method: 'get',
            params,
        },
        context
    )
}

export const useGetBitcoinMasterNodeCollection = <TData = AsyncReturnType<ReturnType<typeof useGetBitcoinMasterNodeCollectionHook>>, TError = ErrorType<unknown>>(
    params?: GetBitcoinMasterNodeCollectionParams, options?: { query?: UseQueryOptions<AsyncReturnType<ReturnType<typeof useGetBitcoinMasterNodeCollectionHook>>, TError, TData>, }
): UseQueryResult<TData, TError> & { queryKey: QueryKey } => {

    const {query: queryOptions} = options || {}

    const queryKey = queryOptions?.queryKey ?? getGetBitcoinMasterNodeCollectionQueryKey(params);

    const getBitcoinMasterNodeCollection = useGetBitcoinMasterNodeCollectionHook()

    const queryFn: QueryFunction<AsyncReturnType<ReturnType<typeof useGetBitcoinMasterNodeCollectionHook>>> = (context) => getBitcoinMasterNodeCollection(params, context);

    const query = useQuery<AsyncReturnType<ReturnType<typeof useGetBitcoinMasterNodeCollectionHook>>, TError, TData>(queryKey, queryFn, queryOptions)

    return {
        queryKey,
        ...query
    }
}

With this useCustomInstance example:

export const useCustomInstance = <T>(): ((
    config: AxiosRequestConfig,
    context?: QueryFunctionContext,
) => Promise<T>) => {
    const {
        getAccessTokenSilently,
    } = useAuth0();

    return (
        config: AxiosRequestConfig,
        context?: QueryFunctionContext
    ) => {
        return getAccessTokenSilently()
            .then(token => {
                return Axios({
                    ...config,
                    headers: {
                        Authorization: `Bearer ${token}`,
                        accept: 'application/ld+json'
                    },
                    signal: context?.signal
                })
            })
            .then(({data}) => data);
    }
};

raulpuente avatar Dec 06 '21 12:12 raulpuente

+1 It will be highly appreciated if this is implemented across both hook type and regular custom axios mutator, though I kinda understand that this and SecondArgument in the mutator might clash so it won't be all easy to add this change without "breaking changes". Eagerly waiting for this implementation.

aashishnagpal avatar Mar 24 '22 22:03 aashishnagpal

Looking through the history it existed before but was removed as it broke some others use cases. Would be nice to have configuration parameters for it. I plan to do the react-query hooks by myself to setup mutates, cache invalidations, cache keys etc so using Orval for the types and api calls only.

I made a patch with node-patch to get signal parameter while using axios generator to get it for now. filename patches/@orval+axios+6.12.1.patch

diff --git a/node_modules/@orval/axios/dist/index.js b/node_modules/@orval/axios/dist/index.js
index f9cd86e..99c0703 100644
--- a/node_modules/@orval/axios/dist/index.js
+++ b/node_modules/@orval/axios/dist/index.js
@@ -1,11 +1,11 @@
-"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _core = require('@orval/core');var W=[{exports:[{name:"axios",default:!0,values:!0,syntheticDefaultImport:!0},{name:"AxiosRequestConfig"},{name:"AxiosResponse"}],dependency:"axios"}],c=new Map,O= exports.getAxiosDependencies =e=>[...e?[]:W],z=({headers:e,queryParams:s,operationName:n,response:i,mutator:a,body:o,props:l,verb:r,override:t,formData:G,formUrlEncoded:S},{route:g,context:$})=>{var A,T;let p=(t==null?void 0:t.requestOptions)!==!1,u=(t==null?void 0:t.formData)!==!1,f=(t==null?void 0:t.formUrlEncoded)!==!1,m=!!((T=(A=$.tsconfig)==null?void 0:A.compilerOptions)!=null&&T.exactOptionalPropertyTypes),E=_core.isSyntheticDefaultImportsAllow.call(void 0, $.tsconfig),y=_core.generateFormDataAndUrlEncodedFunction.call(void 0, {formData:G,formUrlEncoded:S,body:o,isFormData:u,isFormUrlEncoded:f}),F=_core.VERBS_WITH_BODY.includes(r);if(a){let h=_core.generateMutatorConfig.call(void 0, {route:g,body:o,headers:e,queryParams:s,response:i,verb:r,isFormData:u,isFormUrlEncoded:f,isBodyVerb:F,hasSignal:!1,isExactOptionalPropertyTypes:m}),q=p?_core.generateMutatorRequestOptions.call(void 0, t==null?void 0:t.requestOptions,a.hasSecondArg):"";c.set(n,C=>`export type ${_core.pascal.call(void 0, n)}Result = NonNullable<Awaited<ReturnType<${C?`ReturnType<typeof ${C}>['${n}']`:`typeof ${n}`}>>>`);let P=a.bodyTypeName&&o.definition?_core.toObjectString.call(void 0, l,"implementation").replace(new RegExp(`(\\w*):\\s?${o.definition}`),`$1: ${a.bodyTypeName}<${o.definition}>`):_core.toObjectString.call(void 0, l,"implementation");return`const ${n} = (
+"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _core = require('@orval/core');var W=[{exports:[{name:"axios",default:!0,values:!0,syntheticDefaultImport:!0},{name:"AxiosRequestConfig"},{name:"AxiosResponse"}],dependency:"axios"}],c=new Map,O= exports.getAxiosDependencies =e=>[...e?[]:W],z=({headers:e,queryParams:s,operationName:n,response:i,mutator:a,body:o,props:l,verb:r,override:t,formData:G,formUrlEncoded:S},{route:g,context:$})=>{var A,T;let p=(t==null?void 0:t.requestOptions)!==!1,u=(t==null?void 0:t.formData)!==!1,f=(t==null?void 0:t.formUrlEncoded)!==!1,m=!!((T=(A=$.tsconfig)==null?void 0:A.compilerOptions)!=null&&T.exactOptionalPropertyTypes),E=_core.isSyntheticDefaultImportsAllow.call(void 0, $.tsconfig),y=_core.generateFormDataAndUrlEncodedFunction.call(void 0, {formData:G,formUrlEncoded:S,body:o,isFormData:u,isFormUrlEncoded:f}),F=_core.VERBS_WITH_BODY.includes(r);if(a){let h=_core.generateMutatorConfig.call(void 0, {route:g,body:o,headers:e,queryParams:s,response:i,verb:r,isFormData:u,isFormUrlEncoded:f,isBodyVerb:F,hasSignal:1,isExactOptionalPropertyTypes:m}),q=p?_core.generateMutatorRequestOptions.call(void 0, t==null?void 0:t.requestOptions,a.hasSecondArg):"";c.set(n,C=>`export type ${_core.pascal.call(void 0, n)}Result = NonNullable<Awaited<ReturnType<${C?`ReturnType<typeof ${C}>['${n}']`:`typeof ${n}`}>>>`);let P=a.bodyTypeName&&o.definition?_core.toObjectString.call(void 0, l,"implementation").replace(new RegExp(`(\\w*):\\s?${o.definition}`),`$1: ${a.bodyTypeName}<${o.definition}>`):_core.toObjectString.call(void 0, l,"implementation");return`const ${n} = (
     ${P}
- ${p&&a.hasSecondArg?`options?: SecondParameter<typeof ${a.name}>,`:""}) => {${y}
+ ${p&&a.hasSecondArg?`options?: SecondParameter<typeof ${a.name}>,`:""} ${r === "get"? "signal?: AbortSignal ":""}\n) => {${y}
       return ${a.name}<${i.definition.success||"unknown"}>(
       ${h},
       ${q});
     }
-  `}let I=_core.generateOptions.call(void 0, {route:g,body:o,headers:e,queryParams:s,response:i,verb:r,requestOptions:t==null?void 0:t.requestOptions,isFormData:u,isFormUrlEncoded:f,isExactOptionalPropertyTypes:m,hasSignal:!1});return c.set(n,()=>`export type ${_core.pascal.call(void 0, n)}Result = AxiosResponse<${i.definition.success||"unknown"}>`),`const ${n} = <TData = AxiosResponse<${i.definition.success||"unknown"}>>(
+  `}let I=_core.generateOptions.call(void 0, {route:g,body:o,headers:e,queryParams:s,response:i,verb:r,requestOptions:t==null?void 0:t.requestOptions,isFormData:u,isFormUrlEncoded:f,isExactOptionalPropertyTypes:m,hasSignal:1});return c.set(n,()=>`export type ${_core.pascal.call(void 0, n)}Result = AxiosResponse<${i.definition.success||"unknown"}>`),`const ${n} = <TData = AxiosResponse<${i.definition.success||"unknown"}>>(
     ${_core.toObjectString.call(void 0, l,"implementation")} ${p?`options?: AxiosRequestConfig
 `:""} ): Promise<TData> => {${y}
     return axios${E?"":".default"}.${r}(${I});

samlof avatar Mar 22 '23 07:03 samlof

Hello @samlof, when I tried, the signal still passed correctly to the useQuery hook. Can you provide an example?

anymaniax avatar Mar 22 '23 08:03 anymaniax

@anymaniax ah the filename has to be patches/@orval+axios+6.12.1.patch. Made a project at https://playcode.io/1335302 that you can export and run npm i and npm run gen for.

Maybe new orval updates break it but very small changes to enable again. node_modules/@orval/axios/dist/index.js both hasSignal values from !1 to 1 and add that ${r === "get"? "signal?: AbortSignal ":""} as in the patch file and follow patch-package to make new one.

samlof avatar Mar 22 '23 14:03 samlof

@samlof if you use a mutator you can pass a second argument to the mutator to pass whatever you want automatically

anymaniax avatar Mar 22 '23 14:03 anymaniax

oh I see the problem

anymaniax avatar Mar 22 '23 14:03 anymaniax