fix: swagger recursive type not working

This commit is contained in:
Methapon2001 2024-01-17 11:46:14 +07:00
parent ce22f77b32
commit c1eaeb5c64
No known key found for this signature in database
GPG key ID: 849924FEF46BD132
4 changed files with 114 additions and 57 deletions

View file

@ -1,9 +1,10 @@
import { Body, Controller, Post, Route, Security, SuccessResponse, Tags } from "tsoa"; import { Body, Controller, Post, Route, Security, SuccessResponse, Tags } from "tsoa";
import HttpStatusCode from "../interfaces/http-status"; import HttpStatusCode from "../interfaces/http-status";
import esClient from "../elasticsearch"; import esClient from "../elasticsearch";
import { Search, SearchInfo, SearchOperator } from "../interfaces/search"; import { Search, SearchInfo, SearchOperator, SearchOptions } from "../interfaces/search";
import { StorageFile } from "../interfaces/storage-fs"; import { StorageFile } from "../interfaces/storage-fs";
import { QueryDslQueryContainer } from "@elastic/elasticsearch/lib/api/types"; import { QueryDslQueryContainer } from "@elastic/elasticsearch/lib/api/types";
import HttpError from "../interfaces/http-error";
const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX; const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX;
@ -15,7 +16,26 @@ export class SearchController extends Controller {
@Tags("Search") @Tags("Search")
@Security("bearerAuth") @Security("bearerAuth")
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ") @SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
public async searchFile(@Body() search: Search): Promise<StorageFile[]> { public async searchFile(
@Body()
body: {
AND?: (
| SearchInfo
| {
AND?: unknown;
OR?: unknown;
}
)[];
OR?: (
| SearchInfo
| {
AND?: unknown;
OR?: unknown;
}
)[];
} & SearchOptions,
): Promise<StorageFile[]> {
const search = body as Search;
const type = ["match", "match_phrase"] as const; const type = ["match", "match_phrase"] as const;
const searchMapCallback = (v: SearchInfo | SearchOperator): QueryDslQueryContainer => { const searchMapCallback = (v: SearchInfo | SearchOperator): QueryDslQueryContainer => {
@ -23,9 +43,16 @@ export class SearchController extends Controller {
return { [type[search.exact || v.exact ? 1 : 0]]: { [v.field]: v.value } }; return { [type[search.exact || v.exact ? 1 : 0]]: { [v.field]: v.value } };
} }
return { if ("AND" in v || "OR" in v) {
bool: { must: v.AND?.map(searchMapCallback), should: v.OR?.map(searchMapCallback) }, return {
}; bool: { must: v.AND?.map(searchMapCallback), should: v.OR?.map(searchMapCallback) },
};
}
throw new HttpError(
HttpStatusCode.UNPROCESSABLE_ENTITY,
"ข้อมูลค้นหาไม่ถูกต้อง กรุณาตรวจสอบอีกครั้ง",
);
}; };
const result = await esClient.search<StorageFile & { attachment: Record<string, string> }>({ const result = await esClient.search<StorageFile & { attachment: Record<string, string> }>({

View file

@ -1,5 +1,11 @@
import { QueryDslQueryContainer } from "@elastic/elasticsearch/lib/api/types"; import { QueryDslQueryContainer } from "@elastic/elasticsearch/lib/api/types";
export interface SearchOptions {
recursive?: boolean;
path?: string[];
exact?: boolean;
}
export interface SearchInfo { export interface SearchInfo {
field: string; field: string;
value: string; value: string;
@ -11,11 +17,7 @@ export interface SearchOperator {
OR?: (SearchInfo | SearchOperator)[]; OR?: (SearchInfo | SearchOperator)[];
} }
export interface Search extends SearchOperator { export type Search = SearchOperator & SearchOptions;
recursive?: boolean;
path?: string[];
exact?: boolean;
}
export function mapCallback(exact: boolean) { export function mapCallback(exact: boolean) {
const type = ["match", "match_phrase"] as const; const type = ["match", "match_phrase"] as const;

View file

@ -45,14 +45,22 @@ const models: TsoaRoute.Models = {
"additionalProperties": false, "additionalProperties": false,
}, },
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
"Search": { "SearchInfo": {
"dataType": "refObject", "dataType": "refObject",
"properties": { "properties": {
"AND": {"dataType":"array","array":{"dataType":"nestedObjectLiteral","nestedProperties":{"exact":{"dataType":"boolean"},"value":{"dataType":"string","required":true},"field":{"dataType":"string","required":true}}}}, "field": {"dataType":"string","required":true},
"OR": {"dataType":"array","array":{"dataType":"nestedObjectLiteral","nestedProperties":{"exact":{"dataType":"boolean"},"value":{"dataType":"string","required":true},"field":{"dataType":"string","required":true}}}}, "value": {"dataType":"string","required":true},
"exact": {"dataType":"boolean"}, "exact": {"dataType":"boolean"},
},
"additionalProperties": false,
},
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
"SearchOptions": {
"dataType": "refObject",
"properties": {
"recursive": {"dataType":"boolean"}, "recursive": {"dataType":"boolean"},
"path": {"dataType":"array","array":{"dataType":"string"}}, "path": {"dataType":"array","array":{"dataType":"string"}},
"exact": {"dataType":"boolean"},
}, },
"additionalProperties": false, "additionalProperties": false,
}, },
@ -177,7 +185,7 @@ export function RegisterRoutes(app: Router) {
function SearchController_searchFile(request: any, response: any, next: any) { function SearchController_searchFile(request: any, response: any, next: any) {
const args = { const args = {
search: {"in":"body","name":"search","required":true,"ref":"Search"}, body: {"in":"body","name":"body","required":true,"dataType":"intersection","subSchemas":[{"dataType":"nestedObjectLiteral","nestedProperties":{"OR":{"dataType":"array","array":{"dataType":"union","subSchemas":[{"ref":"SearchInfo"},{"dataType":"nestedObjectLiteral","nestedProperties":{"OR":{"dataType":"any"},"AND":{"dataType":"any"}}}]}},"AND":{"dataType":"array","array":{"dataType":"union","subSchemas":[{"ref":"SearchInfo"},{"dataType":"nestedObjectLiteral","nestedProperties":{"OR":{"dataType":"any"},"AND":{"dataType":"any"}}}]}}}},{"ref":"SearchOptions"}]},
}; };
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa

View file

@ -110,53 +110,27 @@
"type": "object", "type": "object",
"additionalProperties": false "additionalProperties": false
}, },
"Search": { "SearchInfo": {
"properties": { "properties": {
"AND": { "field": {
"items": { "type": "string"
"properties": {
"exact": {
"type": "boolean"
},
"value": {
"type": "string"
},
"field": {
"type": "string"
}
},
"required": [
"value",
"field"
],
"type": "object"
},
"type": "array"
}, },
"OR": { "value": {
"items": { "type": "string"
"properties": {
"exact": {
"type": "boolean"
},
"value": {
"type": "string"
},
"field": {
"type": "string"
}
},
"required": [
"value",
"field"
],
"type": "object"
},
"type": "array"
}, },
"exact": { "exact": {
"type": "boolean" "type": "boolean"
}, }
},
"required": [
"field",
"value"
],
"type": "object",
"additionalProperties": false
},
"SearchOptions": {
"properties": {
"recursive": { "recursive": {
"type": "boolean" "type": "boolean"
}, },
@ -165,6 +139,9 @@
"type": "string" "type": "string"
}, },
"type": "array" "type": "array"
},
"exact": {
"type": "boolean"
} }
}, },
"type": "object", "type": "object",
@ -617,7 +594,50 @@
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"$ref": "#/components/schemas/Search" "allOf": [
{
"properties": {
"OR": {
"items": {
"anyOf": [
{
"$ref": "#/components/schemas/SearchInfo"
},
{
"properties": {
"OR": {},
"AND": {}
},
"type": "object"
}
]
},
"type": "array"
},
"AND": {
"items": {
"anyOf": [
{
"$ref": "#/components/schemas/SearchInfo"
},
{
"properties": {
"OR": {},
"AND": {}
},
"type": "object"
}
]
},
"type": "array"
}
},
"type": "object"
},
{
"$ref": "#/components/schemas/SearchOptions"
}
]
} }
} }
} }