Skip to content

Commit

Permalink
feat(fetch): support explode property (#1604)
Browse files Browse the repository at this point in the history
* fix: properly append array values to `normalizedParams` object

* chore: compress nest of if statement

* feat: reference `explode` property

* fix: remove unnecessary `else if` statement

* chore: refactoring for extract `else if` statements

* chore: remove leading newline

* fix: avoid null error in array

* chore: refactoring to decompose `if` statements

* chore: add using `explode` test case

* fix: avoid error in `parameters` not exsit case

* chore: update sample apps

* chore: ignore `.next` directory

* fix: add `return` to explode array proccess

* fix: normal append is not necessary when only explode

* fix: supports cases where explode is both true/false

---------

Co-authored-by: Paul ter Laak <p.terlaak@innovadis.com>
  • Loading branch information
soartec-lab and Paul ter Laak authored Sep 1, 2024
1 parent b0f6cf6 commit 8e9f8ac
Show file tree
Hide file tree
Showing 13 changed files with 90 additions and 37 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,5 @@ typings/

package-lock.json
tests/generated/**
.turbo
.turbo
.next
55 changes: 47 additions & 8 deletions packages/fetch/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ import {
toObjectString,
generateBodyOptions,
isObject,
resolveRef,
} from '@orval/core';
import {
PathItemObject,
ParameterObject,
ReferenceObject,
} from 'openapi3-ts/oas30';

export const generateRequestFunction = (
{
Expand All @@ -26,7 +32,7 @@ export const generateRequestFunction = (
formUrlEncoded,
override,
}: GeneratorVerbOptions,
{ route }: GeneratorOptions,
{ route, context, pathRoute }: GeneratorOptions,
) => {
const isRequestOptions = override?.requestOptions !== false;
const isFormData = override?.formData !== false;
Expand All @@ -42,18 +48,51 @@ export const generateRequestFunction = (
),
'implementation',
);

const spec = context.specs[context.specKey].paths[pathRoute] as
| PathItemObject
| undefined;
const parameters =
spec?.[verb]?.parameters || ([] as (ParameterObject | ReferenceObject)[]);

const explodeParameters = parameters.filter((parameter) => {
const { schema } = resolveRef<ParameterObject>(parameter, context);

return schema.in === 'query' && schema.explode;
});

const explodeParametersNames = explodeParameters.map((parameter) => {
const { schema } = resolveRef<ParameterObject>(parameter, context);

return schema.name;
});

const explodeArrayImplementation =
explodeParameters.length > 0
? `const explodeParameters = ${JSON.stringify(explodeParametersNames)};
if (value instanceof Array && explodeParameters.includes(key)) {
value.forEach((v) => normalizedParams.append(key, v === null ? 'null' : v.toString()));
return;
}
`
: '';

const isExplodeParametersOnly =
explodeParameters.length === parameters.length;

const nomalParamsImplementation = `if (value !== undefined) {
normalizedParams.append(key, value === null ? 'null' : value.toString())
}`;

const getUrlFnImplementation = `export const ${getUrlFnName} = (${getUrlFnProps}) => {
${
queryParams
? `
const normalizedParams = new URLSearchParams();
? ` const normalizedParams = new URLSearchParams();
Object.entries(params || {}).forEach(([key, value]) => {
if (value === null) {
normalizedParams.append(key, 'null');
} else if (value !== undefined) {
normalizedParams.append(key, value.toString());
}
${explodeArrayImplementation}
${!isExplodeParametersOnly ? nomalParamsImplementation : ''}
});`
: ''
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const getCreatePetsResponseMock = (
): Pet => ({
id: faker.number.int({ min: undefined, max: undefined }),
name: faker.word.sample(),
tag: faker.word.sample(),
...overrideResponse,
});

Expand All @@ -31,6 +32,7 @@ export const getUpdatePetsResponseMock = (
): Pet => ({
id: faker.number.int({ min: undefined, max: undefined }),
name: faker.word.sample(),
tag: faker.word.sample(),
...overrideResponse,
});

Expand All @@ -39,6 +41,7 @@ export const getShowPetByIdResponseMock = (
): Pet => ({
id: faker.number.int({ min: undefined, max: undefined }),
name: faker.word.sample(),
tag: faker.word.sample(),
...overrideResponse,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@ export const getListPetsUrl = (params?: ListPetsParams) => {
const normalizedParams = new URLSearchParams();

Object.entries(params || {}).forEach(([key, value]) => {
if (value === null) {
normalizedParams.append(key, 'null');
} else if (value !== undefined) {
normalizedParams.append(key, value.toString());
if (value !== undefined) {
normalizedParams.append(key, value === null ? 'null' : value.toString());
}
});

Expand Down
5 changes: 5 additions & 0 deletions samples/hono/hono-with-fetch-client/next-app/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
6 changes: 2 additions & 4 deletions samples/next-app-with-fetch/app/gen/pets/pets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,8 @@ export const getListPetsUrl = (params?: ListPetsParams) => {
const normalizedParams = new URLSearchParams();

Object.entries(params || {}).forEach(([key, value]) => {
if (value === null) {
normalizedParams.append(key, 'null');
} else if (value !== undefined) {
normalizedParams.append(key, value.toString());
if (value !== undefined) {
normalizedParams.append(key, value === null ? 'null' : value.toString());
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@ export const getListPetsUrl = (params?: ListPetsParams) => {
const normalizedParams = new URLSearchParams();

Object.entries(params || {}).forEach(([key, value]) => {
if (value === null) {
normalizedParams.append(key, 'null');
} else if (value !== undefined) {
normalizedParams.append(key, value.toString());
if (value !== undefined) {
normalizedParams.append(key, value === null ? 'null' : value.toString());
}
});

Expand Down
6 changes: 2 additions & 4 deletions samples/react-query/custom-fetch/src/gen/pets/pets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,8 @@ export const getListPetsUrl = (params?: ListPetsParams) => {
const normalizedParams = new URLSearchParams();

Object.entries(params || {}).forEach(([key, value]) => {
if (value === null) {
normalizedParams.append(key, 'null');
} else if (value !== undefined) {
normalizedParams.append(key, value.toString());
if (value !== undefined) {
normalizedParams.append(key, value === null ? 'null' : value.toString());
}
});

Expand Down
6 changes: 2 additions & 4 deletions samples/svelte-query/custom-fetch/src/gen/pets/pets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,8 @@ export const getListPetsUrl = (params?: ListPetsParams) => {
const normalizedParams = new URLSearchParams();

Object.entries(params || {}).forEach(([key, value]) => {
if (value === null) {
normalizedParams.append(key, 'null');
} else if (value !== undefined) {
normalizedParams.append(key, value.toString());
if (value !== undefined) {
normalizedParams.append(key, value === null ? 'null' : value.toString());
}
});

Expand Down
6 changes: 2 additions & 4 deletions samples/swr-with-zod/src/gen/endpoints/pets/pets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,8 @@ export const getListPetsUrl = (params?: ListPetsParams) => {
const normalizedParams = new URLSearchParams();

Object.entries(params || {}).forEach(([key, value]) => {
if (value === null) {
normalizedParams.append(key, 'null');
} else if (value !== undefined) {
normalizedParams.append(key, value.toString());
if (value !== undefined) {
normalizedParams.append(key, value === null ? 'null' : value.toString());
}
});

Expand Down
6 changes: 2 additions & 4 deletions samples/vue-query/custom-fetch/src/gen/pets/pets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,8 @@ export const getListPetsUrl = (params?: ListPetsParams) => {
const normalizedParams = new URLSearchParams();

Object.entries(params || {}).forEach(([key, value]) => {
if (value === null) {
normalizedParams.append(key, 'null');
} else if (value !== undefined) {
normalizedParams.append(key, value.toString());
if (value !== undefined) {
normalizedParams.append(key, value === null ? 'null' : value.toString());
}
});

Expand Down
10 changes: 10 additions & 0 deletions tests/configs/fetch.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,14 @@ export default defineConfig({
target: '../specifications/form-url-encoded.yaml',
},
},
parameters: {
output: {
target: '../generated/fetch/parameters/endpoints.ts',
schemas: '../generated/fetch/parameters/model',
client: 'fetch',
},
input: {
target: '../specifications/parameters.yaml',
},
},
});
9 changes: 9 additions & 0 deletions tests/specifications/parameters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ paths:
required: false
schema:
type: boolean
- name: tag
in: query
description: include pets tag
required: false
explode: true
schema:
type: array
items:
type: string
responses:
'200':
description: A paged array of pets
Expand Down

0 comments on commit 8e9f8ac

Please sign in to comment.