Skip to content
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

Output empty object response body as {} type instead of void type #250

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions aspida.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ module.exports = [
outputEachDir: true,
openapi: { inputFile: 'samples/allOf-required.yml' },
},
{
input: 'samples/empty-object-response-body',
outputEachDir: true,
openapi: { inputFile: 'samples/empty-object-response-body.yml' },
},
// {
// input: 'samples/path-at-mark',
// outputEachDir: true,
Expand Down
34 changes: 34 additions & 0 deletions samples/empty-object-response-body.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
openapi: 3.0.0
info:
version: 1.0.0
title: Sample
paths:
/without-additional-properties:
delete:
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
/with-additional-properties-true:
delete:
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
additionalProperties: true
/with-additional-properties-false:
delete:
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
additionalProperties: false
57 changes: 57 additions & 0 deletions samples/empty-object-response-body/$api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type { AspidaClient, BasicHeaders } from 'aspida';
import type { Methods as Methods0 } from './with-additional-properties-false';
import type { Methods as Methods1 } from './with-additional-properties-true';
import type { Methods as Methods2 } from './without-additional-properties';

const api = <T>({ baseURL, fetch }: AspidaClient<T>) => {
const prefix = (baseURL === undefined ? '' : baseURL).replace(/\/$/, '');
const PATH0 = '/with-additional-properties-false';
const PATH1 = '/with-additional-properties-true';
const PATH2 = '/without-additional-properties';
const DELETE = 'DELETE';

return {
with_additional_properties_false: {
/**
* @returns OK
*/
delete: (option?: { config?: T | undefined } | undefined) =>
fetch<Methods0['delete']['resBody'], BasicHeaders, Methods0['delete']['status']>(prefix, PATH0, DELETE, option).json(),
/**
* @returns OK
*/
$delete: (option?: { config?: T | undefined } | undefined) =>
fetch<Methods0['delete']['resBody'], BasicHeaders, Methods0['delete']['status']>(prefix, PATH0, DELETE, option).json().then(r => r.body),
$path: () => `${prefix}${PATH0}`,
},
with_additional_properties_true: {
/**
* @returns OK
*/
delete: (option?: { config?: T | undefined } | undefined) =>
fetch<Methods1['delete']['resBody'], BasicHeaders, Methods1['delete']['status']>(prefix, PATH1, DELETE, option).json(),
/**
* @returns OK
*/
$delete: (option?: { config?: T | undefined } | undefined) =>
fetch<Methods1['delete']['resBody'], BasicHeaders, Methods1['delete']['status']>(prefix, PATH1, DELETE, option).json().then(r => r.body),
$path: () => `${prefix}${PATH1}`,
},
without_additional_properties: {
/**
* @returns OK
*/
delete: (option?: { config?: T | undefined } | undefined) =>
fetch<Methods2['delete']['resBody'], BasicHeaders, Methods2['delete']['status']>(prefix, PATH2, DELETE, option).json(),
/**
* @returns OK
*/
$delete: (option?: { config?: T | undefined } | undefined) =>
fetch<Methods2['delete']['resBody'], BasicHeaders, Methods2['delete']['status']>(prefix, PATH2, DELETE, option).json().then(r => r.body),
$path: () => `${prefix}${PATH2}`,
},
};
};

export type ApiInstance = ReturnType<typeof api>;
export default api;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { AspidaClient, BasicHeaders } from 'aspida';
import type { Methods as Methods0 } from '.';

const api = <T>({ baseURL, fetch }: AspidaClient<T>) => {
const prefix = (baseURL === undefined ? '' : baseURL).replace(/\/$/, '');
const PATH0 = '/with-additional-properties-false';
const DELETE = 'DELETE';

return {
/**
* @returns OK
*/
delete: (option?: { config?: T | undefined } | undefined) =>
fetch<Methods0['delete']['resBody'], BasicHeaders, Methods0['delete']['status']>(prefix, PATH0, DELETE, option).json(),
/**
* @returns OK
*/
$delete: (option?: { config?: T | undefined } | undefined) =>
fetch<Methods0['delete']['resBody'], BasicHeaders, Methods0['delete']['status']>(prefix, PATH0, DELETE, option).json().then(r => r.body),
$path: () => `${prefix}${PATH0}`,
};
};

export type ApiInstance = ReturnType<typeof api>;
export default api;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-disable */
export type Methods = {
delete: {
status: 200

/** OK */
resBody: {
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { AspidaClient, BasicHeaders } from 'aspida';
import type { Methods as Methods0 } from '.';

const api = <T>({ baseURL, fetch }: AspidaClient<T>) => {
const prefix = (baseURL === undefined ? '' : baseURL).replace(/\/$/, '');
const PATH0 = '/with-additional-properties-true';
const DELETE = 'DELETE';

return {
/**
* @returns OK
*/
delete: (option?: { config?: T | undefined } | undefined) =>
fetch<Methods0['delete']['resBody'], BasicHeaders, Methods0['delete']['status']>(prefix, PATH0, DELETE, option).json(),
/**
* @returns OK
*/
$delete: (option?: { config?: T | undefined } | undefined) =>
fetch<Methods0['delete']['resBody'], BasicHeaders, Methods0['delete']['status']>(prefix, PATH0, DELETE, option).json().then(r => r.body),
$path: () => `${prefix}${PATH0}`,
};
};

export type ApiInstance = ReturnType<typeof api>;
export default api;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable */
export type Methods = {
delete: {
status: 200

/** OK */
resBody: {
[key: string]: any
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { AspidaClient, BasicHeaders } from 'aspida';
import type { Methods as Methods0 } from '.';

const api = <T>({ baseURL, fetch }: AspidaClient<T>) => {
const prefix = (baseURL === undefined ? '' : baseURL).replace(/\/$/, '');
const PATH0 = '/without-additional-properties';
const DELETE = 'DELETE';

return {
/**
* @returns OK
*/
delete: (option?: { config?: T | undefined } | undefined) =>
fetch<Methods0['delete']['resBody'], BasicHeaders, Methods0['delete']['status']>(prefix, PATH0, DELETE, option).json(),
/**
* @returns OK
*/
$delete: (option?: { config?: T | undefined } | undefined) =>
fetch<Methods0['delete']['resBody'], BasicHeaders, Methods0['delete']['status']>(prefix, PATH0, DELETE, option).json().then(r => r.body),
$path: () => `${prefix}${PATH0}`,
};
};

export type ApiInstance = ReturnType<typeof api>;
export default api;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-disable */
export type Methods = {
delete: {
status: 200

/** OK */
resBody: {
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export type Methods = {
resBody: {
limit: number
offset: number
data: {
}[]
}
}

Expand Down
2 changes: 2 additions & 0 deletions samples/openapi/api/v3/chats/_chatId@string/items/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export type Methods = {
resBody: {
limit: number
offset: number
data: {
}[]
}
Comment on lines 18 to 23
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この部分のスキーマは以下のようになっていました。

"schema": {
"required": ["data", "limit", "offset"],
"properties": {
"limit": { "type": "number" },
"offset": { "type": "number" },
"data": { "type": "array", "items": { "type": "object" } }
}
}

ここは「レスポンスボディが空オブジェクトだった場合」でなく「レスポンスボディの一部分が空オブジェクトだった場合」ですが、修正後の {}[] の方が正しく型を表しています。

}

Expand Down
4 changes: 2 additions & 2 deletions src/builderUtils/converters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,11 @@ export const schema2value = (
} else if (isArraySchema(schema)) {
isArray = true;
value = schema2value(schema.items);
} else if (schema.properties || schema.additionalProperties) {
} else if (schema.type === 'object' || schema.properties || 'additionalProperties' in schema) {
Copy link

@mikana0918 mikana0918 Jan 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} else if (schema.type === 'object' || schema.properties || 'additionalProperties' in schema) {
} else if (schema.type === 'object' || schema.properties || schema.additionalProperties) {

@mashabow PRの確認遅くなっておりすみません。こちら他の箇所と同様に schema.additionalPropertiesとするのはいかがでしょうか? (それに関して問題等ありそうでしょうか。)もし特段の理由がなければ、ドットでのアクセスのほうが周りのコードと乖離がなく、読む際に迷いもなくなるので、いいかな?と思いました。

value = object2value(schema);
} else if (schema.format === 'binary') {
value = isResponse ? 'Blob' : BINARY_TYPE;
} else if (schema.type !== 'object') {
} else {
value = schema.type
? {
integer: 'number',
Expand Down
Loading