Skip to content

Commit f4faf86

Browse files
committed
One more refactor.
1 parent 7541399 commit f4faf86

17 files changed

+306
-304
lines changed

src/api.ts

+51-13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { callFunction } from './controller/call';
22
import { deployDelete } from './controller/delete';
33
import { deploy } from './controller/deploy';
44
import { globalError } from './controller/error';
5+
import { inspect } from './controller/inspect';
56
import { logs } from './controller/logs';
67
import {
78
fetchBranchList,
@@ -12,16 +13,53 @@ import { serveStatic } from './controller/static';
1213
import { uploadPackage } from './controller/upload';
1314
import { validate } from './controller/validate';
1415

15-
export default {
16-
callFunction,
17-
deployDelete,
18-
deploy,
19-
globalError,
20-
logs,
21-
fetchFilesFromRepo,
22-
fetchBranchList,
23-
fetchFileList,
24-
serveStatic,
25-
uploadPackage,
26-
validate
27-
};
16+
import { hostname } from 'os';
17+
18+
import express, { Express, NextFunction, Request, Response } from 'express';
19+
20+
import AppError from './utils/appError';
21+
22+
export function initializeAPI(): Express {
23+
const app = express();
24+
const host = hostname();
25+
26+
app.use(express.json());
27+
app.use(express.urlencoded({ extended: true }));
28+
29+
app.get('/readiness', (_req: Request, res: Response) =>
30+
res.sendStatus(200)
31+
);
32+
app.get('/validate', validate);
33+
app.get('/api/account/deploy-enabled', validate);
34+
35+
app.get(`/${host}/:suffix/:version/call/:func`, callFunction);
36+
app.post(`/${host}/:suffix/:version/call/:func`, callFunction);
37+
app.get(
38+
`/${host}/:suffix/:version/static/.metacall/faas/apps/:suffix/:file`,
39+
serveStatic
40+
);
41+
42+
app.post('/api/package/create', uploadPackage);
43+
app.post('/api/repository/add', fetchFilesFromRepo);
44+
45+
app.post('/api/repository/branchlist', fetchBranchList);
46+
app.post('/api/repository/filelist', fetchFileList);
47+
app.post('/api/deploy/logs', logs);
48+
49+
app.post('/api/deploy/create', deploy);
50+
51+
app.get('/api/inspect', inspect);
52+
53+
app.post('/api/deploy/delete', deployDelete);
54+
55+
// For all the additional unimplemented routes
56+
app.all('*', (req: Request, res: Response, next: NextFunction) => {
57+
next(
58+
new AppError(`Can't find ${req.originalUrl} on this server!`, 404)
59+
);
60+
});
61+
62+
app.use(globalError);
63+
64+
return app;
65+
}

src/app.ts

+23-48
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,23 @@
1-
import { hostname } from 'os';
2-
3-
import express, { NextFunction, Request, Response } from 'express';
4-
5-
import api from './api';
6-
import { allApplications } from './constants';
7-
import AppError from './utils/appError';
8-
9-
const app = express();
10-
const host = hostname();
11-
12-
app.use(express.json());
13-
app.use(express.urlencoded({ extended: true }));
14-
15-
app.get('/readiness', (_req: Request, res: Response) => res.sendStatus(200));
16-
app.get('/validate', api.validate);
17-
app.get('/api/account/deploy-enabled', api.validate);
18-
19-
app.get(`/${host}/:appName/:version/call/:name`, api.callFunction);
20-
app.post(`/${host}/:appName/:version/call/:name`, api.callFunction);
21-
app.get(
22-
`/${host}/:appName/:version/static/.metacall/faas/apps/:app/:file`,
23-
api.serveStatic
24-
);
25-
26-
app.post('/api/package/create', api.uploadPackage);
27-
app.post('/api/repository/add', api.fetchFilesFromRepo);
28-
29-
app.post('/api/repository/branchlist', api.fetchBranchList);
30-
app.post('/api/repository/filelist', api.fetchFileList);
31-
app.post('/api/deploy/logs', api.logs);
32-
33-
app.post('/api/deploy/create', api.deploy);
34-
35-
app.get('/api/inspect', (_req, res) => {
36-
res.send(Object.values(allApplications));
37-
});
38-
39-
app.post('/api/deploy/delete', api.deployDelete);
40-
41-
// For all the additional unimplemented routes
42-
app.all('*', (req: Request, res: Response, next: NextFunction) => {
43-
next(new AppError(`Can't find ${req.originalUrl} on this server!`, 404));
44-
});
45-
46-
app.use(api.globalError);
47-
48-
export default app;
1+
import { Deployment, MetaCallJSON } from '@metacall/protocol/deployment';
2+
import { ChildProcess } from 'child_process';
3+
4+
export interface Resource {
5+
id: string;
6+
path: string;
7+
jsons: MetaCallJSON[];
8+
runners?: string[];
9+
type?: string;
10+
blob?: string;
11+
}
12+
13+
export class Application {
14+
public resource?: Promise<Resource>;
15+
public proc?: ChildProcess;
16+
public deployment?: Deployment;
17+
18+
public kill(): void {
19+
this.proc?.kill();
20+
}
21+
}
22+
23+
export const Applications: Record<string, Application> = {};

src/constants.ts

-54
This file was deleted.

src/controller/call.ts

+45-35
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,72 @@
11
import { NextFunction, Request, Response } from 'express';
2+
import { Applications } from '../app';
23
import AppError from '../utils/appError';
3-
import {
4-
Processes,
5-
WorkerMessageType,
6-
WorkerMessageUnknown
7-
} from '../worker/master';
4+
import { WorkerMessageType, WorkerMessageUnknown } from '../worker/protocol';
85

96
export const callFunction = (
107
req: Request,
118
res: Response,
129
next: NextFunction
1310
): Response | void => {
14-
if (!(req.params && req.params.name))
15-
next(
11+
if (!req.params?.suffix) {
12+
return next(
13+
new AppError('A the deployment name (suffix) is required.', 404)
14+
);
15+
}
16+
17+
if (!req.params?.func) {
18+
return next(
1619
new AppError(
1720
'A function name is required in the path; i.e: /call/sum.',
1821
404
1922
)
2023
);
24+
}
2125

22-
const { appName: app, name } = req.params;
26+
const { suffix, func } = req.params;
2327
const args = Object.values(req.body);
28+
const application = Applications[suffix];
2429

25-
if (!(app in Processes)) {
30+
// Check if the application exists and it is running
31+
if (!application?.proc) {
2632
return res
2733
.status(404)
2834
.send(
29-
`Oops! It looks like the application (${app}) hasn't been deployed yet. Please deploy it before you can call its functions.`
35+
`Oops! It looks like the application '${suffix}' has not been deployed yet. Please deploy it before you can call its functions.`
3036
);
3137
}
3238

33-
let responseSent = false; // Flag to track if response has been sent
34-
let errorCame = false;
35-
36-
Processes[app].send({
37-
type: WorkerMessageType.Invoke,
38-
data: {
39-
name,
40-
args
41-
}
42-
});
39+
new Promise((resolve, reject) => {
40+
application.proc?.send({
41+
type: WorkerMessageType.Invoke,
42+
data: {
43+
name: func,
44+
args
45+
}
46+
});
4347

44-
Processes[app].on('message', (message: WorkerMessageUnknown) => {
45-
if (!responseSent) {
46-
// Check if response has already been sent
48+
application.proc?.on('message', (message: WorkerMessageUnknown) => {
4749
if (message.type === WorkerMessageType.InvokeResult) {
48-
responseSent = true; // Set flag to true to indicate response has been sent
49-
return res.send(JSON.stringify(message.data));
50-
} else {
51-
errorCame = true;
50+
resolve(JSON.stringify(message.data));
5251
}
53-
}
54-
});
52+
});
5553

56-
// Default response in case the 'message' event is not triggered
57-
if (!responseSent && errorCame) {
58-
responseSent = true; // Set flag to true to indicate response has been sent
59-
errorCame = false;
60-
return res.send('Function calling error');
61-
}
54+
application.proc?.on('exit', code => {
55+
// The application may have been ended unexpectedly,
56+
// probably segmentation fault (exit code 139 in Linux)
57+
reject(
58+
JSON.stringify({
59+
error: `Deployment '${suffix}' process exited with code: ${
60+
code || 'unknown'
61+
}`
62+
})
63+
);
64+
});
65+
})
66+
.then(data => {
67+
res.send(data);
68+
})
69+
.catch(error => {
70+
res.status(500).send(error);
71+
});
6272
};

src/controller/delete.ts

+14-46
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,10 @@ import { NextFunction, Request, Response } from 'express';
33
import { rm } from 'fs/promises';
44
import { join } from 'path';
55

6-
import { allApplications } from '../constants';
6+
import { Applications } from '../app';
77
import { appsDirectory } from '../utils/config';
8-
import { ensureFolderExists } from '../utils/filesystem';
9-
import { Processes } from '../worker/master';
108
import { catchAsync } from './catch';
119

12-
const deleteStatusMessage = (
13-
app: string
14-
): {
15-
success: string;
16-
error: string;
17-
folderShouldntExist: string;
18-
appShouldntExist: string;
19-
} => ({
20-
success: 'Deploy Delete Succeed',
21-
error: `Oops! It looks like the application (${app}) hasn't been deployed yet. Please deploy it before you delete it.`,
22-
folderShouldntExist: `The folder shouldnt exist after deleting it.`,
23-
appShouldntExist: `The application shouldnt exist after deleting it`
24-
});
25-
2610
// TODO: Isn't this available inside protocol package? We MUST reuse it
2711
type DeleteBody = {
2812
suffix: string; // name of deployment
@@ -37,45 +21,29 @@ export const deployDelete = catchAsync(
3721
_next: NextFunction
3822
): Promise<Response> => {
3923
// Extract the suffix (application name) of the application from the request body
40-
const { suffix: app } = req.body;
41-
42-
// Initialize isError flag
43-
let isError = false;
44-
45-
// Check if the application exists in Processes and allApplications objects
46-
if (!(app in Processes && app in allApplications)) {
47-
isError = true;
48-
return res.send(deleteStatusMessage(app)['error']);
24+
const { suffix } = req.body;
25+
const application = Applications[suffix];
26+
27+
// Check if the application exists and it is running
28+
if (!application?.proc) {
29+
return res.send(
30+
`Oops! It looks like the application '${suffix}' hasn't been deployed yet. Please deploy it before you delete it.`
31+
);
4932
}
5033

5134
// Retrieve the child process associated with the application and kill it
52-
Processes[app].kill();
35+
application.kill();
5336

54-
// Remove the application from Processes and allApplications objects
55-
delete Processes[app];
56-
delete allApplications[app];
57-
58-
if (app in Processes && app in allApplications) {
59-
isError = true;
60-
return res.send(deleteStatusMessage(app)['appShouldntExist']);
61-
}
37+
// Remove the application Applications object
38+
delete Applications[suffix];
6239

6340
// Determine the location of the application
64-
const appLocation = join(appsDirectory, app);
41+
const appLocation = join(appsDirectory, suffix);
6542

6643
// Delete the directory of the application
6744
await rm(appLocation, { recursive: true, force: true });
6845

69-
if (!(await ensureFolderExists(appLocation))) {
70-
isError = true;
71-
return res.send(deleteStatusMessage(app)['folderShouldntExist']);
72-
}
73-
7446
// Send response based on whether there was an error during deletion
75-
return res.send(
76-
isError
77-
? deleteStatusMessage(app)['error']
78-
: deleteStatusMessage(app)['success']
79-
);
47+
return res.send('Deploy delete succeed!');
8048
}
8149
);

0 commit comments

Comments
 (0)