-
-
Notifications
You must be signed in to change notification settings - Fork 266
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #461 from bigcapitalhq/big-157-attach-documents-to…
…-transactions feat: upload and attach documents
- Loading branch information
Showing
137 changed files
with
9,544 additions
and
6,226 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
259 changes: 259 additions & 0 deletions
259
packages/server/src/api/controllers/Attachments/AttachmentsController.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,259 @@ | ||
import mime from 'mime-types'; | ||
import { Service, Inject } from 'typedi'; | ||
import { Router, Response, NextFunction, Request } from 'express'; | ||
import { body, param } from 'express-validator'; | ||
import BaseController from '@/api/controllers/BaseController'; | ||
import { AttachmentsApplication } from '@/services/Attachments/AttachmentsApplication'; | ||
|
||
@Service() | ||
export class AttachmentsController extends BaseController { | ||
@Inject() | ||
private attachmentsApplication: AttachmentsApplication; | ||
|
||
/** | ||
* Router constructor. | ||
*/ | ||
public router() { | ||
const router = Router(); | ||
|
||
router.post( | ||
'/', | ||
this.attachmentsApplication.uploadPipeline.single('file'), | ||
this.validateUploadedFileExistance, | ||
this.uploadAttachment.bind(this) | ||
); | ||
router.delete( | ||
'/:id', | ||
[param('id').exists()], | ||
this.validationResult, | ||
this.deleteAttachment.bind(this) | ||
); | ||
router.get( | ||
'/:id', | ||
[param('id').exists()], | ||
this.validationResult, | ||
this.getAttachment.bind(this) | ||
); | ||
router.post( | ||
'/:id/link', | ||
[body('modelRef').exists(), body('modelId').exists()], | ||
this.validationResult | ||
); | ||
router.post( | ||
'/:id/link', | ||
[body('modelRef').exists(), body('modelId').exists()], | ||
this.validationResult, | ||
this.linkDocument.bind(this) | ||
); | ||
router.post( | ||
'/:id/unlink', | ||
[body('modelRef').exists(), body('modelId').exists()], | ||
this.validationResult, | ||
this.unlinkDocument.bind(this) | ||
); | ||
router.get( | ||
'/:id/presigned-url', | ||
[param('id').exists()], | ||
this.validationResult, | ||
this.getAttachmentPresignedUrl.bind(this) | ||
); | ||
return router; | ||
} | ||
|
||
/** | ||
* Validates the upload file existance. | ||
* @param {Request} req | ||
* @param {Response} res | ||
* @param {NextFunction} next | ||
* @returns {Response|void} | ||
*/ | ||
private validateUploadedFileExistance( | ||
req: Request, | ||
res: Response, | ||
next: NextFunction | ||
) { | ||
if (!req.file) { | ||
return res.boom.badRequest(null, { | ||
errorType: 'FILE_UPLOAD_FAILED', | ||
message: 'Now file uploaded.', | ||
}); | ||
} | ||
next(); | ||
} | ||
|
||
/** | ||
* Uploads the attachments to S3 and store the file metadata to DB. | ||
* @param {Request} req | ||
* @param {Response} res | ||
* @param {NextFunction} next | ||
* @returns {Response|void} | ||
*/ | ||
private async uploadAttachment( | ||
req: Request, | ||
res: Response, | ||
next: NextFunction | ||
): Promise<Response | void> { | ||
const { tenantId } = req; | ||
const file = req.file; | ||
|
||
try { | ||
const data = await this.attachmentsApplication.upload(tenantId, file); | ||
|
||
return res.status(200).send({ | ||
status: 200, | ||
message: 'The document has uploaded successfully.', | ||
data, | ||
}); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
/** | ||
* Retrieves the given attachment key. | ||
* @param {Request} req | ||
* @param {Response} res | ||
* @param {NextFunction} next | ||
* @returns {Promise<Response|void>} | ||
*/ | ||
private async getAttachment( | ||
req: Request, | ||
res: Response, | ||
next: NextFunction | ||
): Promise<Response | void> { | ||
const { tenantId } = req; | ||
const { id } = req.params; | ||
|
||
try { | ||
const data = await this.attachmentsApplication.get(tenantId, id); | ||
|
||
const byte = await data.Body.transformToByteArray(); | ||
const extension = mime.extension(data.ContentType); | ||
const buffer = Buffer.from(byte); | ||
|
||
res.set( | ||
'Content-Disposition', | ||
`filename="${req.params.id}.${extension}"` | ||
); | ||
res.set('Content-Type', data.ContentType); | ||
res.send(buffer); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
/** | ||
* Deletes the given document key. | ||
* @param {Request} req | ||
* @param {Response} res | ||
* @param {NextFunction} next | ||
* @returns {Promise<Response|void>} | ||
*/ | ||
private async deleteAttachment( | ||
req: Request, | ||
res: Response, | ||
next: NextFunction | ||
): Promise<Response | void> { | ||
const { tenantId } = req; | ||
const { id: documentId } = req.params; | ||
|
||
try { | ||
await this.attachmentsApplication.delete(tenantId, documentId); | ||
|
||
return res.status(200).send({ | ||
status: 200, | ||
message: 'The document has been delete successfully.', | ||
}); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
/** | ||
* Links the given document key. | ||
* @param {Request} req | ||
* @param {Response} res | ||
* @param {NextFunction} next | ||
* @returns {Promise<Response|void>} | ||
*/ | ||
private async linkDocument( | ||
req: Request, | ||
res: Response, | ||
next: Function | ||
): Promise<Response | void> { | ||
const { tenantId } = req; | ||
const { id: documentId } = req.params; | ||
const { modelRef, modelId } = this.matchedBodyData(req); | ||
|
||
try { | ||
await this.attachmentsApplication.link( | ||
tenantId, | ||
documentId, | ||
modelRef, | ||
modelId | ||
); | ||
return res.status(200).send({ | ||
status: 200, | ||
message: 'The document has been linked successfully.', | ||
}); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
/** | ||
* Links the given document key. | ||
* @param {Request} req | ||
* @param {Response} res | ||
* @param {NextFunction} next | ||
* @returns {Promise<Response|void>} | ||
*/ | ||
private async unlinkDocument( | ||
req: Request, | ||
res: Response, | ||
next: NextFunction | ||
): Promise<Response | void> { | ||
const { tenantId } = req; | ||
const { id: documentId } = req.params; | ||
const { modelRef, modelId } = this.matchedBodyData(req); | ||
|
||
try { | ||
await this.attachmentsApplication.link( | ||
tenantId, | ||
documentId, | ||
modelRef, | ||
modelId | ||
); | ||
return res.status(200).send({ | ||
status: 200, | ||
message: 'The document has been linked successfully.', | ||
}); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
|
||
/** | ||
* Retreives the presigned url of the given attachment key. | ||
* @param {Request} req | ||
* @param {Response} res | ||
* @param {NextFunction} next | ||
* @returns {Promise<Response|void>} | ||
*/ | ||
private async getAttachmentPresignedUrl( | ||
req: Request, | ||
res: Response, | ||
next: NextFunction | ||
): Promise<Response | void> { | ||
const { id: documentKey } = req.params; | ||
|
||
try { | ||
const presignedUrl = await this.attachmentsApplication.getPresignedUrl( | ||
documentKey | ||
); | ||
return res.status(200).send({ presignedUrl }); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.