Skip to content
This repository was archived by the owner on Jun 9, 2020. It is now read-only.

Commit fce005a

Browse files
authored
feat(smartive): add promise to start and stop function (#188)
BREAKING CHANGE: Closes #171. Closes #172. This PR introduces async start and stop functions. During this change, the optional callbacks are removed since the call can now be awaited. If no port is given to the start function, a random one is created and used. Migration: remove all callbacks and hostnames from the start function and remove all callbacks from the stop function. They can now be awaited as normal.
1 parent b5b467f commit fce005a

3 files changed

Lines changed: 91 additions & 11 deletions

File tree

src/Giuseppe.ts

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { GiuseppeRoute } from './routes/GiuseppeRoute';
2222
import { ReturnType } from './routes/ReturnType';
2323
import { HttpMethod } from './routes/RouteDefinition';
2424
import { ControllerMetadata } from './utilities/ControllerMetadata';
25+
import { getRandomPort } from './utilities/RandomPort';
2526

2627
/**
2728
* Score sort function for route register information. Calculates the sorting score based on segments, url params
@@ -76,6 +77,18 @@ export class Giuseppe {
7677
return this._server;
7778
}
7879

80+
/**
81+
* Gets the used port of giuseppe (and the given express app). Returns undefined if the app
82+
* is not started yet.
83+
*
84+
* @readonly
85+
* @type {(number | undefined)}
86+
* @memberof Giuseppe
87+
*/
88+
public get port(): number | undefined {
89+
return this._port;
90+
}
91+
7992
/**
8093
* The express application behind this instance of giuseppe. Someone might want to change the used express instance
8194
* before calling [start()]{@link Giuseppe#start()}. Also, on this propert you can add other things like
@@ -105,6 +118,8 @@ export class Giuseppe {
105118
protected _pluginRouteModificators: RouteModificatorConstructor[] | null = null;
106119
protected _pluginParameters: ParameterDefinitionConstructor[] | null = null;
107120

121+
private _port: number | undefined;
122+
108123
/**
109124
* List of registered {@link ReturnType}.
110125
*
@@ -223,29 +238,36 @@ export class Giuseppe {
223238
* them on the given [router]{@link Giuseppe#router}. After the router is configured, fires up the express
224239
* application with the given parameter.
225240
*
226-
* @param {number} [port=8080] The port of the web application (express.listen argument).
241+
* @param {number} [port] The port of the web application (express.listen argument). If no port is provided
242+
* a random one is used.
227243
* @param {string} [baseUrl=''] Base url that is preceeding all urls in the system.
228-
* @param {string} [hostname] Hostname that is passed to express.
229-
* @param {Function} [callback] Callback that is used in express when the system is listening and ready.
230244
* @memberof Giuseppe
231245
*/
232-
public start(port: number = 8080, baseUrl: string = '', hostname?: string, callback?: Function): void {
246+
public async start(port?: number, baseUrl: string = ''): Promise<void> {
247+
const expressPort = port || await getRandomPort();
233248
const router = this.configureRouter(baseUrl);
234249
this.expressApp.use(router);
235-
this._server = this.expressApp.listen.apply(this.expressApp, [port, hostname, callback].filter(Boolean));
250+
this._server = await this.startup(expressPort);
251+
this._port = expressPort;
236252
}
237253

238254
/**
239255
* Closes the server of the application.
240256
*
241-
* @param {Function} [callback] Callback that is passed to the server.
242257
* @memberof Giuseppe
243258
*/
244-
public stop(callback?: Function): void {
245-
if (this._server) {
246-
this._server.close(callback);
247-
delete this._server;
248-
}
259+
public stop(): Promise<void> {
260+
return new Promise(resolve => {
261+
if (this._server) {
262+
this._server.close(() => {
263+
delete this._server;
264+
delete this._port;
265+
resolve();
266+
});
267+
return;
268+
}
269+
resolve();
270+
});
249271
}
250272

251273
/**
@@ -452,4 +474,12 @@ export class Giuseppe {
452474

453475
return true;
454476
}
477+
478+
private startup(port: number): Promise<Server> {
479+
return new Promise(resolve => {
480+
const server = this.expressApp.listen(port, () => {
481+
resolve(server);
482+
});
483+
});
484+
}
455485
}

src/utilities/RandomPort.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { createServer } from 'http';
2+
import { AddressInfo } from 'net';
3+
4+
export function getRandomPort(): Promise<number> {
5+
const server = createServer();
6+
return new Promise((resolve, reject) => {
7+
server.listen(0);
8+
server.on('listening', () => {
9+
try {
10+
const port = (server.address() as AddressInfo).port;
11+
server.close(() => {
12+
resolve(port);
13+
});
14+
} catch (e) {
15+
reject(e);
16+
}
17+
});
18+
server.on('error', reject);
19+
});
20+
}

test/utilities/RandomPort.spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { getRandomPort } from '../../src/utilities/RandomPort';
2+
3+
describe('RandomPort', () => {
4+
5+
describe('getRandomPort()', () => {
6+
7+
it('should return a random port', async () => {
8+
const port = await getRandomPort();
9+
10+
expect(port).toBeGreaterThan(0);
11+
expect(port).toBeLessThan(65536);
12+
});
13+
14+
it('should return multiple random ports', async () => {
15+
const ports = await Promise.all([
16+
getRandomPort(),
17+
getRandomPort(),
18+
getRandomPort(),
19+
getRandomPort(),
20+
]);
21+
22+
for (const port of ports) {
23+
expect(port).toBeGreaterThan(0);
24+
expect(port).toBeLessThan(65536);
25+
}
26+
});
27+
28+
});
29+
30+
});

0 commit comments

Comments
 (0)