import { Controller, Route, Security, Tags, Path, Request, Response, Get, Query } from "tsoa"; import { AppDataSource } from "../database/data-source"; import HttpSuccess from "../interfaces/http-success"; import HttpStatusCode from "../interfaces/http-status"; import HttpError from "../interfaces/http-error"; import { ApiName } from "../entities/ApiName"; import { isPermissionRequest } from "../middlewares/authWebService"; import { RequestWithUserWebService } from "../middlewares/user"; import { OrgRevision } from "../entities/OrgRevision"; import { ApiHistory } from "../entities/ApiHistory"; import { SystemCode } from "./../interfaces/api-type"; @Route("api/v1/org/api-service") @Tags("ApiKey") @Security("webServiceAuth") @Response( HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง", ) export class ApiWebServiceController extends Controller { private apiNameRepository = AppDataSource.getRepository(ApiName); private orgRevisionRepository = AppDataSource.getRepository(OrgRevision); private apiHistoryRepository = AppDataSource.getRepository(ApiHistory); /** * list fields by systems * @summary รายการ fields ตาม systems */ @Get("/:system/:code") async listAttribute( @Request() request: RequestWithUserWebService, @Path("system") system: SystemCode, @Path("code") code: string, @Query("page") page: number = 1, @Query("pageSize") pageSize: number = 100, ): Promise { const apiName = await this.apiNameRepository.findOne({ where: { code }, select: ["id", "code", "methodApi", "system", "isActive"], relations: ["apiAttributes"], order: { apiAttributes: { ordering: "ASC", }, }, }); if (!apiName || apiName.system != system || !apiName.isActive || apiName.methodApi != "GET") { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบ API ที่ระบุ"); } await isPermissionRequest(request, apiName.id); const offset = (page - 1) * pageSize; const propertyKey = apiName.apiAttributes.map((attr) => `${attr.tbName}.${attr.propertyKey}`); let tbMain: string = ""; let condition: string = "1=1"; if (system == "registry") { tbMain = "Profile"; } else if (system == "registry_emp") { tbMain = "ProfileEmployee"; condition = `ProfileEmployee.employeeClass = "PERM"`; } else if (system == "registry_temp") { tbMain = "ProfileEmployee"; condition = `ProfileEmployee.employeeClass = "TEMP"`; } else if (system == "organization") { tbMain = "OrgRoot"; const revision = await this.orgRevisionRepository.findOne({ select: ["id"], where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, }); condition = `OrgRoot.orgRevisionId = "${revision?.id}"`; } else if (system == "position") { tbMain = "PosMaster"; const revision = await this.orgRevisionRepository.findOne({ select: ["id"], where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, }); condition = `PosMaster.orgRevisionId = "${revision?.id}"`; } const repo = AppDataSource.getRepository(tbMain); const metadata = repo.metadata; const relationMap: Record = {}; metadata.relations.forEach((rel) => { relationMap[rel.inverseEntityMetadata.name] = rel.propertyName; }); // ดึงเฉพาะตารางรอง (ถ้าเลือกไว้) let propertyOtherKey: any[] = []; propertyOtherKey = [ ...new Set(propertyKey.map((x) => x.split(".")[0]).filter((tb) => tb !== tbMain)), ]; const queryBuilder = repo.createQueryBuilder(tbMain); // join กับตารารอง if (propertyOtherKey.length > 0) { propertyOtherKey.forEach((tb) => { const relationName = relationMap[tb]; if (relationName) { queryBuilder.leftJoin( `${tbMain}.${relationName === "next_holder" ? "current_holder" : relationName}`, // เช็คว่าถ้าเป็น next_holder ให้ใช้ current_holder แทน tb, ); } }); } // // เพิ่ม Main.id เพราะจะใช้ pk ในการแมบและนับจำนวน // if (!propertyKey.includes(`${Main}.id`)) { // propertyKey.push(`${Main}.id`); // } // add FK let pk: string = ""; const primaryColumns = metadata.primaryColumns; primaryColumns.forEach((col) => { pk = col.propertyName; if (!propertyKey.includes(`${tbMain}.${pk}`)) { propertyKey.push(`${tbMain}.${pk}`); } }); const [items, total] = await queryBuilder .select(propertyKey) .where(condition) .orderBy(propertyKey[0], "ASC") .skip(offset) .take(pageSize) .getManyAndCount(); // ลบ Main.id // const results = items.map(({ id, ...x }) => x); // const results = items.map(({ pk, ...x }) => x); // const results = items.map(item => { // primaryColumns.forEach(col => delete item[col.propertyName]); // return item; // }); // split object id ออกก่อน return const data = items.map((item) => { const { [pk]: removedPk, ...x } = item; return x; }); // console.log("queryBuilder ===> ", queryBuilder.getQuery()); // save api history after query success const history = { headerApi: JSON.stringify({ host: request.headers.host, "x-api-key": request.headers["x-api-key"], connection: request.headers.connection, accept: request.headers.accept, }), tokenApi: Array.isArray(request.headers["x-api-key"]) ? request.headers["x-api-key"][0] || "" : request.headers["x-api-key"] || "", requestApi: `${request.method} ${request.protocol}://${request.headers.host}${request.originalUrl || request.url}`, responseApi: "OK", ipApi: request.ip, codeApi: code, apiKeyId: request.user.id, apiNameId: apiName.id, createdFullName: request.user.name, lastUpdateFullName: request.user.name, }; await this.apiHistoryRepository.save(history); const results = data.map((item) => { const flattenedItem: any = {}; // Extract nested object properties to top level Object.keys(item).forEach((key) => { const value = item[key]; if (value && typeof value === "object") { // if (Array.isArray(value) && value.length === 1) { // // If array has single item, extract it as object // Object.assign(flattenedItem, value[0]); // } else if (!Array.isArray(value)) { // Merge nested object properties to top level Object.assign(flattenedItem, value); } else { // Keep arrays with multiple items or empty arrays as is flattenedItem[key] = value; } } else { // Keep primitive values as is flattenedItem[key] = value; } }); return flattenedItem; }); return new HttpSuccess({ data: results, total }); } }