diff --git a/Prototype/server/src/controllers/fileController.ts b/Prototype/server/src/controllers/fileController.ts index 1c4dca2..c050dca 100644 --- a/Prototype/server/src/controllers/fileController.ts +++ b/Prototype/server/src/controllers/fileController.ts @@ -15,20 +15,21 @@ import minioClient from "../storage"; import HttpStatusCode from "../interfaces/http-status"; import { pathExist } from "../utils/minio"; import HttpError from "../interfaces/http-error"; +import { EhrFile } from "../interfaces/ehr-fs"; @Route("/cabinet") export class FileController extends Controller { - @Post("/{cabinetName}/drawer/{drawerName}/folder/{folderName}") + @Post("/{cabinetName}/drawer/{drawerName}/folder/{folderName}/file") @Tags("File") @Security("bearerAuth") @SuccessResponse(HttpStatusCode.CREATED) public async uploadFile( - @Request() request: any, + @Request() request: { user: { preferred_username: string } }, @UploadedFile() file: Express.Multer.File, @FormField() title: string, @FormField() description: string, - @FormField() keywords: string, - @FormField() categories: string, + @FormField() keyword: string, + @FormField() category: string, @Path() cabinetName: string, @Path() drawerName: string, @Path() folderName: string, @@ -49,21 +50,58 @@ export class FileController extends Controller { createdAt: new Date().toISOString(), createdBy: request.user.preferred_username, }) - .catch(() => new Error("Object storage error occured.")); + .catch((e) => console.error(e)); - if (info) { + if (!info) throw new Error("Object storage error occured."); + + const search = await esClient.search }>({ + index: "ehr-api-client", + query: { + match: { + pathname: pathname, + }, + }, + }); + + const exist = search.hits.hits.find((v) => v._source?.pathname === pathname); + + const metadata: Partial = { + pathname, + fileName: filename, + fileSize: file.size, + fileType: file.mimetype, + title: title, + description: description, + category: category.split(","), + keyword: keyword.split(","), + }; + + if (!exist) { await esClient.index({ pipeline: "attachment", index: "ehr-api-client", document: { data: Buffer.from(file.buffer).toString("base64"), - path: pathname, - title, - description, - keywords, - categories, + createdAt: new Date().toISOString(), + createdBy: request.user.preferred_username, + updatedAt: new Date().toISOString(), + updatedBy: request.user.preferred_username, + ...metadata, + }, + }); + } else { + await esClient.delete({ index: exist._index, id: exist._id }); + await esClient.index({ + pipeline: "attachment", + index: "ehr-api-client", + document: { + data: Buffer.from(file.buffer).toString("base64"), + createdAt: exist._source?.createdAt, + createdBy: exist._source?.createdBy, + updatedAt: new Date().toISOString(), + updatedBy: request.user.preferred_username, + ...metadata, }, - op_type: "index", }); } diff --git a/Prototype/server/src/interfaces/ehr-fs.ts b/Prototype/server/src/interfaces/ehr-fs.ts index 5916436..919cbcc 100644 --- a/Prototype/server/src/interfaces/ehr-fs.ts +++ b/Prototype/server/src/interfaces/ehr-fs.ts @@ -19,9 +19,11 @@ export interface EhrFile { pathname: string; fileName: string; - fileSize: string; + fileSize: number; fileType: string; + title: string; + description: string; category: string[]; keyword: string[]; diff --git a/Prototype/server/src/middlewares/exception.ts b/Prototype/server/src/middlewares/exception.ts index dcf36c3..ccab12a 100644 --- a/Prototype/server/src/middlewares/exception.ts +++ b/Prototype/server/src/middlewares/exception.ts @@ -1,6 +1,7 @@ import { NextFunction, Request, Response } from "express"; import HttpError from "../interfaces/http-error"; import HttpStatusCode from "../interfaces/http-status"; +import { ValidateError } from "tsoa"; function errorHandler(error: Error, _req: Request, res: Response, _next: NextFunction) { if (error instanceof HttpError) { @@ -10,6 +11,16 @@ function errorHandler(error: Error, _req: Request, res: Response, _next: NextFun }); } + if (error instanceof ValidateError) { + return res.status(error.status).json({ + status: HttpStatusCode.UNPROCESSABLE_ENTITY, + message: "Validation error(s).", + detail: error.fields, + }); + } + + console.error(error); + return res.status(HttpStatusCode.INTERNAL_SERVER_ERROR).json({ status: HttpStatusCode.INTERNAL_SERVER_ERROR, message: error.message, diff --git a/Prototype/server/src/routes.ts b/Prototype/server/src/routes.ts index 681dea5..3fa6d7a 100644 --- a/Prototype/server/src/routes.ts +++ b/Prototype/server/src/routes.ts @@ -255,7 +255,8 @@ export function RegisterRoutes(app: Router) { } }); // 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 - app.post('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName', + app.post('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/file', + authenticateMiddleware([{"bearerAuth":[]}]), upload.single('file'), ...(fetchMiddlewares(FileController)), ...(fetchMiddlewares(FileController.prototype.uploadFile)), @@ -266,8 +267,8 @@ export function RegisterRoutes(app: Router) { file: {"in":"formData","name":"file","required":true,"dataType":"file"}, title: {"in":"formData","name":"title","required":true,"dataType":"string"}, description: {"in":"formData","name":"description","required":true,"dataType":"string"}, - keywords: {"in":"formData","name":"keywords","required":true,"dataType":"string"}, - categories: {"in":"formData","name":"categories","required":true,"dataType":"string"}, + keyword: {"in":"formData","name":"keyword","required":true,"dataType":"string"}, + category: {"in":"formData","name":"category","required":true,"dataType":"string"}, cabinetName: {"in":"path","name":"cabinetName","required":true,"dataType":"string"}, drawerName: {"in":"path","name":"drawerName","required":true,"dataType":"string"}, folderName: {"in":"path","name":"folderName","required":true,"dataType":"string"}, @@ -283,7 +284,34 @@ export function RegisterRoutes(app: Router) { const promise = controller.uploadFile.apply(controller, validatedArgs as any); - promiseHandler(controller, promise, response, 204, next); + promiseHandler(controller, promise, response, 201, next); + } catch (err) { + return next(err); + } + }); + // 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 + app.get('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/file', + ...(fetchMiddlewares(FileController)), + ...(fetchMiddlewares(FileController.prototype.getFile)), + + function FileController_getFile(request: any, response: any, next: any) { + const args = { + cabinetName: {"in":"path","name":"cabinetName","required":true,"dataType":"string"}, + drawerName: {"in":"path","name":"drawerName","required":true,"dataType":"string"}, + folderName: {"in":"path","name":"folderName","required":true,"dataType":"string"}, + }; + + // 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 + + let validatedArgs: any[] = []; + try { + validatedArgs = getValidatedArgs(args, request, response); + + const controller = new FileController(); + + + const promise = controller.getFile.apply(controller, validatedArgs as any); + promiseHandler(controller, promise, response, 200, next); } catch (err) { return next(err); } diff --git a/Prototype/server/src/swagger.json b/Prototype/server/src/swagger.json index 8918c87..e0bb1ae 100644 --- a/Prototype/server/src/swagger.json +++ b/Prototype/server/src/swagger.json @@ -375,18 +375,22 @@ ] } }, - "/cabinet/{cabinetName}/drawer/{drawerName}/folder/{folderName}": { + "/cabinet/{cabinetName}/drawer/{drawerName}/folder/{folderName}/file": { "post": { "operationId": "UploadFile", "responses": { - "204": { + "201": { "description": "" } }, "tags": [ "File" ], - "security": [], + "security": [ + { + "bearerAuth": [] + } + ], "parameters": [ { "in": "path", @@ -430,10 +434,10 @@ "description": { "type": "string" }, - "keywords": { + "keyword": { "type": "string" }, - "categories": { + "category": { "type": "string" } }, @@ -441,99 +445,69 @@ "file", "title", "description", - "keywords", - "categories" + "keyword", + "category" ] } } } } }, - "put": { - "operationId": "EditFolder", + "get": { + "operationId": "GetFile", "responses": { - "204": { + "200": { "description": "", "content": { "application/json": { - "schema": {} + "schema": { + "items": { + "properties": { + "type": { + "type": "string" + }, + "category": { + "items": { + "type": "string" + }, + "type": "array" + }, + "keyword": { + "items": { + "type": "string" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "title": { + "type": "string" + }, + "pathname": { + "type": "string" + } + }, + "required": [ + "type", + "category", + "keyword", + "description", + "title", + "pathname" + ], + "type": "object" + }, + "type": "array" + } } } } }, "tags": [ - "Folder" - ], - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "in": "query", - "name": "cabinetName", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "drawerName", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "folderName", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "properties": { - "name": { - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - } - } - } - } - }, - "delete": { - "operationId": "DeleteFolder", - "responses": { - "204": { - "description": "", - "content": { - "application/json": { - "schema": {} - } - } - } - }, - "tags": [ - "Folder" - ], - "security": [ - { - "bearerAuth": [] - } + "File" ], + "security": [], "parameters": [ { "in": "path", @@ -656,6 +630,120 @@ } } }, + "/cabinet/{cabinetName}/drawer/{drawerName}/folder/{folderName}": { + "put": { + "operationId": "EditFolder", + "responses": { + "204": { + "description": "", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "tags": [ + "Folder" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "in": "query", + "name": "cabinetName", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "drawerName", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "folderName", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + } + } + } + } + }, + "delete": { + "operationId": "DeleteFolder", + "responses": { + "204": { + "description": "", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "tags": [ + "Folder" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "cabinetName", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "drawerName", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "folderName", + "required": true, + "schema": { + "type": "string" + } + } + ] + } + }, "/cabinet/{cabinetName}/drawer/{drawerName}/folder/{folderName}/subfolder": { "get": { "operationId": "ListFolder",