From 083e984c89569b7a3d31d9f9dc6b53b7a501e283 Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Fri, 17 Nov 2023 15:17:12 +0700 Subject: [PATCH] feat: subfolder --- .../src/controllers/subFolderController.ts | 146 +++++++++++ Prototype/server/src/routes.ts | 114 +++++++++ Prototype/server/src/swagger.json | 228 ++++++++++++++++++ 3 files changed, 488 insertions(+) create mode 100644 Prototype/server/src/controllers/subFolderController.ts diff --git a/Prototype/server/src/controllers/subFolderController.ts b/Prototype/server/src/controllers/subFolderController.ts new file mode 100644 index 0000000..a8f518d --- /dev/null +++ b/Prototype/server/src/controllers/subFolderController.ts @@ -0,0 +1,146 @@ +import { + Body, + Controller, + Delete, + Get, + Path, + Post, + Put, + Query, + Route, + SuccessResponse, + Tags, +} from "tsoa"; +import * as Minio from "minio"; + +import HttpError from "../interfaces/http-error"; +import HttpStatusCode from "../interfaces/http-status"; +import { listFolder, pathExist } from "../utils/minio"; +import { EhrFolder } from "../interfaces/ehr-fs"; +import minioClient from "../storage"; + +@Route("/cabinet") +export class SubFolderController extends Controller { + @Get("/{cabinetName}/drawer/{drawerName}/folder/{folderName}/subfolder") + @Tags("SubFolder") + @SuccessResponse(HttpStatusCode.OK) + public async listFolder( + @Path() cabinetName: string, + @Path() drawerName: string, + @Path() folderName: string, + ): Promise { + const fullpath = [cabinetName, drawerName, folderName].join("/") + "/"; + + if (!(await pathExist(fullpath))) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "Provided path does not exist."); + } + + return listFolder(fullpath); + } + + @Post("/{cabinetName}/drawer/{drawerName}/folder/{folderName}/subfolder") + @Tags("SubFolder") + @SuccessResponse(HttpStatusCode.CREATED) + public async createFolder( + @Body() body: { name: string }, + @Path() cabinetName: string, + @Path() drawerName: string, + @Path() folderName: string, + ) { + if (!(await pathExist(`${cabinetName}/${drawerName}/${folderName}`))) { + throw new HttpError( + HttpStatusCode.PRECONDITION_FAILED, + "Cabinet, drawer or folder cannot be found.", + ); + } + + const uploaded = await minioClient + .putObject("ehr", `${cabinetName}/${drawerName}/${folderName}/${body.name}/.keep`, "", 0, { + createdAt: new Date().toISOString(), + createdBy: "SomeUser", + }) + .catch((e) => console.error(e)); + + if (!uploaded) { + throw new Error("Object storage error occured."); + } + + return this.setStatus(HttpStatusCode.CREATED); + } + + @Put("/{cabinetName}/drawer/{drawerName}/folder/{folderName}/subfolder/{subFolderName}") + @Tags("SubFolder") + @SuccessResponse(HttpStatusCode.NO_CONTENT) + public async editFolder( + @Body() body: { name: string }, + @Query() cabinetName: string, + @Query() drawerName: string, + @Query() folderName: string, + @Query() subFolderName: string, + ) { + const fullpath = [cabinetName, drawerName, folderName, subFolderName].join("/") + "/"; + + if (!(await pathExist(fullpath))) { + throw new HttpError( + HttpStatusCode.PRECONDITION_FAILED, + "Provided resource location does not exist.", + ); + } + + return new Promise((resolve, reject) => { + const stream = minioClient.listObjectsV2("ehr", fullpath, true); + + stream.on("data", (v) => { + if (!(v && v.name)) return; + + const destination = `${cabinetName}/${drawerName}/${folderName}/${body.name}/${v.name.slice( + fullpath.length, + )}`; + const source = `/ehr/${v.name}`; + const cond = new Minio.CopyConditions(); + + minioClient.copyObject("ehr", destination, source, cond, (e) => { + if (e) { + return reject(new Error("Failed to move.")); + } + return minioClient.removeObject("ehr", v.name); + }); + }); + + stream.on("end", () => { + resolve(this.setStatus(HttpStatusCode.NO_CONTENT)); + }); + stream.on("error", () => reject(new Error("Object storage error occured."))); + }); + } + + @Delete("/{cabinetName}/drawer/{drawerName}/folder/{folderName}/subfolder/{subFolderName}") + @Tags("SubFolder") + @SuccessResponse(HttpStatusCode.NO_CONTENT) + public async deleteFolder( + @Path() cabinetName: string, + @Path() drawerName: string, + @Path() folderName: string, + @Path() subFolderName: string, + ) { + return new Promise((resolve, reject) => { + const objects: string[] = []; + const stream = minioClient.listObjectsV2( + "ehr", + `${cabinetName}/${drawerName}/${folderName}/${subFolderName}`, + true, + ); + + stream.on("data", (v) => { + if (!(v && v.name)) return; + + objects.push(v.name); + }); + + stream.on("close", () => minioClient.removeObjects("ehr", objects)); + stream.on("error", () => reject(new Error("Object storage error occured."))); + + resolve(this.setStatus(HttpStatusCode.NO_CONTENT)); + }); + } +} diff --git a/Prototype/server/src/routes.ts b/Prototype/server/src/routes.ts index 412e75d..12097c0 100644 --- a/Prototype/server/src/routes.ts +++ b/Prototype/server/src/routes.ts @@ -10,6 +10,8 @@ import { DrawerController } from './controllers/drawerController'; import { FileController } from './controllers/fileController'; // 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 import { FolderController } from './controllers/folderController'; +// 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 +import { SubFolderController } from './controllers/subFolderController'; import { expressAuthentication } from './utils/auth'; // @ts-ignore - no great way to install types from subpackage const promiseAny = require('promise.any'); @@ -376,6 +378,118 @@ export function RegisterRoutes(app: Router) { const controller = new FolderController(); + const promise = controller.deleteFolder.apply(controller, validatedArgs as any); + promiseHandler(controller, promise, response, 204, 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/subfolder', + ...(fetchMiddlewares(SubFolderController)), + ...(fetchMiddlewares(SubFolderController.prototype.listFolder)), + + function SubFolderController_listFolder(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 SubFolderController(); + + + const promise = controller.listFolder.apply(controller, validatedArgs as any); + promiseHandler(controller, promise, response, 200, 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.post('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/subfolder', + ...(fetchMiddlewares(SubFolderController)), + ...(fetchMiddlewares(SubFolderController.prototype.createFolder)), + + function SubFolderController_createFolder(request: any, response: any, next: any) { + const args = { + body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"name":{"dataType":"string","required":true}}}, + 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 SubFolderController(); + + + const promise = controller.createFolder.apply(controller, validatedArgs as any); + 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.put('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/subfolder/:subFolderName', + ...(fetchMiddlewares(SubFolderController)), + ...(fetchMiddlewares(SubFolderController.prototype.editFolder)), + + function SubFolderController_editFolder(request: any, response: any, next: any) { + const args = { + body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"name":{"dataType":"string","required":true}}}, + cabinetName: {"in":"query","name":"cabinetName","required":true,"dataType":"string"}, + drawerName: {"in":"query","name":"drawerName","required":true,"dataType":"string"}, + folderName: {"in":"query","name":"folderName","required":true,"dataType":"string"}, + subFolderName: {"in":"query","name":"subFolderName","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 SubFolderController(); + + + const promise = controller.editFolder.apply(controller, validatedArgs as any); + promiseHandler(controller, promise, response, 204, 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.delete('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/subfolder/:subFolderName', + ...(fetchMiddlewares(SubFolderController)), + ...(fetchMiddlewares(SubFolderController.prototype.deleteFolder)), + + function SubFolderController_deleteFolder(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"}, + subFolderName: {"in":"path","name":"subFolderName","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 SubFolderController(); + + const promise = controller.deleteFolder.apply(controller, validatedArgs as any); promiseHandler(controller, promise, response, 204, next); } catch (err) { diff --git a/Prototype/server/src/swagger.json b/Prototype/server/src/swagger.json index 1d55d71..049ab8a 100644 --- a/Prototype/server/src/swagger.json +++ b/Prototype/server/src/swagger.json @@ -607,6 +607,234 @@ } } } + }, + "/cabinet/{cabinetName}/drawer/{drawerName}/folder/{folderName}/subfolder": { + "get": { + "operationId": "ListFolder", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/EhrFolder" + }, + "type": "array" + } + } + } + } + }, + "tags": [ + "SubFolder" + ], + "security": [], + "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" + } + } + ] + }, + "post": { + "operationId": "CreateFolder", + "responses": { + "201": { + "description": "" + } + }, + "tags": [ + "SubFolder" + ], + "security": [], + "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" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + } + } + } + } + } + }, + "/cabinet/{cabinetName}/drawer/{drawerName}/folder/{folderName}/subfolder/{subFolderName}": { + "put": { + "operationId": "EditFolder", + "responses": { + "204": { + "description": "", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "tags": [ + "SubFolder" + ], + "security": [], + "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" + } + }, + { + "in": "query", + "name": "subFolderName", + "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": [ + "SubFolder" + ], + "security": [], + "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" + } + }, + { + "in": "path", + "name": "subFolderName", + "required": true, + "schema": { + "type": "string" + } + } + ] + } } }, "servers": [