mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Add Finalizable interface
This commit is contained in:
parent
93374f011a
commit
29ddf57341
@ -1,13 +1,18 @@
|
||||
import type { Server } from 'http';
|
||||
import { promisify } from 'util';
|
||||
import type { HttpServerFactory } from '../server/HttpServerFactory';
|
||||
import type { Finalizable } from './final/Finalizable';
|
||||
import { Initializer } from './Initializer';
|
||||
|
||||
/**
|
||||
* Creates and starts an HTTP server.
|
||||
*/
|
||||
export class ServerInitializer extends Initializer {
|
||||
export class ServerInitializer extends Initializer implements Finalizable {
|
||||
private readonly serverFactory: HttpServerFactory;
|
||||
private readonly port: number;
|
||||
|
||||
private server?: Server;
|
||||
|
||||
public constructor(serverFactory: HttpServerFactory, port: number) {
|
||||
super();
|
||||
this.serverFactory = serverFactory;
|
||||
@ -15,6 +20,12 @@ export class ServerInitializer extends Initializer {
|
||||
}
|
||||
|
||||
public async handle(): Promise<void> {
|
||||
this.serverFactory.startServer(this.port);
|
||||
this.server = this.serverFactory.startServer(this.port);
|
||||
}
|
||||
|
||||
public async finalize(): Promise<void> {
|
||||
if (this.server) {
|
||||
return promisify(this.server.close.bind(this.server))();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
6
src/init/final/Finalizable.ts
Normal file
6
src/init/final/Finalizable.ts
Normal file
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Allows for cleaning up an object and stopping relevant loops when the application needs to be stopped.
|
||||
*/
|
||||
export interface Finalizable {
|
||||
finalize: () => Promise<void>;
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
import type { Finalizable } from '../../init/final/Finalizable';
|
||||
import { getLoggerFor } from '../../logging/LogUtil';
|
||||
import { InternalServerError } from '../../util/errors/InternalServerError';
|
||||
import type { ExpiringStorage } from './ExpiringStorage';
|
||||
@ -11,9 +12,8 @@ export type Expires<T> = { expires?: string; payload: T };
|
||||
* Will delete expired entries when trying to get their value.
|
||||
* Has a timer that will delete all expired data every hour (default value).
|
||||
*/
|
||||
export class WrappedExpiringStorage<TKey, TValue> implements ExpiringStorage<TKey, TValue> {
|
||||
export class WrappedExpiringStorage<TKey, TValue> implements ExpiringStorage<TKey, TValue>, Finalizable {
|
||||
protected readonly logger = getLoggerFor(this);
|
||||
|
||||
private readonly source: KeyValueStorage<TKey, Expires<TValue>>;
|
||||
private readonly timer: NodeJS.Timeout;
|
||||
|
||||
@ -118,7 +118,7 @@ export class WrappedExpiringStorage<TKey, TValue> implements ExpiringStorage<TKe
|
||||
/**
|
||||
* Stops the continuous cleanup timer.
|
||||
*/
|
||||
public finalize(): void {
|
||||
public async finalize(): Promise<void> {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import type { RedisClient } from 'redis';
|
||||
import { createClient } from 'redis';
|
||||
import type { Lock } from 'redlock';
|
||||
import Redlock from 'redlock';
|
||||
import type { Finalizable } from '../../init/final/Finalizable';
|
||||
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
|
||||
import { getLoggerFor } from '../../logging/LogUtil';
|
||||
import { InternalServerError } from '../errors/InternalServerError';
|
||||
@ -37,7 +38,7 @@ const defaultRedlockConfig = {
|
||||
* in that sense it is kind of multithreaded.
|
||||
* - Redlock does not provide the ability to see which locks have expired
|
||||
*/
|
||||
export class RedisResourceLocker implements ResourceLocker {
|
||||
export class RedisResourceLocker implements ResourceLocker, Finalizable {
|
||||
protected readonly logger = getLoggerFor(this);
|
||||
|
||||
private readonly redlock: Redlock;
|
||||
@ -100,11 +101,14 @@ export class RedisResourceLocker implements ResourceLocker {
|
||||
public async finalize(): Promise<void> {
|
||||
// This for loop is an extra failsafe,
|
||||
// this extra code won't slow down anything, this function will only be called to shut down in peace
|
||||
try {
|
||||
for (const [ , { lock }] of this.lockMap.entries()) {
|
||||
await this.release({ path: lock.resource });
|
||||
}
|
||||
} finally {
|
||||
await this.redlock.quit();
|
||||
}
|
||||
}
|
||||
|
||||
public async acquire(identifier: ResourceIdentifier): Promise<void> {
|
||||
const resource = identifier.path;
|
||||
|
@ -1,13 +1,19 @@
|
||||
import type { Server } from 'http';
|
||||
import { ServerInitializer } from '../../../src/init/ServerInitializer';
|
||||
import type { HttpServerFactory } from '../../../src/server/HttpServerFactory';
|
||||
|
||||
describe('ServerInitializer', (): void => {
|
||||
const serverFactory: jest.Mocked<HttpServerFactory> = {
|
||||
startServer: jest.fn(),
|
||||
};
|
||||
let server: Server;
|
||||
let serverFactory: jest.Mocked<HttpServerFactory>;
|
||||
|
||||
let initializer: ServerInitializer;
|
||||
beforeAll(async(): Promise<void> => {
|
||||
beforeEach(async(): Promise<void> => {
|
||||
server = {
|
||||
close: jest.fn((fn: () => void): void => fn()),
|
||||
} as any;
|
||||
serverFactory = {
|
||||
startServer: jest.fn().mockReturnValue(server),
|
||||
};
|
||||
initializer = new ServerInitializer(serverFactory, 3000);
|
||||
});
|
||||
|
||||
@ -15,4 +21,15 @@ describe('ServerInitializer', (): void => {
|
||||
await initializer.handle();
|
||||
expect(serverFactory.startServer).toHaveBeenCalledWith(3000);
|
||||
});
|
||||
|
||||
it('can stop the server.', async(): Promise<void> => {
|
||||
await initializer.handle();
|
||||
await expect(initializer.finalize()).resolves.toBeUndefined();
|
||||
expect(server.close).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('only tries to stop the server if it was initialized.', async(): Promise<void> => {
|
||||
await expect(initializer.finalize()).resolves.toBeUndefined();
|
||||
expect(server.close).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
@ -154,7 +154,7 @@ describe('A WrappedExpiringStorage', (): void => {
|
||||
yield* data;
|
||||
});
|
||||
|
||||
expect(storage.finalize()).toBeUndefined();
|
||||
await expect(storage.finalize()).resolves.toBeUndefined();
|
||||
|
||||
// Make sure clearInterval was called with the interval timer
|
||||
expect(mockClear.mock.calls).toHaveLength(1);
|
||||
|
Loading…
x
Reference in New Issue
Block a user