diff --git a/README.md b/README.md index a38e8a5c..ea46ad09 100644 --- a/README.md +++ b/README.md @@ -639,6 +639,16 @@ interface RestfulReactConfig { customProps?: { base?: string; }; + customGenerator?: (data: { + componentName: string; + verb: string; + route: string; + description: string; + genericsTypes: string; + operation: OperationObject; + paramsInPath: string[]; + paramsTypes: string; + }) => string; }; } ``` @@ -676,6 +686,16 @@ module.exports = { } ``` +##### Custom generator + +To support even more advanced usecases (like a promise base API, mock generator or anything else that can infer from your specs), you can define your own template in `customGenerator`. This function will be call for each route with some useful computed values (see the types above) and the resulted string will be added to the generated file. + +You can see a concrete usage inside the `examples` folder and try yourself in this repository with the following command: +- `yarn build` +- `yarn example:advanced petstore-custom-fetch` + +You can inspect the result inside `/examples/petstoreFromFileSpecWithCustomFetch.tsx` + ## Contributing All contributions are welcome – especially: diff --git a/examples/fetchers.ts b/examples/fetchers.ts new file mode 100644 index 00000000..1253e7c9 --- /dev/null +++ b/examples/fetchers.ts @@ -0,0 +1,76 @@ +import qs from "qs"; + +export interface CustomGetProps< + _TData = any, + _TError = any, + TQueryParams = { + [key: string]: any; + } +> { + queryParams?: TQueryParams; +} + +export const customGet = < + TData = any, + TError = any, + TQueryParams = { + [key: string]: any; + } +>( + path: string, + props: { queryParams?: TQueryParams }, + signal?: RequestInit["signal"], +): Promise => { + let url = path; + if (props.queryParams && Object.keys(props.queryParams).length) { + url += `?${qs.stringify(props.queryParams)}`; + } + return fetch(url, { + headers: { + "content-type": "application/json", + }, + signal, + }).then(res => res.json()); +}; + +export interface CustomMutateProps< + _TData = any, + _TError = any, + TQueryParams = { + [key: string]: any; + }, + TRequestBody = any +> { + body: TRequestBody; + queryParams?: TQueryParams; +} + +export const customMutate = < + TData = any, + TError = any, + TQueryParams = { + [key: string]: any; + }, + TRequestBody = any +>( + method: string, + path: string, + props: { body: TRequestBody; queryParams?: TQueryParams }, + signal?: RequestInit["signal"], +): Promise => { + let url = path; + if (method === "DELETE" && typeof props.body === "string") { + url += `/${props.body}`; + } + if (props.queryParams && Object.keys(props.queryParams).length) { + url += `?${qs.stringify(props.queryParams)}`; + } + return fetch(url, { + method, + body: JSON.stringify(props.body), + headers: { + "content-type": "application/json", + }, + signal, + }).then(res => res.json()); +}; diff --git a/examples/restful-react.config.js b/examples/restful-react.config.js index 0b20ff9a..9c39a126 100644 --- a/examples/restful-react.config.js +++ b/examples/restful-react.config.js @@ -2,6 +2,8 @@ * Example config for `yarn example:advanced` */ +const { camel } = require("case"); + module.exports = { "petstore-file": { file: "examples/petstore.yaml", @@ -15,4 +17,25 @@ module.exports = { base: `"http://my-pet-store.com"`, }, }, + "petstore-custom-fetch": { + file: "examples/petstore.yaml", + output: "examples/petstoreFromFileSpecWithCustomFetch.tsx", + customImport: `import { customGet, customMutate, CustomGetProps, CustomMutateProps } from "./fetchers"`, + customGenerator: ({ componentName, verb, route, description, genericsTypes, paramsInPath, paramsTypes }) => { + const propsType = type => + `Custom${type}Props<${genericsTypes}>${paramsInPath.length ? ` & {${paramsTypes}}` : ""}`; + + return verb === "get" + ? `${description}export const ${camel(componentName)} = (${ + paramsInPath.length ? `{${paramsInPath.join(", ")}, ...props}` : "props" + }: ${propsType( + "Get", + )}, signal?: RequestInit["signal"]) => customGet<${genericsTypes}>(\`http://petstore.swagger.io/v1${route}\`, props, signal);\n\n` + : `${description}export const ${camel(componentName)} = (${ + paramsInPath.length ? `{${paramsInPath.join(", ")}, ...props}` : "props" + }: ${propsType( + "Mutate", + )}, signal?: RequestInit["signal"]) => customMutate<${genericsTypes}>("${verb.toUpperCase()}", \`http://petstore.swagger.io/v1${route}\`, props, signal);\n\n`; + }, + }, };