From 8bce92f531d3e9915e0f792440aa963a3a73fa3e Mon Sep 17 00:00:00 2001 From: Himanshu Kumar Sharma <97340980+HeeManSu@users.noreply.github.com> Date: Wed, 6 Nov 2024 00:00:44 +0530 Subject: [PATCH] Fix file upload bug (fixes #56) (#57) * Fix file upload bug (fixes #56) * Promisified the upload controller * test added * python runner in repository deployment fixed --- src/app.ts | 2 ++ src/controller/deploy.ts | 6 +++- src/controller/repository.ts | 3 +- src/controller/upload.ts | 54 +++++++++++++++++++++--------------- test/test.sh | 51 ++++++++++++++++++++++++++-------- 5 files changed, 80 insertions(+), 36 deletions(-) diff --git a/src/app.ts b/src/app.ts index 63c943e..78ecfe9 100644 --- a/src/app.ts +++ b/src/app.ts @@ -21,3 +21,5 @@ export class Application { } export const Applications: Record = {}; + +export const unzipPromises: Record> = {}; diff --git a/src/controller/deploy.ts b/src/controller/deploy.ts index 957e65e..894b4e1 100644 --- a/src/controller/deploy.ts +++ b/src/controller/deploy.ts @@ -3,7 +3,7 @@ import { hostname } from 'os'; import AppError from '../utils/appError'; -import { Applications } from '../app'; +import { Applications, unzipPromises } from '../app'; import { deployProcess } from '../utils/deploy'; import { installDependencies } from '../utils/install'; import { catchAsync } from './catch'; @@ -25,6 +25,8 @@ export const deploy = catchAsync( next: NextFunction ) => { try { + await unzipPromises[req.body.suffix]; + const application = Applications[req.body.suffix]; // Check if the application exists and it is stored @@ -37,6 +39,8 @@ export const deploy = catchAsync( const resource = await application.resource; + // Wait for the unzipping to complete + await installDependencies(resource); await deployProcess(resource); diff --git a/src/controller/repository.ts b/src/controller/repository.ts index 5afeb8e..8b2dd5a 100644 --- a/src/controller/repository.ts +++ b/src/controller/repository.ts @@ -44,7 +44,8 @@ const handleRunners = async (repoPath: string): Promise => { if (file === 'package.json') runners.push('nodejs'); if (stat.isDirectory()) { - await handleRunners(fullPath); + const subRunners = await handleRunners(fullPath); + runners.push(...subRunners); } } return runners; diff --git a/src/controller/upload.ts b/src/controller/upload.ts index 2329df2..22e0a7f 100644 --- a/src/controller/upload.ts +++ b/src/controller/upload.ts @@ -4,7 +4,7 @@ import path from 'path'; import busboy from 'busboy'; import { NextFunction, Request, Response } from 'express'; -import { Extract, ParseOptions } from 'unzipper'; +import { Extract } from 'unzipper'; import { MetaCallJSON } from '@metacall/protocol/deployment'; import { Application, Applications, Resource } from '../app'; @@ -56,10 +56,10 @@ export const uploadPackage = ( jsons: [] }; - let handled = false; // TODO: Promisify the whole controller + let handled = false; const errorHandler = (error: AppError) => { - if (handled == false) { + if (handled === false) { req.unpipe(bb); next(error); } @@ -88,8 +88,8 @@ export const uploadPackage = ( const { mimeType, filename } = info; if ( - mimeType != 'application/x-zip-compressed' && - mimeType != 'application/zip' + mimeType !== 'application/x-zip-compressed' && + mimeType !== 'application/zip' ) { return errorHandler( new AppError('Please upload a zip file', 404) @@ -147,7 +147,7 @@ export const uploadPackage = ( eventHandler('finish', () => { if (resource.blob === undefined) { - throw Error('Invalid file upload, blob path is not defined'); + throw new Error('Invalid file upload, blob path is not defined'); } const deleteBlob = () => { @@ -201,33 +201,43 @@ export const uploadPackage = ( } ); - const options: ParseOptions = { path: resource.path }; + const unzipAndResolve = () => { + return new Promise((resolve, reject) => { + fs.createReadStream(resource.blob as string) + .pipe(Extract({ path: resource.path })) + .on('close', () => { + deleteBlob(); + resolve(); + }) + .on('error', error => { + deleteBlob(); + deleteFolder(); + reject( + new AppError( + `Failed to unzip the resource at: ${error.toString()}`, + 500 + ) + ); + }); + }); + }; - fs.createReadStream(resource.blob) - .pipe(Extract(options)) - .on('close', () => { - deleteBlob(); + unzipAndResolve() + .then(() => { resourceResolve(resource); }) - .on('error', error => { - deleteBlob(); - deleteFolder(); - const appError = new AppError( - `Failed to unzip the resource at: ${error.toString()}`, - 500 - ); - errorHandler(appError); - resourceReject(appError); + .catch(error => { + resourceReject(error); + errorHandler(error); }); }); eventHandler('close', () => { - if (handled == false) { + if (handled === false) { res.status(201).json({ id: resource.id }); } - handled = true; }); diff --git a/test/test.sh b/test/test.sh index 30cc959..d760fe4 100755 --- a/test/test.sh +++ b/test/test.sh @@ -185,11 +185,6 @@ function test_nodejs_dependency_app() { [[ $sum_response = 3 ]] || exit 1 } -# Run tests -run_tests "nodejs-base-app" test_nodejs_app -run_tests "python-base-app" test_python_base_app -run_tests "python-dependency-app" test_python_dependency_app -run_tests "nodejs-dependency-app" test_nodejs_dependency_app echo "Integration tests for package deployment passed without errors." @@ -308,20 +303,52 @@ function test_nodejs_dependency_app() { echo "Node.js Dependency App tests passed" } -echo "starting repository deployment tests" +# without Dependencies -#without Dependencies - -#Test NodeJs app +# Test NodeJs app test_deploy_from_repo "https://github.com/HeeManSu/nodejs-parameter-example" "nodejs-parameter-example" test_nodejs-parameter-example -# #Test Python app +# Test Python app test_deploy_from_repo "https://github.com/HeeManSu/metacall-python-example" "metacall-python-example" test_python_time_app -# #With Dependencies +# With Dependencies -# # Test Python app +# Test Python app test_deploy_from_repo "https://github.com/HeeManSu/python-dependency-metacall" "python-dependency-metacall" test_python_dependency_metacall #Test NodeJs app test_deploy_from_repo "https://github.com/HeeManSu/auth-middleware-metacall" "auth-middleware-metacall" test_nodejs_dependency_app echo "Repository deployment tests completed." + + +# Simultaneous deployment tests +function test_simultaneous_deploy() { + echo "Testing simultaneous deployments..." + pids=() + + # Run all tests simultaneously in background + run_tests "nodejs-base-app" test_nodejs_app & + pids+=($!) + + run_tests "python-base-app" test_python_base_app & + pids+=($!) + + run_tests "python-dependency-app" test_python_dependency_app & + pids+=($!) + + run_tests "nodejs-dependency-app" test_nodejs_dependency_app & + pids+=($!) + + for pid in "${pids[@]}"; do + if ! wait $pid; then + echo "Simultaneous deployment test failed - Test failed" + return 1 + exit 1 + fi + done + + echo "Simultaneous deployment test passed - All tests passed" + return 0 +} + +echo "Testing simultaneous deployments..." +test_simultaneous_deploy \ No newline at end of file