@povio/openapi-codegen-cli 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +1 -0
  2. package/dist/commands/generate.d.ts +1 -1
  3. package/dist/generators/const/deps.const.d.ts +6 -0
  4. package/dist/generators/const/endpoints.const.d.ts +1 -0
  5. package/dist/generators/const/zod.const.d.ts +1 -0
  6. package/dist/generators/types/generate.d.ts +1 -0
  7. package/dist/generators/utils/file.utils.d.ts +4 -0
  8. package/dist/generators/utils/generate/generate.endpoints.utils.d.ts +5 -1
  9. package/dist/generators/utils/generate/generate.imports.utils.d.ts +2 -3
  10. package/dist/generators/utils/generate/generate.utils.d.ts +1 -0
  11. package/dist/generators/utils/hbs/hbs.imports.utils.d.ts +1 -0
  12. package/dist/index.js +42 -42
  13. package/dist/sh.js +59 -59
  14. package/package.json +1 -1
  15. package/src/assets/fileAction.ts +47 -0
  16. package/src/assets/rest-client.ts +32 -42
  17. package/src/generators/templates/app-acl.hbs +1 -1
  18. package/src/generators/templates/app-rest-client.hbs +1 -1
  19. package/src/generators/templates/partials/endpoint-config.hbs +6 -0
  20. package/src/generators/templates/partials/endpoint-params.hbs +1 -1
  21. package/src/generators/templates/partials/import.hbs +1 -1
  22. package/src/generators/templates/partials/query-js-docs.hbs +3 -3
  23. package/src/generators/templates/partials/query-keys.hbs +1 -1
  24. package/src/generators/templates/partials/query-use-infinite-query.hbs +4 -4
  25. package/src/generators/templates/partials/query-use-mutation.hbs +14 -2
  26. package/src/generators/templates/partials/query-use-query.hbs +6 -2
  27. package/src/generators/templates/queries.hbs +4 -0
  28. package/src/generators/templates/query-modules.hbs +1 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@povio/openapi-codegen-cli",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "main": "./dist/index.js",
5
5
  "bin": {
6
6
  "openapi-codegen": "./dist/sh.js"
@@ -0,0 +1,47 @@
1
+ import { AxiosResponse, AxiosResponseHeaders } from "axios";
2
+
3
+ export interface FileActionQueryOptions {
4
+ fileName?: string;
5
+ downloadFile?: boolean;
6
+ previewFile?: boolean;
7
+ }
8
+
9
+ export function responseFileAction(res: AxiosResponse<Blob>, options?: FileActionQueryOptions) {
10
+ const fileName = extractFilename(res.headers as AxiosResponseHeaders);
11
+
12
+ fileAction(res.data, { ...options, fileName: options?.fileName ?? fileName });
13
+ }
14
+
15
+ export function fileAction(blob: Blob, options?: FileActionQueryOptions) {
16
+ const url = URL.createObjectURL(blob);
17
+ const a = document.createElement("a");
18
+ a.download = options?.fileName ?? "download";
19
+ a.rel = "noopener";
20
+ a.href = url;
21
+
22
+ if (options?.downloadFile) {
23
+ setTimeout(() => a.dispatchEvent(new MouseEvent("click")), 0);
24
+ }
25
+
26
+ if (options?.previewFile) {
27
+ window.open(url, "_blank");
28
+ }
29
+
30
+ setTimeout(() => URL.revokeObjectURL(url), 40000); // Cleanup after 40s
31
+ }
32
+
33
+ function extractFilename(headers: AxiosResponseHeaders): string | undefined {
34
+ const contentDisposition = Object.keys(headers).find((key) => key.toLowerCase() === "content-disposition");
35
+
36
+ if (!contentDisposition) {
37
+ return undefined;
38
+ }
39
+
40
+ const contentDispositionValue = headers[contentDisposition] as string | undefined;
41
+ if (!contentDispositionValue) {
42
+ return undefined;
43
+ }
44
+
45
+ const filenameMatch = /filename=["']?([^"';]+)/i.exec(contentDispositionValue);
46
+ return filenameMatch ? filenameMatch[1] : undefined;
47
+ }
@@ -6,15 +6,9 @@ interface RequestInfo<ZResDto extends z.ZodRawShape, ResDto, Res> {
6
6
  resSchema: z.ZodEffects<z.ZodObject<ZResDto, "strip", z.ZodTypeAny, ResDto>, Res> | z.ZodSchema<Res>;
7
7
  }
8
8
 
9
- type Method = "get" | "post" | "patch" | "put" | "delete";
10
-
11
- const MethodHasBody: Record<Method, boolean> = {
12
- get: false,
13
- post: true,
14
- patch: true,
15
- put: true,
16
- delete: true,
17
- };
9
+ interface RequestConfig<RawRes extends boolean = false> {
10
+ rawResponse?: RawRes;
11
+ }
18
12
 
19
13
  export class RestClient {
20
14
  private client: AxiosInstance;
@@ -44,64 +38,60 @@ export class RestClient {
44
38
  interceptor.removeInterceptor(this.client);
45
39
  }
46
40
 
47
- public async get<ZResDto extends z.ZodRawShape, ResDto, Res>(
41
+ public async get<ZResDto extends z.ZodRawShape, ResDto, Res, RawRes extends boolean = false>(
48
42
  requestInfo: RequestInfo<ZResDto, ResDto, Res>,
49
43
  url: string,
50
- config?: AxiosRequestConfig,
51
- ): Promise<Res> {
52
- return this.makeRequest(requestInfo, "get", url, undefined, config);
44
+ requestConfig?: AxiosRequestConfig & RequestConfig<RawRes>,
45
+ ): Promise<RawRes extends true ? AxiosResponse<Res> : Res> {
46
+ return this.makeRequest(requestInfo, { ...requestConfig, method: "get", url });
53
47
  }
54
48
 
55
- public async post<ZResDto extends z.ZodRawShape, ResDto, Res>(
49
+ public async post<ZResDto extends z.ZodRawShape, ResDto, Res, RawRes extends boolean = false>(
56
50
  requestInfo: RequestInfo<ZResDto, ResDto, Res>,
57
51
  url: string,
58
52
  data?: any,
59
- config?: AxiosRequestConfig,
60
- ): Promise<Res> {
61
- return this.makeRequest(requestInfo, "post", url, data, config);
53
+ requestConfig?: AxiosRequestConfig & RequestConfig<RawRes>,
54
+ ): Promise<RawRes extends true ? AxiosResponse<Res> : Res> {
55
+ return this.makeRequest(requestInfo, { ...requestConfig, method: "post", url, data });
62
56
  }
63
57
 
64
- public async patch<ZResDto extends z.ZodRawShape, ResDto, Res>(
58
+ public async patch<ZResDto extends z.ZodRawShape, ResDto, Res, RawRes extends boolean = false>(
65
59
  requestInfo: RequestInfo<ZResDto, ResDto, Res>,
66
60
  url: string,
67
61
  data?: any,
68
- config?: AxiosRequestConfig,
69
- ): Promise<Res> {
70
- return this.makeRequest(requestInfo, "patch", url, data, config);
62
+ requestConfig?: AxiosRequestConfig & RequestConfig<RawRes>,
63
+ ): Promise<RawRes extends true ? AxiosResponse<Res> : Res> {
64
+ return this.makeRequest(requestInfo, { ...requestConfig, method: "patch", url, data });
71
65
  }
72
66
 
73
- public async put<ZResDto extends z.ZodRawShape, ResDto, Res>(
67
+ public async put<ZResDto extends z.ZodRawShape, ResDto, Res, RawRes extends boolean = false>(
74
68
  requestInfo: RequestInfo<ZResDto, ResDto, Res>,
75
69
  url: string,
76
70
  data?: any,
77
- config?: AxiosRequestConfig,
78
- ): Promise<Res> {
79
- return this.makeRequest(requestInfo, "put", url, data, config);
71
+ requestConfig?: AxiosRequestConfig & RequestConfig<RawRes>,
72
+ ): Promise<RawRes extends true ? AxiosResponse<Res> : Res> {
73
+ return this.makeRequest(requestInfo, { ...requestConfig, method: "put", url, data });
80
74
  }
81
75
 
82
- public async delete<ZResDto extends z.ZodRawShape, ResDto, Res>(
76
+ public async delete<ZResDto extends z.ZodRawShape, ResDto, Res, RawRes extends boolean = false>(
83
77
  requestInfo: RequestInfo<ZResDto, ResDto, Res>,
84
78
  url: string,
85
79
  data?: any,
86
- config?: AxiosRequestConfig,
87
- ): Promise<Res> {
88
- return this.makeRequest(requestInfo, "delete", url, data, config);
80
+ requestConfig?: AxiosRequestConfig & RequestConfig<RawRes>,
81
+ ): Promise<RawRes extends true ? AxiosResponse<Res> : Res> {
82
+ return this.makeRequest(requestInfo, { ...requestConfig, method: "delete", url, data });
89
83
  }
90
84
 
91
- private async makeRequest<ZResDto extends z.ZodRawShape, ResDto, Res>(
85
+ private async makeRequest<ZResDto extends z.ZodRawShape, ResDto, Res, RawRes extends boolean = false>(
92
86
  requestInfo: RequestInfo<ZResDto, ResDto, Res>,
93
- method: Method,
94
- url: string,
95
- data?: any,
96
- config?: AxiosRequestConfig,
97
- ): Promise<Res> {
98
- let res: AxiosResponse;
99
- if (MethodHasBody[method]) {
100
- res = await this.client[method](url, data, config);
101
- } else {
102
- res = await this.client[method](url, config);
103
- }
87
+ requestConfig: AxiosRequestConfig & RequestConfig<RawRes>,
88
+ ): Promise<RawRes extends true ? AxiosResponse<Res> : Res> {
89
+ const { rawResponse, ...config } = requestConfig;
90
+
91
+ const res = await this.client(config);
92
+
93
+ const resData = requestInfo.resSchema.parse(res.data);
104
94
 
105
- return requestInfo.resSchema.parse(res);
95
+ return (rawResponse ? { ...res, data: resData } : resData) as RawRes extends true ? AxiosResponse<Res> : Res;
106
96
  }
107
97
  }
@@ -10,4 +10,4 @@ export type AppAbilities =
10
10
  {{#if includeNamespace}}{{#each namespaces as | namespace |}} | {{namespace}}.{{../allAbilities}}{{/each}}
11
11
  {{else}}{{#each tags as | tag |}} | {{tagAllAbilitiesName tag}}{{/each}}{{/if}}
12
12
 
13
- export type AppAbility = PureAbility<AppAbilities>;
13
+ export type AppAbility = PureAbility<AppAbilities>;
@@ -4,4 +4,4 @@ export const {{appRestClientName}} = new RestClient({
4
4
  config: {
5
5
  baseURL: "{{baseUrl}}"
6
6
  },
7
- });
7
+ });
@@ -22,4 +22,10 @@
22
22
  {{/each}}
23
23
  },
24
24
  {{/if}}
25
+ {{#if hasBlobResponse}}
26
+ responseType: "blob",
27
+ {{/if}}
28
+ {{#if hasFileDownload}}
29
+ rawResponse: true,
30
+ {{/if}}
25
31
  }
@@ -1 +1 @@
1
- {{#each (endpointParams endpoint extra) as | endpointParam |}}{{endpointParam.name}}{{#unless endpointParam.required}}?{{/unless}}: {{endpointParam.type}}, {{/each}}
1
+ {{#each (endpointParams endpoint options) as | endpointParam |}}{{endpointParam.name}}{{#unless endpointParam.required}}?{{/unless}}: {{endpointParam.type}}, {{/each}}
@@ -1 +1 @@
1
- import { {{commaSeparated import.bindings}} } from "{{import.from}}";
1
+ import {{importNames import.bindings import.defaultImport}} from "{{import.from}}";
@@ -4,7 +4,7 @@
4
4
  * @summary {{addAsteriskAfterNewLine endpoint.summary}}{{/if}}{{#if endpoint.description}}
5
5
  * @description {{addAsteriskAfterNewLine endpoint.description}}{{/if}}{{#if endpoint.acl}}
6
6
  * @permission Requires `{{abilityFunctionName endpoint}}` ability {{/if}}
7
- {{#if (endpointParams endpoint)}}{{#each (endpointParams endpoint infiniteQuery "removePageParam") as | endpointParam |}} * @param { {{endpointParam.type}} } object.{{endpointParam.name}} {{{endpointParamDescription endpointParam}}}
7
+ {{#if (endpointParams endpoint)}}{{#each (endpointParams endpoint infiniteQuery removePageParam=true) as | endpointParam |}} * @param { {{endpointParam.type}} } object.{{endpointParam.name}} {{{endpointParamDescription endpointParam}}}
8
8
  {{/each}}{{/if}} * @param { AppInfiniteQueryOptions } options Infinite query options
9
9
  * @returns { UseInfiniteQueryResult<{{{importedZodSchemaInferedType endpoint.response}}}> } {{endpoint.responseDescription}}
10
10
  * @statusCodes [{{commaSeparated endpoint.responseStatusCodes}}]
@@ -14,8 +14,8 @@
14
14
  * @summary {{addAsteriskAfterNewLine endpoint.summary}}{{/if}}{{#if endpoint.description}}
15
15
  * @description {{addAsteriskAfterNewLine endpoint.description}}{{/if}}{{#if endpoint.acl}}
16
16
  * @permission Requires `{{abilityFunctionName endpoint}}` ability {{/if}}
17
- {{#if (endpointParams endpoint)}}{{#each (endpointParams endpoint) as | endpointParam |}} * @param { {{endpointParam.type}} } {{#if (isQuery ../endpoint)}}object{{else}}mutation{{/if}}.{{endpointParam.name}} {{{endpointParamDescription endpointParam}}}
18
- {{/each}}{{/if}} * @param {{#if (isQuery endpoint)}}{ AppQueryOptions } options Query options{{else}}{ AppMutationOptions{{#if hasInvalidateQueryOptions}} & {{invalidateQueryOptionsType}}{{/if}} } options Mutation options{{/if}}
17
+ {{#if (endpointParams endpoint includeFileParam=true)}}{{#each (endpointParams endpoint includeFileParam=true) as | endpointParam |}} * @param { {{endpointParam.type}} } {{#if (isQuery ../endpoint)}}object{{else}}mutation{{/if}}.{{endpointParam.name}} {{{endpointParamDescription endpointParam}}}
18
+ {{/each}}{{/if}} * @param {{#if (isQuery endpoint)}}{ AppQueryOptions{{#if hasFileActionQueryOptions}} & {{fileActionQueryOptionsType}}{{/if}} } options Query options{{else}}{ AppMutationOptions{{#if hasInvalidateQueryOptions}} & {{invalidateQueryOptionsType}}{{/if}} } options Mutation options{{/if}}
19
19
  * @returns { {{#if (isQuery endpoint)}}UseQueryResult{{else}}UseMutationResult{{/if}}<{{{importedZodSchemaInferedType endpoint.response}}}> } {{endpoint.responseDescription}}
20
20
  * @statusCodes [{{commaSeparated endpoint.responseStatusCodes}}]
21
21
  */{{/if}}
@@ -4,7 +4,7 @@ export const keys = {
4
4
  {{endpointName endpoint}}: ({{{genEndpointParams endpoint}}}) => [...keys.all, "{{endpoint.path}}", {{{endpointArgs endpoint}}}] as const,
5
5
  {{#if ../generateInfiniteQueries}}
6
6
  {{#if (isInfiniteQuery endpoint)}}
7
- {{endpointName endpoint}}Infinite: ({{{genEndpointParams endpoint "removePageParam"}}}) => [...keys.all, "{{endpoint.path}}", "infinite", {{{endpointArgs endpoint "removePageParam"}}}] as const,
7
+ {{endpointName endpoint}}Infinite: ({{{genEndpointParams endpoint removePageParam=true}}}) => [...keys.all, "{{endpoint.path}}", "infinite", {{{endpointArgs endpoint removePageParam=true}}}] as const,
8
8
  {{/if}}
9
9
  {{/if}}
10
10
  {{/each}}
@@ -1,10 +1,10 @@
1
1
  {{! Js docs }}
2
- {{{genQueryJsDocs endpoint "infiniteQuery" }}}
2
+ {{{genQueryJsDocs endpoint infiniteQuery=true}}}
3
3
  {{! Infinite query definition}}
4
- export const {{infiniteQueryName endpoint}} = <TData>({{#if (endpointParams endpoint)}}{ {{{endpointArgs endpoint "removePageParam"}}} }: { {{{genEndpointParams endpoint "removePageParam"}}} }, {{/if}}options?: AppInfiniteQueryOptions<typeof {{importedEndpointName endpoint}}, TData>{{#if hasAxiosRequestConfig}}, {{axiosRequestConfigName}}?: {{axiosRequestConfigType}}{{/if}}) => {
4
+ export const {{infiniteQueryName endpoint}} = <TData>({{#if (endpointParams endpoint)}}{ {{{endpointArgs endpoint removePageParam=true}}} }: { {{{genEndpointParams endpoint removePageParam=true}}} }, {{/if}}options?: AppInfiniteQueryOptions<typeof {{importedEndpointName endpoint}}, TData>{{#if hasAxiosRequestConfig}}, {{axiosRequestConfigName}}?: {{axiosRequestConfigType}}{{/if}}) => {
5
5
  return {{infiniteQueryHook}}({
6
- queryKey: keys.{{endpointName endpoint}}Infinite({{#if (endpointParams endpoint)}}{{{endpointArgs endpoint "removePageParam"}}}{{/if}}),
7
- queryFn: ({ pageParam }) => {{importedEndpointName endpoint}}({{{endpointArgs endpoint "replacePageParam"}}}{{#if hasAxiosRequestConfig}}, {{axiosRequestConfigName}}{{/if}}),
6
+ queryKey: keys.{{endpointName endpoint}}Infinite({{#if (endpointParams endpoint)}}{{{endpointArgs endpoint removePageParam=true}}}{{/if}}),
7
+ queryFn: ({ pageParam }) => {{importedEndpointName endpoint}}({{{endpointArgs endpoint replacePageParam=true}}}{{#if hasAxiosRequestConfig}}, {{axiosRequestConfigName}}{{/if}}),
8
8
  initialPageParam: 1,
9
9
  getNextPageParam: ({ {{pageParamName}}, {{totalItemsName}}, {{limitParamName}}: limitParam }) => {
10
10
  const pageParam = {{pageParamName}} ?? 1;
@@ -1,11 +1,23 @@
1
1
  {{! Js docs }}
2
2
  {{{genQueryJsDocs endpoint}}}
3
3
  {{! Mutation definition}}
4
- export const {{queryName endpoint}} = (options?: AppMutationOptions<typeof {{importedEndpointName endpoint}}, { {{{genEndpointParams endpoint}}} }>{{#if hasInvalidateQueryOptions}} & {{invalidateQueryOptionsType}}{{/if}}{{#if hasAxiosRequestConfig}}, {{axiosRequestConfigName}}?: {{axiosRequestConfigType}}{{/if}}) => {
4
+ export const {{queryName endpoint}} = (options?: AppMutationOptions<typeof {{importedEndpointName endpoint}}, { {{{genEndpointParams endpoint includeFileParam=true}}} }>{{#if hasInvalidateQueryOptions}} & {{invalidateQueryOptionsType}}{{/if}}{{#if hasAxiosRequestConfig}}, {{axiosRequestConfigName}}?: {{axiosRequestConfigType}}{{/if}}) => {
5
5
  {{#if hasInvalidateQueryOptions}} const queryClient = useQueryClient();{{/if}}
6
6
 
7
7
  return {{queryHook}}({
8
- mutationFn: ({{#if (endpointParams endpoint)}} { {{{endpointArgs endpoint}}} } {{/if}}) => {{importedEndpointName endpoint}}({{{endpointArgs endpoint}}}{{#if hasAxiosRequestConfig}}{{#if (endpointArgs endpoint)}}, {{/if}}{{axiosRequestConfigName}}{{/if}}),
8
+ mutationFn: {{#if hasFileUpload}}async {{/if}}({{#if (endpointParams endpoint includeFileParam=true)}} { {{{endpointArgs endpoint includeFileParam=true}}} } {{/if}}) => {{#if hasFileUpload}} {
9
+ const uploadInstructions = await{{/if}} {{importedEndpointName endpoint}}({{{endpointArgs endpoint}}}{{#if hasAxiosRequestConfig}}{{#if (endpointArgs endpoint)}}, {{/if}}{{axiosRequestConfigName}}{{/if}}){{#if hasFileUpload}};
10
+
11
+ if (file && uploadInstructions.url) {
12
+ await axios.put(uploadInstructions.url, file, {
13
+ headers: {
14
+ "Content-Type": file.type,
15
+ },
16
+ });
17
+ }
18
+
19
+ return uploadInstructions;
20
+ }{{/if}},
9
21
  ...options, {{#if hasInvalidateQueryOptions}}
10
22
  onSuccess: (...args) => {
11
23
  {{! Invalidation }}
@@ -1,10 +1,14 @@
1
1
  {{! Js docs }}
2
2
  {{{genQueryJsDocs endpoint}}}
3
3
  {{! Query definition}}
4
- export const {{queryName endpoint}} = <TData>({{#if (endpointParams endpoint)}}{ {{{endpointArgs endpoint}}} }: { {{{genEndpointParams endpoint}}} }, {{/if}}options?: AppQueryOptions<typeof {{importedEndpointName endpoint}}, TData>{{#if hasAxiosRequestConfig}}, {{axiosRequestConfigName}}?: {{axiosRequestConfigType}}{{/if}}) => {
4
+ export const {{queryName endpoint}} = <TData>({{#if (endpointParams endpoint)}}{ {{{endpointArgs endpoint}}} }: { {{{genEndpointParams endpoint}}} }, {{/if}}options?: AppQueryOptions<typeof {{importedEndpointName endpoint}}, TData>{{#if hasFileActionQueryOptions}} & {{fileActionQueryOptionsType}}{{/if}}{{#if hasAxiosRequestConfig}}, {{axiosRequestConfigName}}?: {{axiosRequestConfigType}}{{/if}}) => {
5
5
  return {{queryHook}}({
6
6
  queryKey: keys.{{endpointName endpoint}}({{#if (endpointParams endpoint)}}{{{endpointArgs endpoint}}}{{/if}}),
7
- queryFn: {{#if hasEndpointArgs}}() => {{importedEndpointName endpoint}}({{{endpointArgs endpoint}}}{{#if hasAxiosRequestConfig}}{{#if (endpointArgs endpoint)}}, {{/if}}{{axiosRequestConfigName}}{{/if}}){{else}}{{importedEndpointName endpoint}}{{/if}},
7
+ queryFn: {{#if hasFileDownload}}async {{/if}}{{#if hasQueryFn}}() => {{#if hasFileDownload}} {
8
+ const res = await{{/if}} {{importedEndpointName endpoint}}({{{endpointArgs endpoint}}}{{#if hasAxiosRequestConfig}}{{#if (endpointArgs endpoint)}}, {{/if}}{{axiosRequestConfigName}}{{/if}}){{else}}{{importedEndpointName endpoint}}{{/if}}{{#if hasFileDownload}};
9
+ responseFileAction(res, options);
10
+ return res;
11
+ }{{/if}},
8
12
  ...options,
9
13
  });
10
14
  };
@@ -8,6 +8,10 @@
8
8
  {{#if hasInvalidateQueriesImport}}
9
9
  {{{genImport invalidateQueriesImport}}}
10
10
  {{/if}}
11
+ {{! File action import }}
12
+ {{#if hasFileActionImport}}
13
+ {{{genImport fileActionImport}}}
14
+ {{/if}}
11
15
  {{! React query types import }}
12
16
  {{{genImport queryTypesImport}}}
13
17
  {{! Models import }}
@@ -15,7 +15,4 @@ export type QueriesModuleType = keyof typeof QueriesModuleKeysAll;
15
15
  export const QueriesModule = {
16
16
  {{#if includeNamespace}}{{#each modules as | module |}} {{module.tag}}: {{module.namespace}}.{{../queriesModuleName}}, {{/each}}
17
17
  {{else}}{{#each modules as | module |}} {{module.tag}}: {{tagModuleName module.tag}}, {{/each}}{{/if}}
18
- } satisfies Record<string, QueriesModuleType>;
19
-
20
-
21
-
18
+ } satisfies Record<string, QueriesModuleType>;