Skip to content

Commit

Permalink
feat(core-kernel): initial draft of actions
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Faust committed Aug 26, 2019
1 parent f407903 commit 1a823ee
Show file tree
Hide file tree
Showing 14 changed files with 376 additions and 10 deletions.
98 changes: 98 additions & 0 deletions __tests__/unit/core-kernel/services/actions/actions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { Actions } from "../../../../../packages/core-kernel/src/services/actions/actions";
import { InvalidArgumentException } from "../../../../../packages/core-kernel/src/exceptions/logic";
import { Action } from "../../../../../packages/core-kernel/src/services/actions/action";

let actions: Actions;
beforeEach(() => {
actions = new Actions();
});

test("binds an action with a <before> hook and executes them", async () => {
let fnValue = 0;

actions.bind("count", () => fnValue++).before(() => fnValue++);

await actions.call("count");

expect(fnValue).toBe(2);
});

test("binds an action with a <error> hook and executes them", async () => {
let fnValue = 0;

actions
.bind("count", () => {
fnValue++;

throw new Error("Hello World");
})
.error(() => fnValue++);

await actions.call("count");

expect(fnValue).toBe(2);
});

test("binds an action with a <after> hook and executes them", async () => {
let fnValue = 0;

actions.bind("count", () => fnValue++).after(() => fnValue++);

await actions.call("count");

expect(fnValue).toBe(2);
});

test("binds an action with <before/error/after> hooks and executes them", async () => {
let fnValue = 0;

actions
.bind("count", () => {
fnValue++;

throw new Error("Hello World");
})
.before(() => fnValue++)
.error(() => fnValue++)
.after(() => fnValue++);

await actions.call("count");

expect(fnValue).toBe(4);
});

test("throws an error if an action is not registered", async () => {
expect(actions.call("count")).rejects.toThrowError(
new InvalidArgumentException("The given action [count] is not available."),
);
});

test("throws an error if an action is already registered", async () => {
actions.bind("duplicate", Function);

expect(() => {
actions.bind("duplicate", Function);
}).toThrowError(new InvalidArgumentException("The given action [duplicate] is already registered."));
});

test("throws an error if an action is reserved", async () => {
expect(() => {
actions.bind("internal.action", Function);
}).toThrowError(new InvalidArgumentException("The given action [internal.action] is reserved."));
});

describe("get", () => {
test("returns an action for the given name", async () => {
const fn = () => {};

actions.bind("count", fn);

expect(actions.get("count")).toEqual(new Action(fn));
});

test("throws an error if an action is not registered", async () => {
expect(() => actions.get("count")).toThrowError(
new InvalidArgumentException("The given action [count] is not available."),
);
});
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
"collectCoverage": false,
"coverageDirectory": "<rootDir>/.coverage",
"collectCoverageFrom": [
"packages/**/src/**/*.ts",
"packages/**/src/**/{!(index|service-provider),}.ts",
"!**/node_modules/**"
],
"coverageReporters": [
Expand Down
3 changes: 0 additions & 3 deletions packages/core-kernel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,5 @@
},
"engines": {
"node": ">=10.x"
},
"jest": {
"preset": "../../jest-preset.json"
}
}
19 changes: 18 additions & 1 deletion packages/core-kernel/src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { DirectoryCannotBeFound } from "./exceptions/filesystem";
import { EventDispatcher } from "./services/events";
import { AbstractServiceProvider, ServiceProviderRepository } from "./support";
import { EventListener } from "./types/events";
import { ShutdownSignal } from "./enums/process";

/**
* @export
Expand All @@ -34,6 +35,8 @@ export class Application extends Container implements Contracts.Kernel.IApplicat
public constructor() {
super();

this.listenToShutdownSignals();

this.bind<Contracts.Kernel.IApplication>("app", this);
}

Expand Down Expand Up @@ -451,7 +454,7 @@ export class Application extends Container implements Contracts.Kernel.IApplicat
.resolve<ServiceProviderRepository>("serviceProviderRepository")
.allLoadedProviders();

for (const provider of providers) {
for (const provider of providers.reverse()) {
await provider.dispose();
}
}
Expand Down Expand Up @@ -485,4 +488,18 @@ export class Application extends Container implements Contracts.Kernel.IApplicat

this.bind(`path.${type}`, path);
}

/**
* @private
* @memberof Application
*/
private listenToShutdownSignals(): void {
for (const signal in ShutdownSignal) {
process.on(signal as any, async code => {
await this.terminate(signal);

process.exit(code || 1);
});
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Cache, Filesystem, Log, Queue, Validation } from "../../services";
import { Actions, Cache, Filesystem, Log, Queue, Validation } from "../../services";
import { AbstractBootstrapper } from "../bootstrapper";

/**
Expand All @@ -13,6 +13,8 @@ export class RegisterBaseServiceProviders extends AbstractBootstrapper {
* @memberof RegisterBaseServiceProviders
*/
public async bootstrap(): Promise<void> {
await this.app.build(Actions.ServiceProvider).register();

await this.app.build(Log.ServiceProvider).register();

await this.app.build(Filesystem.ServiceProvider).register();
Expand Down
8 changes: 6 additions & 2 deletions packages/core-kernel/src/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,12 @@ export class Container {
* @memberof Container
*/
private usesReservedBindingName(name: string): boolean {
if (name.startsWith("scopes.")) {
return true;
const prefixes: string[] = ["scopes."];

for (const prefix of prefixes) {
if (name.startsWith(prefix)) {
return true;
}
}

return false;
Expand Down
3 changes: 2 additions & 1 deletion packages/core-kernel/src/enums/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as Events from "./events";
import * as Process from "./process";

export { Events };
export { Events, Process };
17 changes: 17 additions & 0 deletions packages/core-kernel/src/enums/process.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* @export
* @enum {number}
*/
export enum ShutdownSignal {
SIGHUP = "SIGHUP",
SIGINT = "SIGINT",
SIGQUIT = "SIGQUIT",
SIGILL = "SIGILL",
SIGTRAP = "SIGTRAP",
SIGABRT = "SIGABRT",
SIGBUS = "SIGBUS",
SIGFPE = "SIGFPE",
SIGSEGV = "SIGSEGV",
SIGUSR2 = "SIGUSR2",
SIGTERM = "SIGTERM",
}
77 changes: 77 additions & 0 deletions packages/core-kernel/src/services/actions/action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { FunctionReturning } from "awilix";

export class Action<T> {
/**
* @private
* @type {Set<FunctionReturning<void>>}
* @memberof Action
*/
private readonly beforeHooks: Set<FunctionReturning<void>> = new Set<FunctionReturning<void>>();

/**
* @private
* @type {Set<FunctionReturning<void>>}
* @memberof Action
*/
private readonly errorHooks: Set<FunctionReturning<void>> = new Set<FunctionReturning<void>>();

/**
* @private
* @type {Set<FunctionReturning<void>>}
* @memberof Action
*/
private readonly afterHooks: Set<FunctionReturning<void>> = new Set<FunctionReturning<void>>();

/**
* @param {FunctionReturning<void>} fn
* @memberof Action
*/
public constructor(private readonly fn: FunctionReturning<T>) {}

/**
* @returns {FunctionReturning<void>}
* @memberof Action
*/
public execute(...args: any[]): T {
return this.fn(args);
}

/**
* @param {FunctionReturning<void>} fn
* @memberof Action
*/
public before(fn: FunctionReturning<void>): this {
this.beforeHooks.add(fn);

return this;
}

/**
* @param {FunctionReturning<void>} fn
* @memberof Action
*/
public error(fn: FunctionReturning<void>): this {
this.errorHooks.add(fn);

return this;
}

/**
* @param {FunctionReturning<void>} fn
* @memberof Action
*/
public after(fn: FunctionReturning<void>): this {
this.afterHooks.add(fn);

return this;
}

/**
* @param {string} type
* @returns {Set<FunctionReturning<void>>}
* @memberof Action
*/
public hooks(type: string): Set<FunctionReturning<void>> {
return this[`${type}Hooks`];
}
}
Loading

0 comments on commit 1a823ee

Please sign in to comment.